Compare commits

...

30 Commits

Author SHA1 Message Date
Leilei Zhang
9081bc7438 for test 2025-02-24 20:10:39 +08:00
Leilei Zhang
7307242396 test 2025-02-24 20:08:48 +08:00
Leilei Zhang
630ed29700 fix 2025-02-24 19:41:46 +08:00
Leilei Zhang
41b0b850e1 check 2025-02-24 19:14:29 +08:00
Leilei Zhang
96dc200324 remove dep 2025-02-24 18:44:42 +08:00
Leilei Zhang
9597f87dc4 testing2 2025-02-24 18:43:40 +08:00
Leilei Zhang
7572586b4c for testing 2025-02-24 18:39:29 +08:00
PesBandi
3970e89ee7 [PowerRename]Add $, ^ and quantifiers to RegEx cheatsheet (#37062)
* [PowerRename]Add `$`, `^` and `.*` to RegEx cheatsheet

* Add * and +, remove combinations

* correct spelling

* Add ? and \s

* fix spelling once again
2025-02-24 09:46:38 +01:00
Henrik Lau Eriksson
74214f611e [PTRun][Docs] Update new plugin checklist (#36789)
[Docs] Update new plugin checklist
2025-02-21 11:19:56 -08:00
Nathan Gill
908a690316 PowerToys Run Calculator: Add trigonometric angle unit conversion functions (#37475)
* Added trig unit conversion macros to PowerToys Run Calculator plugin.

* Added testing for unit conversions.

* Removed debug messages.
2025-02-21 14:19:12 +01:00
Michael Clayton
6515374ce9 Ready for Review - [Mouse Without Borders] - refactoring "Common" classes (Part 3) - #35155 (#36950)
* [MWB] - refactoring MachineInf from Common.MachineStuff.cs into MachineInf.cs - #35155

* [MWB] - fixing references to MachineInf - #35155

* [MWB] - cleaning up MachineInf.cs - #35155

* [MWB] - moving MyRectangle from Common.MachineStuff.cs into MyRectangle.cs - #35155

* [MWB] - cleaning up MyRectangle.cs - #35155

* [MWB] - moving Common.MachineStuff.cs to MachineStuff.cs - #35155

* [MWB] - fixing references to MachineStuff - #35155

* [MWB] - cleaning up MachineStuff.cs - #35155

* [MWB] - cleaning up MachineStuff.cs - #35155

* [MWB] - moving Common.DragDrop.cs to DragDrop.cs - #35155

* [MWB] - fixing references to DragDrop - #35155

* [MWB] - fixing unit test - #35155

* [MWB] - cleaning up DragDrop.cs - #35155

* [MWB] - cleaning up DragDrop.cs - #35155
2025-02-21 09:31:44 +01:00
PesBandi
273a45ff1f [PTRun][Calc]Add list separator handling for different cultures (#36735) 2025-02-21 09:26:52 +01:00
Kayla Cinnamon
17f3c12a11 Remove "new" label from ZoomIt (#37417)
remove new from zoomit
2025-02-20 12:29:14 +01:00
Ani
fa4471a9e6 [MWB] Fix file transfer not working in service mode (#37542)
* [MWB] Fix file transfer not working in service mode

* Spellcheck issues
2025-02-20 11:58:29 +01:00
Dave Rayment
727de3e1fc [Run] Fix dark mode detection code, plus refactor (#37324)
* Fix risky int cast in dark mode detection.

* Refactored Helper and Manager classes. New unit tests and changes to support Registry access mocking.

* Spelling update.

* Improve documentation for the registry-related classes.

* Fix issue with UpdateTheme raised in review. Enhance documentation. Rewrite tests to use parameterised unit tests, and expand to cover more cases.
2025-02-20 11:47:30 +01:00
Ani
c6f9701818 [Fancy Zones] Fixed accessibility text of monitors on Layout Editor (#36997)
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
2025-02-20 10:40:26 +01:00
dreamstart
9453e38881 UITestAutomation Framework (#37461)
* Add UITestAutomation framework

* add code comments

* Optimized code format

* Optimized code format

* Update commons and add keyboard manager ui test project

* Optimized code format

* test scope and fix fancyzone exe path

* Add readme

* Optimize helper functions and UI test method

* Fix spelling errors and restore module UI tests

* Restore Indent

* Update NOTICE.md

* Update comments to Session and Elements

* Update comments for Button and Window

* delete unnecessary code

* change FindElementByName to FindElmenet

* Update comments for ModuleConfigData

* Update readme and comments

* Remove extra comments

* change public property

* Optimize code readability

* add default Attach Function

* change attach function name

* Update comments to XML format

* Hide by internal functions

* Update readme

* Refine the framework

* Fix process start position and update readme

* Remove Enum PowerToysModuleWindow

* Update attach comments

* Update ModuleConfigData comments

---------

Co-authored-by: Zhaopeng Wang (from Dev Box) <zhaopengwang@microsoft.com>
Co-authored-by: Xiaofeng Wang (from Dev Box) <xiaofengwang@microsoft.com>
Co-authored-by: urnotdfs <709586527@qq.com>
2025-02-20 13:25:20 +08:00
chenmy77
a1a02889d5 [Fuzz] Add fuzz testing for Hosts (#37516)
* add hostsfile fuzztests templates code

* modify  typos of hostsfile

* add hosts file

* add hosts fuzz to pipeline

* modify varify depjson rule

* fuzz validIPv4

* update  .net7 to .net 8

* add valid6/validhosts tests on hosts

* catch all exception

* update onefuzzconfig.json to add 3 test cases

* add fuzz writeasync tests and fill exception

* add writeasync onefuzz config

* add dll of writeasync in job dependencies

* for testing az

* change file

* use mock filesystem in hosts tests projct

* fix spell erro

* fix spell erro and change notations

* update test

* fix space erro in code

* install python

* update

* test

* use powershell

* remove unused dll in oneconfig.json


* change download artifacts

* update

* test

* add

* test

* merge

* az

* change

* update

* test cli

* add debug

* test large

* fix

* use templete

* remove pdb file filter in job test project

* fix x64 python install

* for testing

* add

* fix

* use 3.11.1

* change for test

* revert some testing file

* update the file name for spelling check

* use azure cli zip

* use aka.ms

* rename the zip file

* remove test artifactname

* add exception and job dependencies

* Remove the limitation of fuzzing only on hosts

* add fuzz readme

* remove unused changes and space

* fix x86 in sln and remove newtonsoft.json.dll in oneconfig.json

* readd wrapper.dll in oneconfig.json

* drop randomsplit when fuzz writeasync and remove unuseful package

---------
2025-02-20 10:39:42 +08:00
Ani
0592e74d3d [Image Resizer] Added AutomationProperties.HelpText to dimensions combo-box (#37122)
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
2025-02-19 17:26:49 +01:00
Kai Tao
9d148d0a3a Update runner documentation to reflect newest code structure 2025-02-19 10:03:09 +08:00
leileizhang
0a51687b65 [CI] fix: Use Azure CLI for artifact download to prevent OutOfMemory issues (#37455)
* for testing az

* change file

* update test

* install python

* update

* test

* use powershell

* tes

* update enve

* update

* test

* add

* test

* merge

* az

* change

* update

* test cli

* add debug

* test large

* fix

* use templete

* fix x64 python install

* for testing

* add

* fix

* use 3.11.1

* change for test

* revert some testing file

* update the file name for spelling check

* use azure cli zip

* use aka.ms

* rename the zip file
2025-02-19 09:17:15 +08:00
Ani
771fcaba96 [Settings] Fixed missing accessibility name of secondary links panel (#37014)
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
2025-02-19 00:22:56 +01:00
Jaime Bernardo
82e386f63c [MouseHighlighter]Fix stray highlights stucking (#37309)
* [MouseHighlighter]Fix stray highlights stucking

* Fix spellcheck

---------

Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
2025-02-19 00:21:03 +01:00
Massimiliano Alberti
91b53cdc13 [QuickAccent]Added ` (backtick) and ~ (tilde) to VK_OEM_5 (#20333) (#37286) 2025-02-18 23:45:58 +01:00
PesBandi
5c2c74a6c9 [QuickAccent]Add more letters with caron to IPA (#37369)
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
2025-02-18 23:13:51 +01:00
Nathan Gill
cb5baad677 Use system default web browser when opening links through Monaco in RegistryPreview (#37466)
* Handled NewWindowRequested WebView2 event, to allow links opened through Registry Preview to open in the system default web browser, rather than a new WebView2 window.

* Modified RegistryPreview implementatiion to use the open URI dialog that is currently used in Peek.
2025-02-18 23:01:03 +01:00
moooyo
ec136d7bb7 [PowerRename] Fix negative enumerate start parameter parse bug. (#37375)
Fix reg bug to make PowerRename accept negative number as start parameter

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
2025-02-19 01:28:49 +08:00
Laszlo Nemeth
e33efb7f10 [Workspaces] Fix case: monitor not present at launch (#37005)
* [Workspaces] Fix case: monitor not present at launch

* Fix DPI multiplicator calculation when monitor not present
2025-02-18 17:03:34 +01:00
Hao Liu
68afc6623f [KeyboardManager WinUI3] Create WinUI3 project and wrapper for Keyboard Manager Editor (#37427)
* Set up KBM WinUI3 Editor UI project

* Test invoking the KBM library via wrapper for WinUI3 C# UI

* Set up Editor Library Wrapper and enable logging

* fix spelling

* update spacing and remove unused file

* fix formatting

* update sln

* update wrapper project config

* import common props

* update UI reference

* gate the new editor with the experimentation toggle in settings
2025-02-18 17:10:15 +08:00
Massimiliano Alberti
5008d77105 [Zoomit]Fix warning C4706 and related error C2220 (#37283) 2025-02-13 19:45:52 +00:00
114 changed files with 4804 additions and 2683 deletions

View File

@@ -263,6 +263,10 @@ onefuzz
# NameInCode
leilzh
mengyuanchen
# DllName
testhost
#Tools
OIP
OIP

View File

@@ -734,6 +734,7 @@ KEYBDINPUT
keyboardeventhandlers
keyboardmanagercommon
KEYBOARDMANAGEREDITOR
KEYBOARDMANAGEREDITORLIBRARYWRAPPER
keyboardmanagerstate
keyboardmanagerui
KEYEVENTF

View File

@@ -1,12 +1,5 @@
trigger: none
pr: none
schedules:
- cron: "0 0 * * *" # every day at midnight
displayName: "Daily midnight Build"
branches:
include:
- main
always: false # only run if there's code changes!
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
@@ -15,7 +8,6 @@ parameters:
type: object
default:
- x64
- arm64
- name: enableMsBuildCaching
type: boolean
displayName: "Enable MSBuild Caching"
@@ -28,15 +20,6 @@ parameters:
type: boolean
displayName: "Build Using Visual Studio Preview"
default: false
- name: useLatestWinAppSDK
type: boolean
default: true
- name: winAppSDKVersionNumber
type: string
default: 1.6
- name: useExperimentalVersion
type: boolean
default: false
extends:
template: templates/pipeline-ci-build.yml
@@ -45,6 +28,3 @@ extends:
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
runTests: ${{ parameters.runTests }}
useVSPreview: ${{ parameters.useVSPreview }}
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}

View File

@@ -456,12 +456,6 @@ jobs:
Copy-Item src\gpo\assets\* "$(JobOutputDirectory)/gpo" -Recurse
displayName: Stage GPO files
- ${{ if eq(parameters.publishArtifacts, true) }}:
- publish: $(JobOutputDirectory)
artifact: $(JobOutputArtifactName)
displayName: Publish all outputs
condition: always()
# Running the tests may result in future jobs consuming artifacts out of this build
- ${{ if eq(parameters.runTests, true) }}:
- task: CopyFiles@2
@@ -471,17 +465,8 @@ jobs:
contents: '$(BuildPlatform)/$(BuildConfiguration)/**/*'
targetFolder: '$(JobOutputDirectory)\$(BuildPlatform)\$(BuildConfiguration)'
- task: CopyFiles@2
displayName: Stage entire build output
inputs:
sourceFolder: $(JobOutputDirectory)
contents: |-
**
!**\*.pdb
!**\*.lib
targetFolder: '$(JobOutputDirectory)\TestArtifacts'
- publish: $(JobOutputDirectory)\TestArtifacts
artifact: $(JobOutputArtifactName)-TestArtifacts
displayName: Publish all outputs for testing
- ${{ if eq(parameters.publishArtifacts, true) }}:
- publish: $(JobOutputDirectory)
artifact: $(JobOutputArtifactName)
displayName: Publish all outputs
condition: always()

View File

@@ -19,7 +19,7 @@ jobs:
BuildPlatform: ${{ parameters.platform }}
BuildConfiguration: ${{ parameters.configuration }}
SrcPath: $(Build.Repository.LocalPath)
TestArtifactsName: build-${{ parameters.platform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}-TestArtifacts
TestArtifactsName: build-${{ parameters.platform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}
pool:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
${{ if ne(parameters.platform, 'ARM64') }}:
@@ -61,13 +61,18 @@ jobs:
reg add "HKLM\Software\Policies\Microsoft\Edge\WebView2\ReleaseChannels" /v PowerToys.exe /t REG_SZ /d "3"
displayName: "Enable WebView2 Canary Channel"
- download: current
displayName: Download artifacts
artifact: $(TestArtifactsName)
patterns: |-
**
!**\*.pdb
!**\*.lib
- ${{ if ne(parameters.platform, 'arm64') }}:
- download: current
displayName: Download artifacts
artifact: $(TestArtifactsName)
patterns: |-
**
!**\*.pdb
!**\*.lib
- ${{ else }}:
- template: steps-download-artifacts-with-azure-cli.yml
parameters:
artifactName: $(TestArtifactsName)
- template: steps-ensure-dotnet-version.yml
parameters:

View File

@@ -43,43 +43,11 @@ stages:
- template: job-ci-precheck.yml
- ${{ each platform in parameters.buildPlatforms }}:
- stage: Build_${{ platform }}
displayName: Build ${{ platform }}
${{ if ne(variables['Build.Reason'], 'Manual') }}:
dependsOn: [Precheck]
condition: and(succeeded(), ne(dependencies.Precheck.outputs['Precheck.verifyBuildRequest.skipBuild'], 'Yes'))
${{ else }}:
dependsOn: []
- stage: Test_${{ platform }}
displayName: Test ${{ platform }}
jobs:
- template: job-build-project.yml
- template: job-test-project.yml
parameters:
pool:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
buildPlatforms:
- ${{ platform }}
buildConfigurations: [Release]
enablePackageCaching: true
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
runTests: ${{ parameters.runTests }}
useVSPreview: ${{ parameters.useVSPreview }}
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
${{ if eq(parameters.useLatestWinAppSDK, true) }}:
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
- ${{ if eq(parameters.runTests, true) }}:
- stage: Test_${{ platform }}
displayName: Test ${{ platform }}
dependsOn:
- Build_${{platform}}
jobs:
- template: job-test-project.yml
parameters:
platform: ${{ platform }}
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}
platform: ${{ platform }}
configuration: Release
useLatestWebView2: ${{ parameters.useLatestWebView2 }}

View File

@@ -0,0 +1,41 @@
parameters:
- name: artifactName
type: string
default: ""
# Why use az cli to download? → The ARM agent may run into OutOfMemory issues.
# Why use the Azure CLI ZIP version? → It comes with its own Python and works fine under emulation on ARM64.
# Why not use AzureCLI@2 task? → It requires azureSubscription, which is unnecessary for downloading artifacts.
steps:
- powershell: |
Write-Host "Downloading Azure CLI ZIP..."
$azCliUrl = "https://aka.ms/installazurecliwindowszipx64"
$azCliZip = "$(Build.ArtifactStagingDirectory)\azure-cli.zip"
Invoke-WebRequest -Uri $azCliUrl -OutFile $azCliZip
displayName: 'Install Azure CLI from ZIP'
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)\azure-cli.zip'
destinationFolder: '$(Build.ArtifactStagingDirectory)\AzureCLI'
- pwsh: |
$azureCliPath = "$(Build.ArtifactStagingDirectory)\AzureCLI\bin"
$env:Path = "$azureCliPath;" + $env:Path
Write-Host "Add azure-devops..."
az extension add -n azure-devops
Write-Host "Configuring Azure DevOps defaults..."
az devops configure --defaults organization='$(System.TeamFoundationCollectionUri)' project='$(System.TeamProject)' --use-git-aliases true
Write-Host "check permission"
az pipelines list --org "$(System.TeamFoundationCollectionUri)" --project "$(System.TeamProject)" --output table
Write-Host "Downloading artifacts..."
if ($env:AZURE_DEVOPS_EXT_PAT -eq $null -or $env:AZURE_DEVOPS_EXT_PAT -eq "") {
Write-Host "Error: AZURE_DEVOPS_EXT_PAT is not set."
exit 1
}
az pipelines runs artifact download --artifact-name ${{parameters.artifactName}} --path "$(Pipeline.Workspace)/${{parameters.artifactName}}" --run-id 116384714 --debug
displayName: 'Download artifacts with Azure CLI'
env:
AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)

View File

@@ -15,7 +15,7 @@ Param(
$referencedFileVersionsPerDll = @{}
$totalFailures = 0
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude UITests-FancyZones*,MouseJump.Common.UnitTests*,AdvancedPaste.FuzzTests* | ForEach-Object {
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude UITests-FancyZones*,MouseJump.Common.UnitTests*,*.FuzzTests* | ForEach-Object {
# Temporarily exclude FancyZones UI tests because of Appium.WebDriver dependencies
$depsJsonFullFileName = $_.FullName
$depsJsonFileName = $_.Name

View File

@@ -1322,6 +1322,7 @@ EXHIBIT A -Mozilla Public License.
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
- Microsoft.Data.Sqlite 9.0.2
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
- Microsoft.DotNet.ILCompiler (A)
- Microsoft.Extensions.DependencyInjection 9.0.2
- Microsoft.Extensions.Hosting 9.0.2
- Microsoft.Extensions.Hosting.WindowsServices 9.0.2

File diff suppressed because it is too large Load Diff

91
doc/devdocs/UITests.md Normal file
View File

@@ -0,0 +1,91 @@
# UI tests framework
A specialized UI test framework for PowerToys that makes it easy to write UI tests for PowerToys modules or settings. Let's start writing UI tests!
## Before running tests
- Install Windows Application Driver v1.2.1 from https://github.com/microsoft/WinAppDriver/releases/tag/v1.2.1 to the default directory (`C:\Program Files (x86)\Windows Application Driver`)
- Enable Developer Mode in Windows settings
## Running tests
- Exit PowerToys if it's running.
- Open `PowerToys.sln` in Visual Studio and build the solution.
- Run tests in the Test Explorer (`Test > Test Explorer` or `Ctrl+E, T`).
## How to add the first UI tests for your modules
- Create a new project and add the following references to the project file. Change the OutputPath to your own module's path.
```
<PropertyGroup>
<OutputType>Library</OutputType>
<!-- This is a UI test, so don't run as part of MSBuild -->
<RunVSTest>false</RunVSTest>
</PropertyGroup>
<PropertyGroup>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\KeyboardManagerUITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
<Folder Include="Properties\" />
</ItemGroup>
```
- Inherit your test class from UITestBase.
>Set Scope: The default scope starts from the PowerToys settings UI. If you want to start from your own module, set the constructor as shown below:
>Specify Scope:
```
[TestClass]
public class RunFancyZonesTest : UITestBase
{
public RunFancyZonesTest()
: base(PowerToysModule.FancyZone)
{
}
}
```
- Then you can start using session to perform the UI operations.
**Example**
```
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UITests_KeyboardManager
{
[TestClass]
public class RunKeyboardManagerUITests : UITestBase
{
[TestMethod]
public void OpenKeyboardManagerEditor()
{
// Open KeyboardManagerEditor
this.Session.Find<Button>(By.Name("Remap a key")).Click();
this.Session.Attach("Remap keys");
// Maximize window
var window = Session.Find<Window>(By.Name("Remap keys")).Maximize();
// Add Key Remapping
this.Session.Find<Button>(By.Name("Add key remapping")).Click();
window.Close();
// Back to Settings
this.Session.Attach(PowerToysModule.PowerToysSettings);
}
}
}
```
## Extra tools and information
**Accessibility Tools**:
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview)

View File

@@ -3,8 +3,9 @@
- [ ] The plugin is a project under `modules\launcher\Plugins`
- [ ] Microsoft plugin project name pattern: `Microsoft.PowerToys.Run.Plugin.{PluginName}`
- [ ] Community plugin project name pattern: `Community.PowerToys.Run.Plugin.{PluginName}`
- [ ] The plugin target framework should be `net8.0-windows`
- [ ] The plugin target framework should be `net9.0-windows10.0.22621.0`
- [ ] If the plugin uses any 3rd party dependencies the project file should import `DynamicPlugin.props`
- [ ] 3rd party dependencies must be compatible with .NET 9
- [ ] The plugin has to contain a `plugin.json` file of the following format in its root folder:
```json
@@ -35,7 +36,6 @@ public static string PluginID => "xxxxxxx"; // The part xxxxxxx stands for the p
- [ ] Plugin's output code and assets have to be included in the installer [`Product.wxs`](/installer/PowerToysSetup/Product.wxs)
- [ ] Test the plugin with a local build. Build the installer, install, check that the plugin works as expected
- [ ] All plugin's binaries have to be included in the signed build [`pipeline.user.windows.yml`](/.pipelines/pipeline.user.windows.yml)
- [ ] The plugin target framework has to be net8.0-windows. All dependencies should be compatible with .NET 8.
Some localization steps can only be done after the first pass by the localization team to provide the localized resources.
In the PR that adds a new plugin, reference a new issue to track the work for fully enabling localization for the new plugin.

View File

@@ -4,18 +4,10 @@ Contains the executable starting point, initialization code and the list of know
#### [`powertoy_module.h`](/src/runner/powertoy_module.h) and [`powertoy_module.cpp`](/src/runner/powertoy_module.cpp)
Contains code for initializing and managing the PowerToy modules. `PowertoyModule` is a RAII-style holder for the `PowertoyModuleIface` pointer, which we got by [invoking module DLL's `powertoy_create` function](https://github.com/microsoft/PowerToys/blob/1760af50c8803588cb575167baae0439af38a9c1/src/runner/powertoy_module.cpp#L13-L24).
#### [`powertoys_events.cpp`](/src/runner/powertoys_events.cpp)
Contains code that handles the various events listeners, and forwards those events to the PowerToys modules. You can learn more about the current event architecture in [shared hooks](/doc/devdocs/shared-hooks.md).
#### [`lowlevel_keyboard_event.cpp`](/src/runner/lowlevel_keyboard_event.cpp)
Contains code for registering the low level keyboard event hook that listens for keyboard events. Please note that `signal_event` is called from the main thread for this event.
#### [`win_hook_event.cpp`](/src/runner/win_hook_event.cpp)
Contains code for registering a Windows event hook through `SetWinEventHook`, that listens for various events raised when a window is interacted with. Please note, that `signal_event` is called from a separate `dispatch_thread_proc` worker thread, so you must provide thread-safety for your `signal_event` if you intend to receive it. This is a subject to change.
#### [`tray_icon.cpp`](/src/runner/tray_icon.cpp)
Contains code for managing the PowerToys tray icon and its menu commands. Note that `dispatch_run_on_main_ui_thread` is used to
transfer received json message from the [Settings window](/doc/devdocs/settings.md) to the main thread, since we're communicating with it from [a dedicated thread](https://github.com/microsoft/PowerToys/blob/7357e40d3f54de51176efe54fda6d57028837b8c/src/runner/settings_window.cpp#L267-L271).
#### [`settings_window.cpp`](/src/runner/settings_window.cpp)
Contains code for starting the PowerToys settings window and communicating with it. Settings window is a separate process, so we're using [Windows pipes](https://learn.microsoft.com/windows/win32/ipc/pipes) as a transport for json messages.
@@ -33,3 +25,24 @@ Contains code for telemetry.
#### [`svgs`](/src/runner/svgs/)
Contains the SVG assets used by the PowerToys modules.
#### [`bug_report.cpp`](/src/runner/bug_report.cpp)
Contains logic to start bug report tool.
#### [`centralized_hotkeys.cpp`](/src/runner/centralized_hotkeys.cpp)
Contains hot key logic registration and un-registration.
#### [`centralized_kb_hook.cpp`](/src/runner/centralized_kb_hook.cpp)
Contains logic to handle PowerToys' keyboard shortcut functionality.
#### [`restart_elevated.cpp`](/src/runner/restart_elevated.cpp)
Contains logic for restarting the current process with different elevation levels.
#### [`RestartManagement.cpp`](/src/runner/RestartManagement.cpp)
Contains code for restarting a process.
#### [`settings_telemetry.cpp`](/src/runner/settings_telemetry.cpp)
Contains logic that periodically triggers module-specific setting's telemetry delivery and manages timing and error handling for the process.
#### [`UpdateUtils.cpp`](/src/runner/UpdateUtils.cpp)
Contains code to handle the automatic update checking, notification, and installation process for PowerToys.

View File

@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.Events;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Represents a button in the UI test environment.
/// </summary>
public class Button : Element
{
}
}

View File

@@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using static OpenQA.Selenium.By;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// This class represents a By selector.
/// </summary>
public class By
{
private readonly OpenQA.Selenium.By by;
private By(OpenQA.Selenium.By by)
{
this.by = by;
}
/// <summary>
/// Creates a By object using the name attribute.
/// </summary>
/// <param name="name">The name attribute to search for.</param>
/// <returns>A By object.</returns>
public static By Name(string name) => new By(OpenQA.Selenium.By.Name(name));
/// <summary>
/// Creates a By object using the ID attribute.
/// </summary>
/// <param name="id">The ID attribute to search for.</param>
/// <returns>A By object.</returns>
public static By Id(string id) => new By(OpenQA.Selenium.By.Id(id));
/// <summary>
/// Creates a By object using the XPath expression.
/// </summary>
/// <param name="xpath">The XPath expression to search for.</param>
/// <returns>A By object.</returns>
public static By XPath(string xpath) => new By(OpenQA.Selenium.By.XPath(xpath));
/// <summary>
/// Creates a By object using the CSS selector.
/// </summary>
/// <param name="cssSelector">The CSS selector to search for.</param>
/// <returns>A By object.</returns>
public static By CssSelector(string cssSelector) => new By(OpenQA.Selenium.By.CssSelector(cssSelector));
/// <summary>
/// Creates a By object using the link text.
/// </summary>
/// <param name="linkText">The link text to search for.</param>
/// <returns>A By object.</returns>
public static By LinkText(string linkText) => new By(OpenQA.Selenium.By.LinkText(linkText));
/// <summary>
/// Creates a By object using the tag name.
/// </summary>
/// <param name="tagName">The tag name to search for.</param>
/// <returns>A By object.</returns>
public static By TagName(string tagName) => new By(OpenQA.Selenium.By.TagName(tagName));
/// <summary>
/// Converts the By object to an OpenQA.Selenium.By object.
/// </summary>
/// <returns>An OpenQA.Selenium.By object.</returns>
internal OpenQA.Selenium.By ToSeleniumBy() => by;
}
}

View File

@@ -0,0 +1,185 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.Events;
using static Microsoft.PowerToys.UITest.UITestBase;
[assembly: InternalsVisibleTo("Session")]
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Represents a basic UI element in the application.
/// </summary>
public class Element
{
private WindowsElement? windowsElement;
private WindowsDriver<WindowsElement>? driver;
internal void SetWindowsElement(WindowsElement windowsElement) => this.windowsElement = windowsElement;
internal void SetSession(WindowsDriver<WindowsElement> driver) => this.driver = driver;
/// <summary>
/// Gets the name of the UI element.
/// </summary>
public string Name
{
get { return GetAttribute("Name"); }
}
/// <summary>
/// Gets the text of the UI element.
/// </summary>
public string Text
{
get { return GetAttribute("Value"); }
}
/// <summary>
/// Gets the AutomationID of the UI element.
/// </summary>
public string AutomationId
{
get { return GetAttribute("AutomationId"); }
}
/// <summary>
/// Gets the class name of the UI element.
/// </summary>
public string ClassName
{
get { return GetAttribute("ClassName"); }
}
/// <summary>
/// Gets the help text of the UI element.
/// </summary>
public string HelpText
{
get { return GetAttribute("HelpText"); }
}
/// <summary>
/// Gets the control type of the UI element.
/// </summary>
public string ControlType
{
get { return GetAttribute("ControlType"); }
}
/// <summary>
/// Checks if the UI element is enabled.
/// </summary>
/// <returns>True if the element is enabled; otherwise, false.</returns>
public bool IsEnabled() => GetAttribute("IsEnabled") == "True";
/// <summary>
/// Checks if the UI element is selected.
/// </summary>
/// <returns>True if the element is selected; otherwise, false.</returns>
public bool IsSelected() => GetAttribute("IsSelected") == "True";
/// <summary>
/// Click the UI element.
/// </summary>
/// <param name="rightClick">If true, performs a right-click; otherwise, performs a left-click.</param>
public void Click(bool rightClick = false)
{
PerformAction(actions =>
{
if (rightClick)
{
actions.ContextClick();
}
else
{
actions.Click();
}
});
}
/// <summary>
/// Gets the attribute value of the UI element.
/// </summary>
/// <param name="attributeName">The name of the attribute to get.</param>
/// <returns>The value of the attribute.</returns>
public string GetAttribute(string attributeName)
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method GetAttribute with parameter: attributeName = {attributeName}");
var attributeValue = this.windowsElement.GetAttribute(attributeName);
Assert.IsNotNull(attributeValue, $"Attribute '{attributeName}' is null.");
return attributeValue;
}
/// <summary>
/// Finds an element by the selector.
/// </summary>
/// <typeparam name="T">The class type of the element to find.</typeparam>
/// <param name="by">The selector to use for finding the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>The found element.</returns>
public T Find<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
var foundElement = FindElementHelper.Find<T, AppiumWebElement>(
() =>
{
var element = this.windowsElement.FindElement(by.ToSeleniumBy());
Assert.IsNotNull(element, $"Element not found using selector: {by}");
return element;
},
this.driver,
timeoutMS);
return foundElement;
}
/// <summary>
/// Finds all elements by the selector.
/// </summary>
/// <typeparam name="T">The class type of the elements to find.</typeparam>
/// <param name="by">The selector to use for finding the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds.</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T>? FindAll<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.windowsElement, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
var foundElements = FindElementHelper.FindAll<T, AppiumWebElement>(
() =>
{
var elements = this.windowsElement.FindElements(by.ToSeleniumBy());
Assert.IsTrue(elements.Count > 0, $"Elements not found using selector: {by}");
return elements;
},
this.driver,
timeoutMS);
return foundElements;
}
/// <summary>
/// Simulates a manual operation on the element.
/// </summary>
private void PerformAction(Action<Actions> action)
{
var element = this.windowsElement;
Actions actions = new Actions(this.driver);
actions.MoveToElement(element);
action(actions);
actions.Build().Perform();
}
}
}

View File

@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
[assembly: InternalsVisibleTo("Element")]
[assembly: InternalsVisibleTo("Session")]
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Helper class for finding elements.
/// </summary>
internal static class FindElementHelper
{
public static T Find<T, TW>(Func<TW> findElementFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
where T : Element, new()
{
var item = findElementFunc() as WindowsElement;
return NewElement<T>(item, driver, timeoutMS);
}
public static ReadOnlyCollection<T>? FindAll<T, TW>(Func<ReadOnlyCollection<TW>> findElementsFunc, WindowsDriver<WindowsElement>? driver, int timeoutMS)
where T : Element, new()
{
var items = findElementsFunc();
var res = items.Select(item =>
{
var element = item as WindowsElement;
return NewElement<T>(element, driver, timeoutMS);
}).ToList();
return new ReadOnlyCollection<T>(res);
}
public static T NewElement<T>(WindowsElement? element, WindowsDriver<WindowsElement>? driver, int timeoutMS)
where T : Element, new()
{
Assert.IsNotNull(driver, $"New Element {typeof(T).Name} error: driver is null.");
Assert.IsNotNull(element, $"New Element {typeof(T).Name} error: element is null.");
T newElement = new T();
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromMilliseconds(timeoutMS);
newElement.SetSession(driver);
newElement.SetWindowsElement(element);
return newElement;
}
}
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.Events;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Represents a window in the UI test environment.
/// </summary>
public class Window : Element
{
/// <summary>
/// Maximizes the window.
/// </summary>
/// <param name="byClickButton">If true, clicks the Maximize button; otherwise, sets the window state.</param>
/// <returns>The current Window instance.</returns>
public Window Maximize(bool byClickButton = true)
{
if (byClickButton)
{
Find<Button>(By.Name("Maximize")).Click();
}
else
{
// TODO: Implement maximizing the window using an alternative method
}
return this;
}
/// <summary>
/// Restores the window.
/// </summary>
/// <param name="byClickButton">If true, clicks the Restore button; otherwise, sets the window state.</param>
/// <returns>The current Window instance.</returns>
public Window Restore(bool byClickButton = true)
{
if (byClickButton)
{
Find<Button>(By.Name("Restore")).Click();
}
else
{
// TODO: Implement restoring the window using an alternative method
}
return this;
}
/// <summary>
/// Minimizes the window.
/// </summary>
/// <param name="byClickButton">If true, clicks the Minimize button; otherwise, sets the window state.</param>
/// <returns>The current Window instance.</returns>
public Window Minimize(bool byClickButton = true)
{
if (byClickButton)
{
Find<Button>(By.Name("Minimize")).Click();
}
else
{
// TODO: Implement minimizing the window using an alternative method
}
return this;
}
/// <summary>
/// Closes the window.
/// </summary>
/// <param name="byClickButton">If true, clicks the Close button; otherwise, closes the window using an alternative method.</param>
public void Close(bool byClickButton = true)
{
if (byClickButton)
{
Find<Button>(By.Name("Close")).Click();
}
else
{
// TODO: Implement closing the window using an alternative method
}
}
}
}

View File

@@ -0,0 +1,72 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("UITestBase")]
[assembly: InternalsVisibleTo("Session")]
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// This file manages the configuration of modules for UI tests.
/// </summary>
/// <remarks>
/// How to add a new module:
/// 1. Define the new module in the PowerToysModule enum.
/// 2. Add the exe window name to the ModuleWindowName dictionary in the ModuleConfigData constructor.
/// 3. Add the exe path to the ModulePath dictionary in the ModuleConfigData constructor.
/// </remarks>
/// <summary>
/// Represents the modules in PowerToys.
/// </summary>
public enum PowerToysModule
{
PowerToysSettings,
FancyZone,
Hosts,
}
internal class ModuleConfigData
{
private Dictionary<PowerToysModule, string> ModulePath { get; }
// Singleton instance of ModuleConfigData.
private static readonly Lazy<ModuleConfigData> SingletonInstance = new Lazy<ModuleConfigData>(() => new ModuleConfigData());
public static ModuleConfigData Instance => SingletonInstance.Value;
public const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
public Dictionary<PowerToysModule, string> ModuleWindowName { get; }
private ModuleConfigData()
{
// The exe window name for each module.
ModuleWindowName = new Dictionary<PowerToysModule, string>
{
[PowerToysModule.PowerToysSettings] = "PowerToys Settings",
[PowerToysModule.FancyZone] = "FancyZones Layout",
[PowerToysModule.Hosts] = "Hosts File Editor",
};
// Exe start path for the module if it exists.
ModulePath = new Dictionary<PowerToysModule, string>
{
[PowerToysModule.PowerToysSettings] = @"\..\..\..\WinUI3Apps\PowerToys.Settings.exe",
[PowerToysModule.FancyZone] = @"\..\..\..\PowerToys.FancyZonesEditor.exe",
[PowerToysModule.Hosts] = @"\..\..\..\WinUI3Apps\PowerToys.Hosts.exe",
};
}
public string GetModulePath(PowerToysModule scope) => ModulePath[scope];
public string GetWindowsApplicationDriverUrl() => WindowsApplicationDriverUrl;
public string GetModuleWindowName(PowerToysModule scope) => ModuleWindowName[scope];
}
}

View File

@@ -0,0 +1,129 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Provides interfaces for interacting with UI elements.
/// </summary>
public class Session
{
private WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement> WindowsDriver { get; set; }
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(nint hWnd);
public Session(WindowsDriver<WindowsElement> root, WindowsDriver<WindowsElement> windowsDriver)
{
this.Root = root;
this.WindowsDriver = windowsDriver;
}
/// <summary>
/// Finds an element by selector.
/// </summary>
/// <typeparam name="T">The class of the element, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the element.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>The found element.</returns>
public T Find<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method Find<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
var foundElement = FindElementHelper.Find<T, WindowsElement>(
() =>
{
var element = this.WindowsDriver.FindElement(by.ToSeleniumBy());
Assert.IsNotNull(element, $"Element not found using selector: {by}");
return element;
},
this.WindowsDriver,
timeoutMS);
return foundElement;
}
/// <summary>
/// Finds all elements by selector.
/// </summary>
/// <typeparam name="T">The class of the elements, should be Element or its derived class.</typeparam>
/// <param name="by">The selector to find the elements.</param>
/// <param name="timeoutMS">The timeout in milliseconds (default is 3000).</param>
/// <returns>A read-only collection of the found elements.</returns>
public ReadOnlyCollection<T>? FindAll<T>(By by, int timeoutMS = 3000)
where T : Element, new()
{
Assert.IsNotNull(this.WindowsDriver, $"WindowsElement is null in method FindAll<{typeof(T).Name}> with parameters: by = {by}, timeoutMS = {timeoutMS}");
var foundElements = FindElementHelper.FindAll<T, WindowsElement>(
() =>
{
var elements = this.WindowsDriver.FindElements(by.ToSeleniumBy());
Assert.IsTrue(elements.Count > 0, $"Elements not found using selector: {by}");
return elements;
},
this.WindowsDriver,
timeoutMS);
return foundElements;
}
/// <summary>
/// Attaches to an existing PowerToys module.
/// </summary>
/// <param name="module">The PowerToys module to attach to.</param>
/// <returns>The attached session.</returns>
public Session Attach(PowerToysModule module)
{
string windowName = ModuleConfigData.Instance.GetModuleWindowName(module);
return this.Attach(windowName);
}
/// <summary>
/// Attaches to an existing exe by string window name.
/// The session should be attached when a new app is started.
/// </summary>
/// <param name="windowName">The window name to attach to.</param>
/// <returns>The attached session.</returns>
public Session Attach(string windowName)
{
if (this.Root != null)
{
var window = this.Root.FindElementByName(windowName);
Assert.IsNotNull(window, $"Failed to attach. Window '{windowName}' not found");
var windowHandle = new nint(int.Parse(window.GetAttribute("NativeWindowHandle")));
SetForegroundWindow(windowHandle);
var hexWindowHandle = windowHandle.ToString("x");
var appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("appTopLevelWindow", hexWindowHandle);
appCapabilities.AddAdditionalCapability("deviceName", "WindowsPC");
this.WindowsDriver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), appCapabilities);
Assert.IsNotNull(this.WindowsDriver, "Attach WindowsDriver is null");
// Set implicit timeout to make element search retry every 500 ms
this.WindowsDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(3);
}
else
{
Assert.IsNotNull(this.Root, $"Failed to attach to the window '{windowName}'. Root driver is null");
}
return this;
}
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Appium.WebDriver" />
<PackageReference Include="MSTest" />
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,130 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Base class that should be inherited by all Test Classes.
/// </summary>
public class UITestBase
{
public Session Session { get; set; }
private readonly TestInit testInit = new TestInit();
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings)
{
this.testInit.SetScope(scope);
this.testInit.Init();
this.Session = new Session(this.testInit.GetRoot(), this.testInit.GetDriver());
}
~UITestBase()
{
this.testInit.Cleanup();
}
/// <summary>
/// Nested class for test initialization.
/// </summary>
private sealed class TestInit
{
private WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement>? Driver { get; set; }
private static Process? appDriver;
// Default session path is PowerToys settings dashboard
private static string sessionPath = ModuleConfigData.Instance.GetModulePath(PowerToysModule.PowerToysSettings);
public TestInit()
{
appDriver = Process.Start(new ProcessStartInfo
{
FileName = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe",
Verb = "runas",
});
var desktopCapabilities = new AppiumOptions();
desktopCapabilities.AddAdditionalCapability("app", "Root");
this.Root = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), desktopCapabilities);
// Set default timeout to 5 seconds
this.Root.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
/// <summary>
/// Initializes the test environment.
/// </summary>
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
public void Init()
{
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
this.StartExe(path + sessionPath);
Assert.IsNotNull(this.Driver, $"Failed to initialize the test environment. Driver is null.");
// Set default timeout to 5 seconds
this.Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
/// <summary>
/// Cleans up the test environment.
/// </summary>
public void Cleanup()
{
try
{
appDriver?.Kill();
}
catch (Exception ex)
{
// Handle exceptions if needed
Debug.WriteLine($"Exception during Cleanup: {ex.Message}");
}
}
/// <summary>
/// Starts a new exe and takes control of it.
/// </summary>
/// <param name="appPath">The path to the application executable.</param>
public void StartExe(string appPath)
{
var opts = new AppiumOptions();
opts.AddAdditionalCapability("app", appPath);
this.Driver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), opts);
}
/// <summary>
/// Sets scope to the Test Class.
/// </summary>
/// <param name="scope">The PowerToys module to start.</param>
public void SetScope(PowerToysModule scope)
{
sessionPath = ModuleConfigData.Instance.GetModulePath(scope);
}
public WindowsDriver<WindowsElement> GetRoot() => this.Root;
public WindowsDriver<WindowsElement> GetDriver()
{
Assert.IsNotNull(this.Driver, $"Failed to get driver. Driver is null.");
return this.Driver;
}
}
}
}

View File

@@ -0,0 +1,35 @@
# Create Fuzzing Tests in your .NET Code Project
This document provides a step-by-step guide for integrating fuzzing tests into your .NET project.
### Step1: Add a Fuzzing Test Project
Create a new test project within your module folder. Ensure the project name follows the format *.FuzzTests*.
### step2: Add FuzzTests and OneFuzzConfig.json to your fuzzing test project
Follow the instructions in [Fuzz.md](https://github.com/microsoft/PowerToys/blob/main/src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/Fuzz.md) from AdvancedPaste.FuzzTests to properly integrate fuzzing tests into your project.
Configuring **OneFuzzConfig.json**:
1. Update the dll, class, method, and FuzzingTargetBinaries field in the fuzzers list.
2. Modify the AssignedTo field in the adoTemplate list.
3. Set the jobNotificationEmail to your Microsoft email account.
4. Update the projectName and targetName fields in the oneFuzzJobs list.
5. Define job dependencies in the following directory:
Example:
```PowerToys\x64\Debug\tests\Hosts.FuzzTests\net8.0-windows10.0.19041.0```
# step3: Configure the OneFuzz Pipeline
Modify the patterns in the job steps within [job-fuzz.yml](https://github.com/microsoft/PowerToys/blob/main/.pipelines/v2/templates/job-fuzz.yml) to match your fuzzing project name.
Example:
```
- download: current
displayName: Download artifacts
artifact: $(ArtifactName)
patterns: |-
**/tests/Hosts.FuzzTests/**
```
# step4: Submit OneFuzz Pipeline and Verify Results on the OneFuzz Platform
After executing the tests, check your email for the job link. Click the link to review the fuzzing test results.

View File

@@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.IO.Abstractions.TestingHelpers;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Hosts.Tests.Mocks;
using HostsUILib.Helpers;
using HostsUILib.Models;
using HostsUILib.Settings;
using Moq;
namespace Hosts.FuzzTests
{
public class FuzzTests
{
private static Mock<IUserSettings> _userSettings;
private static Mock<IElevationHelper> _elevationHelper;
// Case1 Fuzzing method for ValidIPv4
public static void FuzzValidIPv4(ReadOnlySpan<byte> input)
{
try
{
string address = System.Text.Encoding.UTF8.GetString(input);
bool isValid = ValidationHelper.ValidIPv4(address);
}
catch (Exception ex) when (ex is RegexMatchTimeoutException)
{
throw;
}
}
// Case2: fuzzing method for ValidIPv6
public static void FuzzValidIPv6(ReadOnlySpan<byte> input)
{
try
{
string address = System.Text.Encoding.UTF8.GetString(input);
bool isValid = ValidationHelper.ValidIPv6(address);
}
catch (Exception ex) when (ex is RegexMatchTimeoutException)
{
throw;
}
}
// Case3: fuzzing method for ValidHosts
public static void FuzzValidHosts(ReadOnlySpan<byte> input)
{
try
{
string hosts = System.Text.Encoding.UTF8.GetString(input);
bool isValid = ValidationHelper.ValidHosts(hosts, true);
}
catch (Exception ex) when (ex is RegexMatchTimeoutException)
{
// It's important to filter out any *expected* exceptions from our code here.
// However, catching all exceptions is considered an anti-pattern because it may suppress legitimate
// issues, such as a NullReferenceException thrown by our code. In this case, we still re-throw
// the exception, as the ToJsonFromXmlOrCsvAsync method is not expected to throw any exceptions.
throw;
}
}
public static void FuzzWriteAsync(ReadOnlySpan<byte> data)
{
try
{
_userSettings = new Mock<IUserSettings>();
_elevationHelper = new Mock<IElevationHelper>();
_elevationHelper.Setup(m => m.IsElevated).Returns(true);
var fileSystem = new CustomMockFileSystem();
var service = new HostsService(fileSystem, _userSettings.Object, _elevationHelper.Object);
string input = System.Text.Encoding.UTF8.GetString(data);
// Since the WriteAsync method does not involve content parsing, we won't fuzz the additionalLines in the hosts file.
string additionalLines = " ";
string hosts = input;
string address = input;
string comments = input;
var entries = new List<Entry>
{
new Entry(1, hosts, address, comments, true),
};
// fuzzing WriteAsync
_ = Task.Run(async () => await service.WriteAsync(additionalLines, entries));
}
catch (Exception ex) when (ex is ArgumentException)
{
throw;
}
}
}
}

View File

@@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\Hosts.FuzzTests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Hosts.Tests\Mocks\CustomMockFileSystem.cs" Link="CustomMockFileSystem.cs" />
<Compile Include="..\Hosts.Tests\Mocks\MockFileSystemWatcher.cs" Link="MockFileSystemWatcher.cs" />
<Compile Include="..\Hosts.Tests\Mocks\MockFileSystemWatcherFactory.cs" Link="MockFileSystemWatcherFactory.cs" />
<Compile Include="..\HostsUILib\Consts.cs" Link="Consts.cs" />
<Compile Include="..\HostsUILib\Helpers\ValidationHelper.cs" Link="ValidationHelper.cs" />
<Compile Include="..\HostsUILib\Exceptions\NotRunningElevatedException.cs" Link="NotRunningElevatedException.cs" />
<Compile Include="..\HostsUILib\Exceptions\ReadOnlyHostsException.cs" Link="ReadOnlyHostsException.cs" />
<Compile Include="..\HostsUILib\Helpers\HostsService.cs" Link="HostsService.cs" />
<Compile Include="..\HostsUILib\Helpers\IElevationHelper.cs" Link="IElevationHelper.cs" />
<Compile Include="..\HostsUILib\Helpers\IHostsService.cs" Link="IHostsService.cs" />
<Compile Include="..\HostsUILib\Helpers\ILogger.cs" Link="ILogger.cs" />
<Compile Include="..\HostsUILib\Helpers\LoggerInstance.cs" Link="LoggerInstance.cs" />
<Compile Include="..\HostsUILib\Models\AddressType.cs" Link="AddressType.cs" />
<Compile Include="..\HostsUILib\Models\Entry.cs" Link="Entry.cs" />
<Compile Include="..\HostsUILib\Models\HostsData.cs" Link="HostsData.cs" />
<Compile Include="..\HostsUILib\Settings\HostsAdditionalLinesPosition.cs" Link="HostsAdditionalLinesPosition.cs" />
<Compile Include="..\HostsUILib\Settings\HostsEncoding.cs" Link="HostsEncoding.cs" />
<Compile Include="..\HostsUILib\Settings\IUserSettings.cs" Link="IUserSettings.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq" />
<PackageReference Include="MSTest" />
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" />
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
<ItemGroup>
<Content Include="OneFuzzConfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]

View File

@@ -0,0 +1,178 @@
{
"configVersion": 3,
"entries": [
{
"fuzzer": {
"$type": "libfuzzerDotNet",
"dll": "Hosts.FuzzTests.dll",
"class": "Hosts.FuzzTests.FuzzTests",
"method": "FuzzValidIPv4",
"FuzzingTargetBinaries": [
"PowerToys.Hosts.dll"
]
},
"adoTemplate": {
// supply the values appropriate to your
// project, where bugs will be filed
"org": "microsoft",
"project": "OS",
"AssignedTo": "mengyuanchen@microsoft.com",
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys",
"IterationPath": "OS\\Future"
},
"jobNotificationEmail": "mengyuanchen@microsoft.com",
"skip": false,
"rebootAfterSetup": false,
"oneFuzzJobs": [
// at least one job is required
{
"projectName": "Hosts",
"targetName": "Hosts-dotnet-fuzzer-Ipv4"
}
],
"jobDependencies": [
// this should contain, at minimum,
// the DLL and PDB files
// you will need to add any other files required
// (globs are supported)
"Hosts.FuzzTests.dll",
"Hosts.FuzzTests.pdb",
"Microsoft.Windows.SDK.NET.dll",
"WinRT.Runtime.dll"
],
"SdlWorkItemId": 49911822
},
{
"fuzzer": {
"$type": "libfuzzerDotNet",
"dll": "Hosts.FuzzTests.dll",
"class": "Hosts.FuzzTests.FuzzTests",
"method": "FuzzValidIPv6",
"FuzzingTargetBinaries": [
"PowerToys.Hosts.dll"
]
},
"adoTemplate": {
// supply the values appropriate to your
// project, where bugs will be filed
"org": "microsoft",
"project": "OS",
"AssignedTo": "mengyuanchen@microsoft.com",
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys",
"IterationPath": "OS\\Future"
},
"jobNotificationEmail": "mengyuanchen@microsoft.com",
"skip": false,
"rebootAfterSetup": false,
"oneFuzzJobs": [
// at least one job is required
{
"projectName": "Hosts",
"targetName": "Hosts-dotnet-fuzzer-Ipv6"
}
],
"jobDependencies": [
// this should contain, at minimum,
// the DLL and PDB files
// you will need to add any other files required
// (globs are supported)
"Hosts.FuzzTests.dll",
"Hosts.FuzzTests.pdb",
"Microsoft.Windows.SDK.NET.dll",
"WinRT.Runtime.dll"
],
"SdlWorkItemId": 49911822
},
{
"fuzzer": {
"$type": "libfuzzerDotNet",
"dll": "Hosts.FuzzTests.dll",
"class": "Hosts.FuzzTests.FuzzTests",
"method": "FuzzValidHosts",
"FuzzingTargetBinaries": [
"PowerToys.Hosts.dll"
]
},
"adoTemplate": {
// supply the values appropriate to your
// project, where bugs will be filed
"org": "microsoft",
"project": "OS",
"AssignedTo": "mengyuanchen@microsoft.com",
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys",
"IterationPath": "OS\\Future"
},
"jobNotificationEmail": "mengyuanchen@microsoft.com",
"skip": false,
"rebootAfterSetup": false,
"oneFuzzJobs": [
// at least one job is required
{
"projectName": "Hosts",
"targetName": "Hosts-dotnet-fuzzer-hosts"
}
],
"jobDependencies": [
// this should contain, at minimum,
// the DLL and PDB files
// you will need to add any other files required
// (globs are supported)
"Hosts.FuzzTests.dll",
"Hosts.FuzzTests.pdb",
"Microsoft.Windows.SDK.NET.dll",
"WinRT.Runtime.dll"
],
"SdlWorkItemId": 49911822
},
{
"fuzzer": {
"$type": "libfuzzerDotNet",
"dll": "Hosts.FuzzTests.dll",
"class": "Hosts.FuzzTests.FuzzTests",
"method": "FuzzWriteAsync",
"FuzzingTargetBinaries": [
"PowerToys.Hosts.dll"
]
},
"adoTemplate": {
// supply the values appropriate to your
// project, where bugs will be filed
"org": "microsoft",
"project": "OS",
"AssignedTo": "mengyuanchen@microsoft.com",
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys",
"IterationPath": "OS\\Future"
},
"jobNotificationEmail": "mengyuanchen@microsoft.com",
"skip": false,
"rebootAfterSetup": false,
"oneFuzzJobs": [
// at least one job is required
{
"projectName": "Hosts",
"targetName": "Hosts-dotnet-fuzzer-WriteAsync"
}
],
"jobDependencies": [
// this should contain, at minimum,
// the DLL and PDB files
// you will need to add any other files required
// (globs are supported)
"Hosts.FuzzTests.dll",
"Hosts.FuzzTests.pdb",
"Microsoft.Windows.SDK.NET.dll",
"WinRT.Runtime.dll",
"Moq.dll",
"testhost.dll",
"Castle.Core.dll",
"System.IO.Abstractions.dll",
"CommunityToolkit.Mvvm.dll",
"System.IO.Abstractions.TestingHelpers.dll",
"TestableIO.System.IO.Abstractions.dll",
"TestableIO.System.IO.Abstractions.TestingHelpers.dll",
"TestableIO.System.IO.Abstractions.Wrappers.dll"
],
"SdlWorkItemId": 49911822
}
]
}

View File

@@ -233,6 +233,12 @@ void Highlighter::ClearDrawingPoint(MouseButton _button)
{
winrt::Windows::UI::Composition::CompositionSpriteShape circleShape{ nullptr };
if (nullptr == m_alwaysPointer)
{
// Guard against alwaysPointer not being initialized.
return;
}
// always
circleShape = m_alwaysPointer;
@@ -265,6 +271,11 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa
// Clear AlwaysPointer only when it's enabled and RightPointer is not active
instance->ClearDrawingPoint(MouseButton::None);
}
if (instance->m_leftButtonPressed)
{
// There might be a stray point from the user releasing the mouse button on an elevated window, which wasn't caught by us.
instance->StartDrawingPointFading(MouseButton::Left);
}
instance->AddDrawingPoint(MouseButton::Left);
instance->m_leftButtonPressed = true;
// start a timer for the scenario, when the user clicks a pinned window which has no focus.
@@ -284,6 +295,11 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa
// Clear AlwaysPointer only when it's enabled and LeftPointer is not active
instance->ClearDrawingPoint(MouseButton::None);
}
if (instance->m_rightButtonPressed)
{
// There might be a stray point from the user releasing the mouse button on an elevated window, which wasn't caught by us.
instance->StartDrawingPointFading(MouseButton::Right);
}
instance->AddDrawingPoint(MouseButton::Right);
instance->m_rightButtonPressed = true;
// same as for the left button, start a timer for reposition ourselves to topmost position

View File

@@ -431,7 +431,7 @@ namespace MouseWithoutBorders
if (!IsConnectedByAClientSocketTo(remoteMachine))
{
Logger.Log($"No potential inbound connection from {MachineName} to {remoteMachine}, ask for a push back instead.");
ID machineId = MachinePool.ResolveID(remoteMachine);
ID machineId = MachineStuff.MachinePool.ResolveID(remoteMachine);
if (machineId != ID.NONE)
{
@@ -840,7 +840,7 @@ namespace MouseWithoutBorders
Logger.LogDebug($"{nameof(ShakeHand)}: Connection from {name}:{package.Src}");
if (Common.MachinePool.ResolveID(name) == package.Src && Common.IsConnectedTo(package.Src))
if (MachineStuff.MachinePool.ResolveID(name) == package.Src && Common.IsConnectedTo(package.Src))
{
clientPushData = package.Type == PackageType.ClipboardPush;
postAction = package.PostAction;

View File

@@ -1,409 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.PowerToys.Telemetry;
// <summary>
// Drag/Drop implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Core;
using Thread = MouseWithoutBorders.Core.Thread;
namespace MouseWithoutBorders
{
/* Common.DragDrop.cs
* Drag&Drop is one complicated implementation of the tool with some tricks.
*
* SEQUENCE OF EVENTS:
* DragDropStep01: MachineX: Remember mouse down state since it could be a start of a dragging
* DragDropStep02: MachineY: Send an message to the MachineX to ask it to check if it is
* doing drag/drop
* DragDropStep03: MachineX: Got explorerDragDrop, send WM_CHECK_EXPLORER_DRAG_DROP to its mainForm
* DragDropStep04: MachineX: Show Mouse Without Borders Helper form at mouse cursor to get DragEnter event.
* DragDropStepXX: MachineX: Mouse Without Borders Helper: Called by DragEnter, check if dragging a single file,
* remember the file (set as its window caption)
* DragDropStep05: MachineX: Get the file name from Mouse Without Borders Helper, hide Mouse Without Borders Helper window
* DragDropStep06: MachineX: Broadcast a message saying that it has some drag file.
* DragDropStep08: MachineY: Got ClipboardDragDrop, isDropping set, get the MachineX name from the package.
* DragDropStep09: MachineY: Since isDropping is true, show up the drop form (looks like an icon).
* DragDropStep10: MachineY: MouseUp, set isDropping to false, hide the drop "icon" and get data.
* DragDropStep11: MachineX: Mouse move back without drop event, cancelling drag/dop
* SendClipboardBeatDragDropEnd
* DragDropStep12: MachineY: Hide the drop "icon" when received ClipboardDragDropEnd.
*
* FROM VERSION 1.6.3: Drag/Drop is temporary removed, Drop action cannot be done from a lower integrity app to a higher one.
* We have to run a helper process...
* http://forums.microsoft.com/MSDN/ShowPost.aspx?PageIndex=1&SiteID=1&PageID=1&PostID=736086
*
* 2008.10.28: Trying to restore the Drag/Drop feature by adding the drag/drop helper process. Coming in version
* 1.6.5
* */
internal partial class Common
{
private static bool isDragging;
internal static bool IsDragging
{
get => Common.isDragging;
set => Common.isDragging = value;
}
internal static void DragDropStep01(int wParam)
{
if (!Setting.Values.TransferFile)
{
return;
}
if (wParam == WM_LBUTTONDOWN)
{
MouseDown = true;
DragMachine = desMachineID;
dropMachineID = ID.NONE;
Logger.LogDebug("DragDropStep01: MouseDown");
}
else if (wParam == WM_LBUTTONUP)
{
MouseDown = false;
Logger.LogDebug("DragDropStep01: MouseUp");
}
if (wParam == WM_RBUTTONUP && IsDropping)
{
IsDropping = false;
LastIDWithClipboardData = ID.NONE;
}
}
internal static void DragDropStep02()
{
if (desMachineID == MachineID)
{
Logger.LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent to myself");
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
else
{
SendCheckExplorerDragDrop();
Logger.LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent");
}
}
internal static void DragDropStep03(DATA package)
{
if (RunOnLogonDesktop || RunOnScrSaverDesktop)
{
return;
}
if (package.Des == MachineID || package.Des == ID.ALL)
{
Logger.LogDebug("DragDropStep03: ExplorerDragDrop Received.");
dropMachineID = package.Src; // Drop machine is the machine that sent ExplorerDragDrop
if (MouseDown || IsDropping)
{
Logger.LogDebug("DragDropStep03: Mouse is down, check if dragging...sending WM_CHECK_EXPLORER_DRAG_DROP to myself...");
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
}
}
private static int dragDropStep05ExCalledByIpc;
internal static void DragDropStep04()
{
if (!IsDropping)
{
IntPtr h = (IntPtr)NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT);
if (h.ToInt32() > 0)
{
_ = Interlocked.Exchange(ref dragDropStep05ExCalledByIpc, 0);
MainForm.Hide();
MainFormVisible = false;
Point p = default;
// NativeMethods.SetWindowText(h, "");
_ = NativeMethods.SetWindowPos(h, NativeMethods.HWND_TOPMOST, 0, 0, 0, 0, NativeMethods.SWP_SHOWWINDOW);
for (int i = -10; i < 10; i++)
{
if (dragDropStep05ExCalledByIpc > 0)
{
Logger.LogDebug("DragDropStep04: DragDropStep05ExCalledByIpc.");
break;
}
_ = NativeMethods.GetCursorPos(ref p);
Logger.LogDebug("DragDropStep04: Moving Mouse Without Borders Helper to (" + p.X.ToString(CultureInfo.CurrentCulture) + ", " + p.Y.ToString(CultureInfo.CurrentCulture) + ")");
_ = NativeMethods.SetWindowPos(h, NativeMethods.HWND_TOPMOST, p.X - 100 + i, p.Y - 100 + i, 200, 200, 0);
_ = NativeMethods.SendMessage(h, 0x000F, IntPtr.Zero, IntPtr.Zero); // WM_PAINT
Thread.Sleep(20);
Application.DoEvents();
// if (GetText(h).Length > 1) break;
}
}
else
{
Logger.LogDebug("DragDropStep04: Mouse without Borders Helper not found!");
}
}
else
{
Logger.LogDebug("DragDropStep04: IsDropping == true, skip checking");
}
Logger.LogDebug("DragDropStep04: Got WM_CHECK_EXPLORER_DRAG_DROP, done with processing jump to DragDropStep05...");
}
internal static void DragDropStep05Ex(string dragFileName)
{
Logger.LogDebug("DragDropStep05 called.");
_ = Interlocked.Exchange(ref dragDropStep05ExCalledByIpc, 1);
if (RunOnLogonDesktop || RunOnScrSaverDesktop)
{
return;
}
if (!IsDropping)
{
_ = Common.ImpersonateLoggedOnUserAndDoSomething(() =>
{
if (!string.IsNullOrEmpty(dragFileName) && (File.Exists(dragFileName) || Directory.Exists(dragFileName)))
{
Common.LastDragDropFile = dragFileName;
/*
* possibleDropMachineID is used as desID sent in DragDropStep06();
* */
if (dropMachineID == ID.NONE)
{
dropMachineID = newDesMachineID;
}
DragDropStep06();
Logger.LogDebug("DragDropStep05: File dragging: " + dragFileName);
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DD_HELPER, (IntPtr)1, (IntPtr)0);
}
else
{
Logger.LogDebug("DragDropStep05: File not found: [" + dragFileName + "]");
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DD_HELPER, (IntPtr)0, (IntPtr)0);
}
Logger.LogDebug("DragDropStep05: WM_HIDE_DDHelper sent");
});
}
else
{
Logger.LogDebug("DragDropStep05: IsDropping == true, change drop machine...");
IsDropping = false;
MainFormVisible = true; // WM_HIDE_DRAG_DROP
SendDropBegin(); // To dropMachineID set in DragDropStep03
}
MouseDown = false;
}
internal static void DragDropStep06()
{
IsDragging = true;
Logger.LogDebug("DragDropStep06: SendClipboardBeatDragDrop");
SendClipboardBeatDragDrop();
SendDropBegin();
}
internal static void DragDropStep08(DATA package)
{
Receiver.GetNameOfMachineWithClipboardData(package);
Logger.LogDebug("DragDropStep08: ClipboardDragDrop Received. machine with drag file was set");
}
internal static void DragDropStep08_2(DATA package)
{
if (package.Des == MachineID && !RunOnLogonDesktop && !RunOnScrSaverDesktop)
{
IsDropping = true;
dropMachineID = MachineID;
Logger.LogDebug("DragDropStep08_2: ClipboardDragDropOperation Received. IsDropping set");
}
}
internal static void DragDropStep09(int wParam)
{
if (wParam == WM_MOUSEMOVE && IsDropping)
{
// Show/Move form
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_SHOW_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
else if (wParam == WM_LBUTTONUP && (IsDropping || IsDragging))
{
if (IsDropping)
{
// Hide form, get data
DragDropStep10();
}
else
{
IsDragging = false;
LastIDWithClipboardData = ID.NONE;
}
}
}
internal static void DragDropStep10()
{
Logger.LogDebug("DragDropStep10: Hide the form and get data...");
IsDropping = false;
IsDragging = false;
LastIDWithClipboardData = ID.NONE;
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersDragAndDropEvent());
GetRemoteClipboard("desktop");
}
internal static void DragDropStep11()
{
Logger.LogDebug("DragDropStep11: Mouse drag coming back, canceling drag/drop");
SendClipboardBeatDragDropEnd();
IsDropping = false;
IsDragging = false;
DragMachine = (ID)1;
LastIDWithClipboardData = ID.NONE;
LastDragDropFile = null;
MouseDown = false;
}
internal static void DragDropStep12()
{
Logger.LogDebug("DragDropStep12: ClipboardDragDropEnd received");
IsDropping = false;
LastIDWithClipboardData = ID.NONE;
DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
internal static void SendCheckExplorerDragDrop()
{
DATA package = new();
package.Type = PackageType.ExplorerDragDrop;
/*
* package.src = newDesMachineID:
* sent from the master machine but the src must be the
* new des machine since the previous des machine will get this and set
* to possibleDropMachineID in DragDropStep3()
* */
package.Src = newDesMachineID;
package.Des = desMachineID;
package.MachineName = MachineName;
SkSend(package, null, false);
}
private static void ChangeDropMachine()
{
// desMachineID = current drop machine
// newDesMachineID = new drop machine
// 1. Cancelling dropping in current drop machine
if (dropMachineID == MachineID)
{
// Drag/Drop coming through me
IsDropping = false;
}
else
{
// Drag/Drop coming back
SendClipboardBeatDragDropEnd();
}
// 2. SendClipboardBeatDragDrop to new drop machine
// new drop machine is not me
if (newDesMachineID != MachineID)
{
dropMachineID = newDesMachineID;
SendDropBegin();
}
// New drop machine is me
else
{
IsDropping = true;
}
}
internal static void SendClipboardBeatDragDrop()
{
SendPackage(ID.ALL, PackageType.ClipboardDragDrop);
}
internal static void SendDropBegin()
{
Logger.LogDebug("SendDropBegin...");
SendPackage(dropMachineID, PackageType.ClipboardDragDropOperation);
}
internal static void SendClipboardBeatDragDropEnd()
{
if (desMachineID != MachineID)
{
SendPackage(desMachineID, PackageType.ClipboardDragDropEnd);
}
}
private static bool isDropping;
private static ID dragMachine;
internal static ID DragMachine
{
get => Common.dragMachine;
set => Common.dragMachine = value;
}
internal static bool IsDropping
{
get => Common.isDropping;
set => Common.isDropping = value;
}
internal static bool MouseDown { get; set; }
}
}

View File

@@ -72,7 +72,7 @@ namespace MouseWithoutBorders
if (switchByMouseEnabled && Sk != null && (DesMachineID == MachineID || !Setting.Values.MoveMouseRelatively) && e.dwFlags == WM_MOUSEMOVE)
{
Point p = MoveToMyNeighbourIfNeeded(e.X, e.Y, desMachineID);
Point p = MachineStuff.MoveToMyNeighbourIfNeeded(e.X, e.Y, MachineStuff.desMachineID);
if (!p.IsEmpty)
{
@@ -81,20 +81,20 @@ namespace MouseWithoutBorders
Logger.LogDebug(string.Format(
CultureInfo.CurrentCulture,
"***** Host Machine: newDesMachineIdEx set = [{0}]. Mouse is now at ({1},{2})",
newDesMachineIdEx,
MachineStuff.newDesMachineIdEx,
e.X,
e.Y));
myLastX = e.X;
myLastY = e.Y;
PrepareToSwitchToMachine(newDesMachineIdEx, p);
PrepareToSwitchToMachine(MachineStuff.newDesMachineIdEx, p);
}
}
if (desMachineID != MachineID && SwitchLocation.Count <= 0)
if (MachineStuff.desMachineID != MachineID && MachineStuff.SwitchLocation.Count <= 0)
{
MousePackage.Des = desMachineID;
MousePackage.Des = MachineStuff.desMachineID;
MousePackage.Type = PackageType.Mouse;
MousePackage.Md.dwFlags = e.dwFlags;
MousePackage.Md.WheelDelta = e.WheelDelta;
@@ -107,8 +107,8 @@ namespace MouseWithoutBorders
}
else
{
MousePackage.Md.X = (e.X - primaryScreenBounds.Left) * 65535 / screenWidth;
MousePackage.Md.Y = (e.Y - primaryScreenBounds.Top) * 65535 / screenHeight;
MousePackage.Md.X = (e.X - MachineStuff.primaryScreenBounds.Left) * 65535 / screenWidth;
MousePackage.Md.Y = (e.Y - MachineStuff.primaryScreenBounds.Top) * 65535 / screenHeight;
}
SkSend(MousePackage, null, false);
@@ -156,15 +156,15 @@ namespace MouseWithoutBorders
{
Logger.LogDebug($"PrepareToSwitchToMachine: newDesMachineID = {newDesMachineID}, desMachineXY = {desMachineXY}");
if (((GetTick() - lastJump < 100) && (GetTick() - lastJump > 0)) || desMachineID == ID.ALL)
if (((GetTick() - MachineStuff.lastJump < 100) && (GetTick() - MachineStuff.lastJump > 0)) || MachineStuff.desMachineID == ID.ALL)
{
Logger.LogDebug("PrepareToSwitchToMachine: lastJump");
return;
}
lastJump = GetTick();
MachineStuff.lastJump = GetTick();
string newDesMachineName = NameFromID(newDesMachineID);
string newDesMachineName = MachineStuff.NameFromID(newDesMachineID);
if (!IsConnectedTo(newDesMachineID))
{// Connection lost, cancel switching
@@ -174,46 +174,46 @@ namespace MouseWithoutBorders
}
else
{
Common.newDesMachineID = newDesMachineID;
SwitchLocation.X = desMachineXY.X;
SwitchLocation.Y = desMachineXY.Y;
SwitchLocation.ResetCount();
MachineStuff.newDesMachineID = newDesMachineID;
MachineStuff.SwitchLocation.X = desMachineXY.X;
MachineStuff.SwitchLocation.Y = desMachineXY.Y;
MachineStuff.SwitchLocation.ResetCount();
_ = EvSwitch.Set();
// PostMessage(mainForm.Handle, WM_SWITCH, IntPtr.Zero, IntPtr.Zero);
if (newDesMachineID != DragMachine)
if (newDesMachineID != DragDrop.DragMachine)
{
if (!IsDragging && !IsDropping)
if (!DragDrop.IsDragging && !DragDrop.IsDropping)
{
if (MouseDown && !RunOnLogonDesktop && !RunOnScrSaverDesktop)
if (DragDrop.MouseDown && !RunOnLogonDesktop && !RunOnScrSaverDesktop)
{
DragDropStep02();
DragDrop.DragDropStep02();
}
}
else if (DragMachine != (ID)1)
else if (DragDrop.DragMachine != (ID)1)
{
ChangeDropMachine();
DragDrop.ChangeDropMachine();
}
}
else
{
DragDropStep11();
DragDrop.DragDropStep11();
}
// Change des machine
if (desMachineID != newDesMachineID)
if (MachineStuff.desMachineID != newDesMachineID)
{
Logger.LogDebug("MouseEvent: Switching to new machine:" + newDesMachineName);
// Ask current machine to hide the Mouse cursor
if (newDesMachineID != ID.ALL && desMachineID != MachineID)
if (newDesMachineID != ID.ALL && MachineStuff.desMachineID != MachineID)
{
SendPackage(desMachineID, PackageType.HideMouse);
SendPackage(MachineStuff.desMachineID, PackageType.HideMouse);
}
DesMachineID = newDesMachineID;
if (desMachineID == MachineID)
if (MachineStuff.desMachineID == MachineID)
{
if (GetTick() - clipboardCopiedTime < BIG_CLIPBOARD_DATA_TIMEOUT)
{
@@ -224,7 +224,7 @@ namespace MouseWithoutBorders
else
{
// Ask the new active machine to get clipboard data (if the data is too big)
SendPackage(desMachineID, PackageType.MachineSwitched);
SendPackage(MachineStuff.desMachineID, PackageType.MachineSwitched);
}
_ = Interlocked.Increment(ref switchCount);
@@ -249,15 +249,15 @@ namespace MouseWithoutBorders
try
{
PaintCount = 0;
if (desMachineID != newDesMachineID)
if (MachineStuff.desMachineID != MachineStuff.newDesMachineID)
{
Logger.LogDebug("KeybdEvent: Switching to new machine...");
DesMachineID = newDesMachineID;
DesMachineID = MachineStuff.newDesMachineID;
}
if (desMachineID != MachineID)
if (MachineStuff.desMachineID != MachineID)
{
KeybdPackage.Des = desMachineID;
KeybdPackage.Des = MachineStuff.desMachineID;
KeybdPackage.Type = PackageType.Keyboard;
KeybdPackage.Kd = e;
KeybdPackage.DateTime = GetTick();

View File

@@ -9,6 +9,7 @@ using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Windows.Forms;
@@ -87,35 +88,35 @@ namespace MouseWithoutBorders
break;
}
if (Common.NewDesMachineID != Common.MachineID && Common.NewDesMachineID != ID.ALL)
if (MachineStuff.NewDesMachineID != Common.MachineID && MachineStuff.NewDesMachineID != ID.ALL)
{
HideMouseCursor(false);
Common.MainFormDotEx(true);
}
else
{
if (Common.SwitchLocation.Count > 0)
if (MachineStuff.SwitchLocation.Count > 0)
{
Common.SwitchLocation.Count--;
MachineStuff.SwitchLocation.Count--;
// When we want to move mouse by pixels, we add 300k to x and y (search for XY_BY_PIXEL for other related code).
Logger.LogDebug($"+++++ Moving mouse to {Common.SwitchLocation.X}, {Common.SwitchLocation.Y}");
Logger.LogDebug($"+++++ Moving mouse to {MachineStuff.SwitchLocation.X}, {MachineStuff.SwitchLocation.Y}");
// MaxXY = 65535 so 100k is safe.
if (Common.SwitchLocation.X > XY_BY_PIXEL - 100000 || Common.SwitchLocation.Y > XY_BY_PIXEL - 100000)
if (MachineStuff.SwitchLocation.X > XY_BY_PIXEL - 100000 || MachineStuff.SwitchLocation.Y > XY_BY_PIXEL - 100000)
{
InputSimulation.MoveMouse(Common.SwitchLocation.X - XY_BY_PIXEL, Common.SwitchLocation.Y - XY_BY_PIXEL);
InputSimulation.MoveMouse(MachineStuff.SwitchLocation.X - XY_BY_PIXEL, MachineStuff.SwitchLocation.Y - XY_BY_PIXEL);
}
else
{
InputSimulation.MoveMouseEx(Common.SwitchLocation.X, Common.SwitchLocation.Y);
InputSimulation.MoveMouseEx(MachineStuff.SwitchLocation.X, MachineStuff.SwitchLocation.Y);
}
Common.MainFormDot();
}
}
if (Common.NewDesMachineID == Common.MachineID)
if (MachineStuff.NewDesMachineID == Common.MachineID)
{
ReleaseAllKeys();
}
@@ -136,8 +137,8 @@ namespace MouseWithoutBorders
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
int left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 1;
int top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2);
int left = MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2) - 1;
int top = Setting.Values.HideMouse ? 3 : MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2);
Common.MainFormVisible = true;
@@ -197,8 +198,8 @@ namespace MouseWithoutBorders
DoSomethingInUIThread(
() =>
{
MainForm.Left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 2;
MainForm.Top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2) - 1;
MainForm.Left = MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2) - 2;
MainForm.Top = Setting.Values.HideMouse ? 3 : MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2) - 1;
MainForm.Width = 3;
MainForm.Height = 3;
MainForm.Opacity = 0.11D;
@@ -229,8 +230,8 @@ namespace MouseWithoutBorders
{
_ = Common.SendMessageToHelper(0x408, IntPtr.Zero, IntPtr.Zero, false);
MainForm.Left = Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2) - 1;
MainForm.Top = Setting.Values.HideMouse ? 3 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2);
MainForm.Left = MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2) - 1;
MainForm.Top = Setting.Values.HideMouse ? 3 : MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2);
MainForm.Width = 1;
MainForm.Height = 1;
MainForm.Opacity = 0.15;
@@ -380,7 +381,7 @@ namespace MouseWithoutBorders
log += $"{Setting.Values.Username}/{GetDebugInfo(MyKey)}\r\n";
log += $"{MachineName}/{MachineID}/{DesMachineID}\r\n";
log += $"Id: {Setting.Values.DeviceId}\r\n";
log += $"Matrix: {string.Join(",", MachineMatrix)}\r\n";
log += $"Matrix: {string.Join(",", MachineStuff.MachineMatrix)}\r\n";
log += $"McPool: {Setting.Values.MachinePoolString}\r\n";
log += "\r\nOPTIONS:\r\n";
@@ -443,7 +444,31 @@ namespace MouseWithoutBorders
{
_ = Common.ImpersonateLoggedOnUserAndDoSomething(() =>
{
Setting.Values.Username = WindowsIdentity.GetCurrent(true).Name;
// See: https://stackoverflow.com/questions/19487541/how-to-get-windows-user-name-from-sessionid
static string GetUsernameBySessionId(int sessionId)
{
string username = "SYSTEM";
if (NativeMethods.WTSQuerySessionInformation(IntPtr.Zero, sessionId, NativeMethods.WTSInfoClass.WTSUserName, out nint buffer, out int strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer);
NativeMethods.WTSFreeMemory(buffer);
if (NativeMethods.WTSQuerySessionInformation(IntPtr.Zero, sessionId, NativeMethods.WTSInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
{
username = @$"{Marshal.PtrToStringAnsi(buffer)}\{username}";
NativeMethods.WTSFreeMemory(buffer);
}
}
return username;
}
// The most direct way to fetch the username is WindowsIdentity.GetCurrent(true).Name
// but GetUserName can run within an ExecutionContext.SuppressFlow block, which creates issues
// with WindowsIdentity.GetCurrent.
// See: https://stackoverflow.com/questions/76998988/exception-when-using-executioncontext-suppressflow-in-net-7
// So we use WTSQuerySessionInformation as a workaround.
Setting.Values.Username = GetUsernameBySessionId(Process.GetCurrentProcess().SessionId);
});
}
else

View File

@@ -46,7 +46,7 @@ namespace MouseWithoutBorders
internal static void UpdateMachineTimeAndID()
{
Common.MachineName = Common.MachineName.Trim();
_ = Common.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
}
private static void InitializeMachinePoolFromSettings()
@@ -59,13 +59,13 @@ namespace MouseWithoutBorders
info[i].Name = info[i].Name.Trim();
}
Common.MachinePool.Initialize(info);
Common.MachinePool.ResetIPAddressesForDeadMachines(true);
MachineStuff.MachinePool.Initialize(info);
MachineStuff.MachinePool.ResetIPAddressesForDeadMachines(true);
}
catch (Exception ex)
{
Logger.Log(ex);
Common.MachinePool.Clear();
MachineStuff.MachinePool.Clear();
}
}
@@ -74,16 +74,16 @@ namespace MouseWithoutBorders
try
{
GetMachineName();
DesMachineID = NewDesMachineID = MachineID;
DesMachineID = MachineStuff.NewDesMachineID = MachineID;
// MessageBox.Show(machineID.ToString(CultureInfo.CurrentCulture)); // For test
InitializeMachinePoolFromSettings();
Common.MachineName = Common.MachineName.Trim();
_ = Common.MachinePool.LearnMachine(Common.MachineName);
_ = Common.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
_ = MachineStuff.MachinePool.LearnMachine(Common.MachineName);
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
Common.UpdateMachinePoolStringSetting();
MachineStuff.UpdateMachinePoolStringSetting();
}
catch (Exception e)
{
@@ -154,7 +154,7 @@ namespace MouseWithoutBorders
{
Logger.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information);
LastResumeSuspendTime = DateTime.UtcNow;
SwitchToMultipleMode(false, true);
MachineStuff.SwitchToMultipleMode(false, true);
}
}

View File

@@ -5,7 +5,6 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Principal;
@@ -39,23 +38,31 @@ namespace MouseWithoutBorders
}
else
{
// SuppressFlow fixes an issue on service mode, where WTSQueryUserToken runs successfully once and then fails
// on subsequent calls. The reason appears to be an unknown issue with reverting the impersonation,
// meaning that subsequent impersonation attempts run as the logged-on user and fail.
// This is a workaround.
using var asyncFlowControl = System.Threading.ExecutionContext.SuppressFlow();
uint dwSessionId;
IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero;
try
{
dwSessionId = (uint)Process.GetCurrentProcess().SessionId;
uint rv = NativeMethods.WTSQueryUserToken(dwSessionId, ref hUserToken);
Logger.LogDebug("WTSQueryUserToken returned " + rv.ToString(CultureInfo.CurrentCulture));
var lastError = rv == 0 ? Marshal.GetLastWin32Error() : 0;
Logger.LogDebug($"{nameof(NativeMethods.WTSQueryUserToken)} returned {rv.ToString(CultureInfo.CurrentCulture)}");
if (rv == 0)
{
Logger.LogDebug($"WTSQueryUserToken failed with: {Marshal.GetLastWin32Error()}.");
Logger.Log($"{nameof(NativeMethods.WTSQueryUserToken)} failed with: {lastError}.");
return false;
}
if (!NativeMethods.DuplicateToken(hUserToken, (int)NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref hUserTokenDup))
{
Logger.TelemetryLogTrace($"DuplicateToken Failed! {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
Logger.TelemetryLogTrace($"{nameof(NativeMethods.DuplicateToken)} Failed! {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
_ = NativeMethods.CloseHandle(hUserToken);
_ = NativeMethods.CloseHandle(hUserTokenDup);
return false;

View File

@@ -41,7 +41,7 @@ namespace MouseWithoutBorders
GetScreenConfig();
}
private static readonly List<Point> SensitivePoints = new();
internal static readonly List<Point> SensitivePoints = new();
private static bool MonitorEnumProc(IntPtr hMonitor, IntPtr hdcMonitor, ref NativeMethods.RECT lprcMonitor, IntPtr dwData)
{
@@ -162,21 +162,21 @@ namespace MouseWithoutBorders
// 1000 calls to EnumDisplayMonitors cost a dozen of milliseconds
#endif
Interlocked.Exchange(ref desktopBounds, newDesktopBounds);
Interlocked.Exchange(ref primaryScreenBounds, newPrimaryScreenBounds);
Interlocked.Exchange(ref MachineStuff.desktopBounds, newDesktopBounds);
Interlocked.Exchange(ref MachineStuff.primaryScreenBounds, newPrimaryScreenBounds);
Logger.Log(string.Format(
CultureInfo.CurrentCulture,
"logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}",
Common.RunOnLogonDesktop,
Common.PrimaryScreenBounds.Left,
Common.PrimaryScreenBounds.Top,
Common.PrimaryScreenBounds.Right,
Common.PrimaryScreenBounds.Bottom,
Common.DesktopBounds.Left,
Common.DesktopBounds.Top,
Common.DesktopBounds.Right,
Common.DesktopBounds.Bottom));
MachineStuff.PrimaryScreenBounds.Left,
MachineStuff.PrimaryScreenBounds.Top,
MachineStuff.PrimaryScreenBounds.Right,
MachineStuff.PrimaryScreenBounds.Bottom,
MachineStuff.DesktopBounds.Left,
MachineStuff.DesktopBounds.Top,
MachineStuff.DesktopBounds.Right,
MachineStuff.DesktopBounds.Bottom));
Logger.Log("==================== GetScreenConfig ended");
}

View File

@@ -112,7 +112,7 @@ namespace MouseWithoutBorders
internal const int JUST_GOT_BACK_FROM_SCREEN_SAVER = 9999;
internal const int NETWORK_STREAM_BUF_SIZE = 1024 * 1024;
private static readonly EventWaitHandle EvSwitch = new(false, EventResetMode.AutoReset);
internal static readonly EventWaitHandle EvSwitch = new(false, EventResetMode.AutoReset);
private static Point lastPos;
private static int switchCount;
private static long lastReconnectByHotKeyTime;
@@ -236,12 +236,12 @@ namespace MouseWithoutBorders
internal static ID DesMachineID
{
get => Common.desMachineID;
get => MachineStuff.desMachineID;
set
{
Common.desMachineID = value;
Common.DesMachineName = Common.NameFromID(Common.desMachineID);
MachineStuff.desMachineID = value;
MachineStuff.DesMachineName = MachineStuff.NameFromID(MachineStuff.desMachineID);
}
}
@@ -351,7 +351,7 @@ namespace MouseWithoutBorders
Logger.TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}, restarting the process.", SeverityLevel.Warning, true);
string desktop = Common.GetMyDesktop();
oneInstanceCheck?.Close();
MachineStuff.oneInstanceCheck?.Close();
_ = Process.Start(Application.ExecutablePath, desktop);
Logger.LogDebug($"Started on desktop {desktop}");
@@ -619,12 +619,12 @@ namespace MouseWithoutBorders
internal static void ProcessByeByeMessage(DATA package)
{
if (package.Src == desMachineID)
if (package.Src == MachineStuff.desMachineID)
{
SwitchToMachine(MachineName.Trim());
MachineStuff.SwitchToMachine(MachineName.Trim());
}
_ = RemoveDeadMachines(package.Src);
_ = MachineStuff.RemoveDeadMachines(package.Src);
}
internal static long GetTick() // ms
@@ -644,12 +644,12 @@ namespace MouseWithoutBorders
try
{
string fileName = GetMyStorageDir() + @"ScreenCaptureByMouseWithoutBorders.png";
int w = desktopBounds.Right - desktopBounds.Left;
int h = desktopBounds.Bottom - desktopBounds.Top;
int w = MachineStuff.desktopBounds.Right - MachineStuff.desktopBounds.Left;
int h = MachineStuff.desktopBounds.Bottom - MachineStuff.desktopBounds.Top;
Bitmap bm = new(w, h);
Graphics g = Graphics.FromImage(bm);
Size s = new(w, h);
g.CopyFromScreen(desktopBounds.Left, desktopBounds.Top, 0, 0, s);
g.CopyFromScreen(MachineStuff.desktopBounds.Left, MachineStuff.desktopBounds.Top, 0, 0, s);
bm.Save(fileName, ImageFormat.Png);
bm.Dispose();
return fileName;
@@ -665,7 +665,7 @@ namespace MouseWithoutBorders
{
Common.DoSomethingInUIThread(() =>
{
if (!MouseDown && Common.SendMessageToHelper(0x401, IntPtr.Zero, IntPtr.Zero) > 0)
if (!DragDrop.MouseDown && Common.SendMessageToHelper(0x401, IntPtr.Zero, IntPtr.Zero) > 0)
{
Common.MMSleep(0.2);
InputSimulation.SendKey(new KEYBDDATA() { wVk = (int)VK.SNAPSHOT });
@@ -675,10 +675,10 @@ namespace MouseWithoutBorders
_ = NativeMethods.MoveWindow(
(IntPtr)NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT),
Common.DesktopBounds.Left,
Common.DesktopBounds.Top,
Common.DesktopBounds.Right - Common.DesktopBounds.Left,
Common.DesktopBounds.Bottom - Common.DesktopBounds.Top,
MachineStuff.DesktopBounds.Left,
MachineStuff.DesktopBounds.Top,
MachineStuff.DesktopBounds.Right - MachineStuff.DesktopBounds.Left,
MachineStuff.DesktopBounds.Bottom - MachineStuff.DesktopBounds.Top,
false);
_ = Common.SendMessageToHelper(0x406, IntPtr.Zero, IntPtr.Zero, false);
@@ -729,7 +729,7 @@ namespace MouseWithoutBorders
}
else
{
ID id = Common.MachinePool.ResolveID(machine);
ID id = MachineStuff.MachinePool.ResolveID(machine);
if (id != ID.NONE)
{
SendPackage(id, PackageType.ClipboardCapture);
@@ -753,7 +753,7 @@ namespace MouseWithoutBorders
{
if (Setting.Values.FirstRun)
{
Common.Settings?.ShowTip(icon, tip, timeOutInMilliseconds);
MachineStuff.Settings?.ShowTip(icon, tip, timeOutInMilliseconds);
}
Common.MatrixForm?.ShowTip(icon, tip, timeOutInMilliseconds);
@@ -882,7 +882,7 @@ namespace MouseWithoutBorders
if (updateClientSockets)
{
UpdateClientSockets(nameof(IsConnectedTo));
MachineStuff.UpdateClientSockets(nameof(IsConnectedTo));
}
return false;
@@ -921,7 +921,7 @@ namespace MouseWithoutBorders
{
if (t != null && t.BackingSocket != null && (t.Status == SocketStatus.Connected || (t.Status == SocketStatus.Handshaking && includeHandShakingSockets)))
{
if (t.MachineId == (uint)data.Des || (data.Des == ID.ALL && t.MachineId != exceptDes && InMachineMatrix(t.MachineName)))
if (t.MachineId == (uint)data.Des || (data.Des == ID.ALL && t.MachineId != exceptDes && MachineStuff.InMachineMatrix(t.MachineName)))
{
try
{
@@ -952,20 +952,20 @@ namespace MouseWithoutBorders
{
Logger.LogDebug("********** No active connection found for the remote machine! **********" + data.Des.ToString());
if (data.Des == ID.NONE || RemoveDeadMachines(data.Des))
if (data.Des == ID.NONE || MachineStuff.RemoveDeadMachines(data.Des))
{
// SwitchToMachine(MachineName.Trim());
NewDesMachineID = DesMachineID = MachineID;
SwitchLocation.X = XY_BY_PIXEL + myLastX;
SwitchLocation.Y = XY_BY_PIXEL + myLastY;
SwitchLocation.ResetCount();
MachineStuff.NewDesMachineID = DesMachineID = MachineID;
MachineStuff.SwitchLocation.X = XY_BY_PIXEL + myLastX;
MachineStuff.SwitchLocation.Y = XY_BY_PIXEL + myLastY;
MachineStuff.SwitchLocation.ResetCount();
EvSwitch.Set();
}
}
if (updateClientSockets)
{
UpdateClientSockets("SkSend");
MachineStuff.UpdateClientSockets("SkSend");
}
}
catch (Exception e)
@@ -1203,7 +1203,7 @@ namespace MouseWithoutBorders
{
int machineCt = 0;
foreach (string m in Common.MachineMatrix)
foreach (string m in MachineStuff.MachineMatrix)
{
if (!string.IsNullOrEmpty(m.Trim()))
{
@@ -1211,15 +1211,15 @@ namespace MouseWithoutBorders
}
}
if (machineCt < 2 && Common.Settings != null && (Common.Settings.GetCurrentPage() is SetupPage1 || Common.Settings.GetCurrentPage() is SetupPage2b))
if (machineCt < 2 && MachineStuff.Settings != null && (MachineStuff.Settings.GetCurrentPage() is SetupPage1 || MachineStuff.Settings.GetCurrentPage() is SetupPage2b))
{
Common.MachineMatrix = new string[Common.MAX_MACHINE] { Common.MachineName.Trim(), desMachine, string.Empty, string.Empty };
Logger.LogDebug("UpdateSetupMachineMatrix: " + string.Join(",", Common.MachineMatrix));
MachineStuff.MachineMatrix = new string[MachineStuff.MAX_MACHINE] { Common.MachineName.Trim(), desMachine, string.Empty, string.Empty };
Logger.LogDebug("UpdateSetupMachineMatrix: " + string.Join(",", MachineStuff.MachineMatrix));
Common.DoSomethingInUIThread(
() =>
{
Common.Settings.SetControlPage(new SetupPage4());
MachineStuff.Settings.SetControlPage(new SetupPage4());
},
true);
}
@@ -1255,7 +1255,7 @@ namespace MouseWithoutBorders
SocketStuff.ClearBadIPs();
}
UpdateClientSockets("ReopenSockets");
MachineStuff.UpdateClientSockets("ReopenSockets");
}
},
true);
@@ -1473,17 +1473,17 @@ namespace MouseWithoutBorders
{
Logger.LogDebug("+++++ MoveMouseToCenter");
InputSimulation.MoveMouse(
Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2),
Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2));
MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2),
MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2));
}
internal static void HideMouseCursor(bool byHideMouseMessage)
{
Common.LastPos = new Point(
Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2),
Setting.Values.HideMouse ? 4 : Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2));
MachineStuff.PrimaryScreenBounds.Left + ((MachineStuff.PrimaryScreenBounds.Right - MachineStuff.PrimaryScreenBounds.Left) / 2),
Setting.Values.HideMouse ? 4 : MachineStuff.PrimaryScreenBounds.Top + ((MachineStuff.PrimaryScreenBounds.Bottom - MachineStuff.PrimaryScreenBounds.Top) / 2));
if ((desMachineID != MachineID && desMachineID != ID.ALL) || byHideMouseMessage)
if ((MachineStuff.desMachineID != MachineID && MachineStuff.desMachineID != ID.ALL) || byHideMouseMessage)
{
_ = NativeMethods.SetCursorPos(Common.LastPos.X, Common.LastPos.Y);
_ = NativeMethods.GetCursorPos(ref Common.lastPos);

View File

@@ -154,7 +154,7 @@ namespace MouseWithoutBorders
public void SendDragFile(string fileName)
{
Common.DragDropStep05Ex(fileName);
DragDrop.DragDropStep05Ex(fileName);
}
public void SendClipboardData(ByteArrayOrString data, bool isFilePath)

View File

@@ -222,10 +222,10 @@ namespace MouseWithoutBorders.Class
{
Common.RealInputEventCount++;
if (Common.NewDesMachineID == Common.MachineID || Common.NewDesMachineID == ID.ALL)
if (MachineStuff.NewDesMachineID == Common.MachineID || MachineStuff.NewDesMachineID == ID.ALL)
{
local = true;
if (Common.MainFormVisible && !Common.IsDropping)
if (Common.MainFormVisible && !DragDrop.IsDropping)
{
Common.MainFormDot();
}
@@ -265,19 +265,19 @@ namespace MouseWithoutBorders.Class
}
else
{
if (Common.SwitchLocation.Count > 0 && Common.NewDesMachineID != Common.MachineID && Common.NewDesMachineID != ID.ALL)
if (MachineStuff.SwitchLocation.Count > 0 && MachineStuff.NewDesMachineID != Common.MachineID && MachineStuff.NewDesMachineID != ID.ALL)
{
Common.SwitchLocation.Count--;
MachineStuff.SwitchLocation.Count--;
if (Common.SwitchLocation.X > Common.XY_BY_PIXEL - 100000 || Common.SwitchLocation.Y > Common.XY_BY_PIXEL - 100000)
if (MachineStuff.SwitchLocation.X > Common.XY_BY_PIXEL - 100000 || MachineStuff.SwitchLocation.Y > Common.XY_BY_PIXEL - 100000)
{
hookCallbackMouseData.X = Common.SwitchLocation.X - Common.XY_BY_PIXEL;
hookCallbackMouseData.Y = Common.SwitchLocation.Y - Common.XY_BY_PIXEL;
hookCallbackMouseData.X = MachineStuff.SwitchLocation.X - Common.XY_BY_PIXEL;
hookCallbackMouseData.Y = MachineStuff.SwitchLocation.Y - Common.XY_BY_PIXEL;
}
else
{
hookCallbackMouseData.X = (Common.SwitchLocation.X * Common.ScreenWidth / 65535) + Common.PrimaryScreenBounds.Left;
hookCallbackMouseData.Y = (Common.SwitchLocation.Y * Common.ScreenHeight / 65535) + Common.PrimaryScreenBounds.Top;
hookCallbackMouseData.X = (MachineStuff.SwitchLocation.X * Common.ScreenWidth / 65535) + MachineStuff.PrimaryScreenBounds.Left;
hookCallbackMouseData.Y = (MachineStuff.SwitchLocation.Y * Common.ScreenHeight / 65535) + MachineStuff.PrimaryScreenBounds.Top;
}
Common.HideMouseCursor(false);
@@ -290,22 +290,22 @@ namespace MouseWithoutBorders.Class
hookCallbackMouseData.X += dx;
hookCallbackMouseData.Y += dy;
if (hookCallbackMouseData.X < Common.PrimaryScreenBounds.Left)
if (hookCallbackMouseData.X < MachineStuff.PrimaryScreenBounds.Left)
{
hookCallbackMouseData.X = Common.PrimaryScreenBounds.Left - 1;
hookCallbackMouseData.X = MachineStuff.PrimaryScreenBounds.Left - 1;
}
else if (hookCallbackMouseData.X > Common.PrimaryScreenBounds.Right)
else if (hookCallbackMouseData.X > MachineStuff.PrimaryScreenBounds.Right)
{
hookCallbackMouseData.X = Common.PrimaryScreenBounds.Right + 1;
hookCallbackMouseData.X = MachineStuff.PrimaryScreenBounds.Right + 1;
}
if (hookCallbackMouseData.Y < Common.PrimaryScreenBounds.Top)
if (hookCallbackMouseData.Y < MachineStuff.PrimaryScreenBounds.Top)
{
hookCallbackMouseData.Y = Common.PrimaryScreenBounds.Top - 1;
hookCallbackMouseData.Y = MachineStuff.PrimaryScreenBounds.Top - 1;
}
else if (hookCallbackMouseData.Y > Common.PrimaryScreenBounds.Bottom)
else if (hookCallbackMouseData.Y > MachineStuff.PrimaryScreenBounds.Bottom)
{
hookCallbackMouseData.Y = Common.PrimaryScreenBounds.Bottom + 1;
hookCallbackMouseData.Y = MachineStuff.PrimaryScreenBounds.Bottom + 1;
}
dx += dx < 0 ? -Common.MOVE_MOUSE_RELATIVE : Common.MOVE_MOUSE_RELATIVE;
@@ -315,8 +315,8 @@ namespace MouseWithoutBorders.Class
MouseEvent(hookCallbackMouseData, dx, dy);
Common.DragDropStep01(wParam);
Common.DragDropStep09(wParam);
DragDrop.DragDropStep01(wParam);
DragDrop.DragDropStep09(wParam);
}
if (local)
@@ -432,7 +432,7 @@ namespace MouseWithoutBorders.Class
if (Common.DesMachineID != ID.ALL)
{
Common.SwitchToMachine(Common.MachineName.Trim());
MachineStuff.SwitchToMachine(Common.MachineName.Trim());
}
/*
@@ -518,7 +518,7 @@ namespace MouseWithoutBorders.Class
if (Common.HotkeyMatched(vkCode, winDown, CtrlDown, altDown, shiftDown, Setting.Values.HotKeySwitch2AllPC))
{
ResetLastSwitchKeys();
Common.SwitchToMultipleMode(Common.DesMachineID != ID.ALL, true);
MachineStuff.SwitchToMultipleMode(Common.DesMachineID != ID.ALL, true);
}
if (Common.HotkeyMatched(vkCode, winDown, CtrlDown, altDown, shiftDown, Setting.Values.HotKeyToggleEasyMouse))
@@ -543,7 +543,7 @@ namespace MouseWithoutBorders.Class
{
if (Common.GetTick() - lastHotKeyLockMachine < 500)
{
Common.SwitchToMultipleMode(true, true);
MachineStuff.SwitchToMultipleMode(true, true);
var codes = GetVkCodesList(Setting.Values.HotKeyLockMachine);
@@ -561,7 +561,7 @@ namespace MouseWithoutBorders.Class
KeyboardEvent(hookCallbackKeybdData);
}
Common.SwitchToMultipleMode(false, true);
MachineStuff.SwitchToMultipleMode(false, true);
_ = NativeMethods.LockWorkStation();
}
@@ -625,9 +625,9 @@ namespace MouseWithoutBorders.Class
private static bool Switch2(int index)
{
if (Common.MachineMatrix != null && Common.MachineMatrix.Length > index)
if (MachineStuff.MachineMatrix != null && MachineStuff.MachineMatrix.Length > index)
{
string mcName = Common.MachineMatrix[index].Trim();
string mcName = MachineStuff.MachineMatrix[index].Trim();
if (!string.IsNullOrEmpty(mcName))
{
// Common.DoSomethingInUIThread(delegate()
@@ -636,7 +636,7 @@ namespace MouseWithoutBorders.Class
}
// );
Common.SwitchToMachine(mcName);
MachineStuff.SwitchToMachine(mcName);
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{

View File

@@ -162,10 +162,10 @@ namespace MouseWithoutBorders.Class
uint rv = 0;
NativeMethods.INPUT mouse_input = default;
long w65535 = (Common.DesktopBounds.Right - Common.DesktopBounds.Left) * 65535 / Common.ScreenWidth;
long h65535 = (Common.DesktopBounds.Bottom - Common.DesktopBounds.Top) * 65535 / Common.ScreenHeight;
long l65535 = Common.DesktopBounds.Left * 65535 / Common.ScreenWidth;
long t65535 = Common.DesktopBounds.Top * 65535 / Common.ScreenHeight;
long w65535 = (MachineStuff.DesktopBounds.Right - MachineStuff.DesktopBounds.Left) * 65535 / Common.ScreenWidth;
long h65535 = (MachineStuff.DesktopBounds.Bottom - MachineStuff.DesktopBounds.Top) * 65535 / Common.ScreenHeight;
long l65535 = MachineStuff.DesktopBounds.Left * 65535 / Common.ScreenWidth;
long t65535 = MachineStuff.DesktopBounds.Top * 65535 / Common.ScreenHeight;
mouse_input.type = 0;
long dx = (md.X * w65535 / 65535) + l65535;
long dy = (md.Y * h65535 / 65535) + t65535;
@@ -221,7 +221,7 @@ namespace MouseWithoutBorders.Class
rv = SendInputEx(mouse_input);
});
if (Common.MainFormVisible && !Common.IsDropping)
if (Common.MainFormVisible && !DragDrop.IsDropping)
{
Common.MainFormDot();
}
@@ -233,10 +233,10 @@ namespace MouseWithoutBorders.Class
{
NativeMethods.INPUT mouse_input = default;
long w65535 = (Common.DesktopBounds.Right - Common.DesktopBounds.Left) * 65535 / Common.ScreenWidth;
long h65535 = (Common.DesktopBounds.Bottom - Common.DesktopBounds.Top) * 65535 / Common.ScreenHeight;
long l65535 = Common.DesktopBounds.Left * 65535 / Common.ScreenWidth;
long t65535 = Common.DesktopBounds.Top * 65535 / Common.ScreenHeight;
long w65535 = (MachineStuff.DesktopBounds.Right - MachineStuff.DesktopBounds.Left) * 65535 / Common.ScreenWidth;
long h65535 = (MachineStuff.DesktopBounds.Bottom - MachineStuff.DesktopBounds.Top) * 65535 / Common.ScreenHeight;
long l65535 = MachineStuff.DesktopBounds.Left * 65535 / Common.ScreenWidth;
long t65535 = MachineStuff.DesktopBounds.Top * 65535 / Common.ScreenHeight;
mouse_input.type = 0;
long dx = (x * w65535 / 65535) + l65535;
long dy = (y * h65535 / 65535) + t65535;

View File

@@ -12,6 +12,8 @@ using System.Diagnostics;
using System.Linq;
using System.Threading;
using MouseWithoutBorders.Core;
namespace MouseWithoutBorders.Class
{
/// <summary>
@@ -113,10 +115,10 @@ namespace MouseWithoutBorders.Class
{
Name = list[i].Name,
Id = list[i].Id,
Time = list[i].Time > Common.GetTick() - Common.HEARTBEAT_TIMEOUT + 10000 ? Common.GetTick() - Common.HEARTBEAT_TIMEOUT + 10000 : list[i].Time,
Time = list[i].Time > Common.GetTick() - MachineStuff.HEARTBEAT_TIMEOUT + 10000 ? Common.GetTick() - MachineStuff.HEARTBEAT_TIMEOUT + 10000 : list[i].Time,
};
foundAndTimedOut = list[i].Time < Common.GetTick() - Common.HEARTBEAT_TIMEOUT + 10000 - 5000;
foundAndTimedOut = list[i].Time < Common.GetTick() - MachineStuff.HEARTBEAT_TIMEOUT + 10000 - 5000;
}
}
@@ -157,7 +159,7 @@ namespace MouseWithoutBorders.Class
}
else if (list.Count >= 4)
{
throw new ArgumentException($"The number of machines exceeded the maximum allowed limit of {Common.MAX_MACHINE}. Actual count: {list.Count}.");
throw new ArgumentException($"The number of machines exceeded the maximum allowed limit of {MachineStuff.MAX_MACHINE}. Actual count: {list.Count}.");
}
_ = LearnMachine(name);
@@ -179,7 +181,7 @@ namespace MouseWithoutBorders.Class
}
else if (list.Count >= 4)
{
throw new ArgumentException($"The number of machines exceeded the maximum allowed limit of {Common.MAX_MACHINE}. Actual count: {list.Count}.");
throw new ArgumentException($"The number of machines exceeded the maximum allowed limit of {MachineStuff.MAX_MACHINE}. Actual count: {list.Count}.");
}
_ = LearnMachine(inf.Name);
@@ -212,13 +214,13 @@ namespace MouseWithoutBorders.Class
}
}
if (list.Count >= Common.MAX_MACHINE)
if (list.Count >= MachineStuff.MAX_MACHINE)
{
int slotFound = -1;
for (int i = 0; i < list.Count; i++)
{
if (!Common.InMachineMatrix(list[i].Name))
if (!MachineStuff.InMachineMatrix(list[i].Name))
{
slotFound = i;
break;
@@ -289,7 +291,7 @@ namespace MouseWithoutBorders.Class
List<MachineInf> machinePool = ListAllMachines();
string rv = string.Join(",", machinePool.Select(m => $"{m.Name}:{m.Id}"));
for (int j = machinePool.Count; j < Common.MAX_MACHINE; j++)
for (int j = machinePool.Count; j < MachineStuff.MAX_MACHINE; j++)
{
rv += ",:";
}
@@ -301,7 +303,7 @@ namespace MouseWithoutBorders.Class
/// <param name="clockSkewInMS_forTesting">When doing unit tests it's nice to be able to fudge with the clock time, adding milliseconds, instead of sleeping.</param>
internal static bool IsAlive(MachineInf inf, int clockSkewInMS_forTesting = 0)
{
return inf.Id != ID.NONE && (Common.GetTick() + clockSkewInMS_forTesting - inf.Time <= Common.HEARTBEAT_TIMEOUT || Common.IsConnectedTo(inf.Id));
return inf.Id != ID.NONE && (Common.GetTick() + clockSkewInMS_forTesting - inf.Time <= MachineStuff.HEARTBEAT_TIMEOUT || Common.IsConnectedTo(inf.Id));
}
private static bool NamesAreEqual(string name1, string name2)
@@ -326,7 +328,7 @@ namespace MouseWithoutBorders.Class
continue;
}
if ((firstLoaded && !Common.InMachineMatrix(list[i].Name)) || (!firstLoaded && (!Common.InMachineMatrix(list[i].Name) || !IsAlive(list[i]))))
if ((firstLoaded && !MachineStuff.InMachineMatrix(list[i].Name)) || (!firstLoaded && (!MachineStuff.InMachineMatrix(list[i].Name) || !IsAlive(list[i]))))
{
list[i] =
new MachineInf

View File

@@ -4,6 +4,8 @@
using System;
using MouseWithoutBorders.Core;
namespace MouseWithoutBorders.Class
{
internal static class MachinePoolHelpers
@@ -20,20 +22,20 @@ namespace MouseWithoutBorders.Class
string[] st = s.Split(Comma);
if (st.Length < Common.MAX_MACHINE)
if (st.Length < MachineStuff.MAX_MACHINE)
{
throw new ArgumentException("Not enough elements in encoded MachinePool string");
}
MachineInf[] rv = new MachineInf[Common.MAX_MACHINE];
for (int i = 0; i < Common.MAX_MACHINE; i++)
MachineInf[] rv = new MachineInf[MachineStuff.MAX_MACHINE];
for (int i = 0; i < MachineStuff.MAX_MACHINE; i++)
{
string[] mc = st[i].Split(Colon);
if (mc.Length == 2)
{
rv[i].Name = mc[0];
rv[i].Id = uint.TryParse(mc[1], out uint ip) ? (ID)ip : ID.NONE;
rv[i].Time = rv[i].Id == ID.NONE ? Common.GetTick() - Common.HEARTBEAT_TIMEOUT : Common.GetTick();
rv[i].Time = rv[i].Id == ID.NONE ? Common.GetTick() - MachineStuff.HEARTBEAT_TIMEOUT : Common.GetTick();
}
}

View File

@@ -75,6 +75,41 @@ namespace MouseWithoutBorders.Class
[DllImport("kernel32.dll")]
internal static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
internal static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTSInfoClass infoClass, out IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
internal static extern void WTSFreeMemory(IntPtr pointer);
internal enum WTSInfoClass
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType,
WTSIdleTime,
WTSLogonTime,
WTSIncomingBytes,
WTSOutgoingBytes,
WTSIncomingFrames,
WTSOutgoingFrames,
WTSClientInfo,
WTSSessionInfo,
}
#endif
[DllImport("user32.dll", SetLastError = true)]
@@ -812,7 +847,7 @@ namespace MouseWithoutBorders.Class
// [DllImport("kernel32.dll", SetLastError = true)]
// internal static extern IntPtr CreateToolhelp32Snapshot(UInt32 dwFlags, UInt32 th32ProcessID);
[DllImport("Wtsapi32.dll")]
[DllImport("Wtsapi32.dll", SetLastError = true)]
internal static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "1", Justification = "Dotnet port with style preservation")]

View File

@@ -31,6 +31,7 @@ using System.Xml.Linq;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Telemetry;
using MouseWithoutBorders.Core;
using Newtonsoft.Json;
using StreamJsonRpc;
@@ -135,7 +136,7 @@ namespace MouseWithoutBorders.Class
if (firstArg != string.Empty)
{
if (Common.CheckSecondInstance(Common.RunWithNoAdminRight))
if (MachineStuff.CheckSecondInstance(Common.RunWithNoAdminRight))
{
Logger.Log("*** Second instance, exiting...");
return;
@@ -165,7 +166,7 @@ namespace MouseWithoutBorders.Class
}
else
{
if (Common.CheckSecondInstance(true))
if (MachineStuff.CheckSecondInstance(true))
{
Logger.Log("*** Second instance, exiting...");
return;
@@ -301,20 +302,20 @@ namespace MouseWithoutBorders.Class
{
Setting.Values.PauseInstantSaving = true;
Common.ClearComputerMatrix();
MachineStuff.ClearComputerMatrix();
Setting.Values.MyKey = securityKey;
Common.MyKey = securityKey;
Common.MagicNumber = Common.Get24BitHash(Common.MyKey);
Common.MachineMatrix = new string[Common.MAX_MACHINE] { pcName.Trim().ToUpper(CultureInfo.CurrentCulture), Common.MachineName.Trim(), string.Empty, string.Empty };
MachineStuff.MachineMatrix = new string[MachineStuff.MAX_MACHINE] { pcName.Trim().ToUpper(CultureInfo.CurrentCulture), Common.MachineName.Trim(), string.Empty, string.Empty };
string[] machines = Common.MachineMatrix;
Common.MachinePool.Initialize(machines);
Common.UpdateMachinePoolStringSetting();
string[] machines = MachineStuff.MachineMatrix;
MachineStuff.MachinePool.Initialize(machines);
MachineStuff.UpdateMachinePoolStringSetting();
SocketStuff.InvalidKeyFound = false;
Common.ReopenSocketDueToReadError = true;
Common.ReopenSockets(true);
Common.SendMachineMatrix();
MachineStuff.SendMachineMatrix();
Setting.Values.PauseInstantSaving = false;
Setting.Values.SaveSettings();
@@ -325,7 +326,7 @@ namespace MouseWithoutBorders.Class
Setting.Values.PauseInstantSaving = true;
Setting.Values.EasyMouse = (int)EasyMouseOption.Enable;
Common.ClearComputerMatrix();
MachineStuff.ClearComputerMatrix();
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Common.GeneratedKey = true;
@@ -352,7 +353,7 @@ namespace MouseWithoutBorders.Class
Common.MMSleep(0.2);
}
Common.SendMachineMatrix();
MachineStuff.SendMachineMatrix();
}
public void Shutdown()

View File

@@ -102,7 +102,7 @@ namespace MouseWithoutBorders.Class
if (!Enumerable.SequenceEqual(last_properties.MachineMatrixString, _settings.Properties.MachineMatrixString))
{
_properties.MachineMatrixString = _settings.Properties.MachineMatrixString;
Common.MachineMatrix = null; // Forces read next time it's needed.
MachineStuff.MachineMatrix = null; // Forces read next time it's needed.
shouldSendMachineMatrix = true;
}
@@ -123,7 +123,7 @@ namespace MouseWithoutBorders.Class
if (shouldSendMachineMatrix)
{
Common.SendMachineMatrix();
MachineStuff.SendMachineMatrix();
shouldSaveNewSettingsValues = true;
}

View File

@@ -188,7 +188,7 @@ namespace MouseWithoutBorders.Class
{
if (Common.DesMachineID != Common.MachineID)
{
Common.SwitchToMultipleMode(false, true);
MachineStuff.SwitchToMultipleMode(false, true);
}
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
@@ -253,7 +253,7 @@ namespace MouseWithoutBorders.Class
{
if (Setting.Values.LastX == Common.JUST_GOT_BACK_FROM_SCREEN_SAVER)
{
Common.NewDesMachineID = Common.DesMachineID = Common.MachineID;
MachineStuff.NewDesMachineID = Common.DesMachineID = Common.MachineID;
}
else
{
@@ -263,11 +263,11 @@ namespace MouseWithoutBorders.Class
if (Common.RunOnLogonDesktop && Setting.Values.DesMachineID == (uint)ID.ALL)
{
Common.SwitchToMultipleMode(true, false);
MachineStuff.SwitchToMultipleMode(true, false);
}
else
{
Common.SwitchToMultipleMode(false, false);
MachineStuff.SwitchToMultipleMode(false, false);
}
}
}
@@ -804,11 +804,11 @@ namespace MouseWithoutBorders.Class
try
{
if (Common.MachineMatrix != null)
if (MachineStuff.MachineMatrix != null)
{
Logger.LogDebug("MachineMatrix = " + string.Join(", ", Common.MachineMatrix));
Logger.LogDebug("MachineMatrix = " + string.Join(", ", MachineStuff.MachineMatrix));
foreach (string st in Common.MachineMatrix)
foreach (string st in MachineStuff.MachineMatrix)
{
string machineName = st.Trim();
if (!string.IsNullOrEmpty(machineName) &&
@@ -961,7 +961,7 @@ namespace MouseWithoutBorders.Class
UpdateTcpSockets(dummyTcp, SocketStatus.NA);
if (!Common.InMachineMatrix(machineName))
if (!MachineStuff.InMachineMatrix(machineName))
{
// While Resolving from name to IP, user may have changed the machine name and clicked on Apply.
return;
@@ -1449,19 +1449,19 @@ namespace MouseWithoutBorders.Class
Common.SendHeartBeat(initial: true);
if (Common.MachinePool.TryFindMachineByName(remoteMachine, out MachineInf machineInfo))
if (MachineStuff.MachinePool.TryFindMachineByName(remoteMachine, out MachineInf machineInfo))
{
Logger.LogDebug("Machine updated: " + remoteMachine + "/" + remoteID.ToString());
if (machineInfo.Name.Equals(Common.DesMachineName, StringComparison.OrdinalIgnoreCase))
if (machineInfo.Name.Equals(MachineStuff.DesMachineName, StringComparison.OrdinalIgnoreCase))
{
Logger.LogDebug("Des ID updated: " + Common.DesMachineID.ToString() +
"/" + remoteID.ToString());
Common.NewDesMachineID = Common.DesMachineID = remoteID;
MachineStuff.NewDesMachineID = Common.DesMachineID = remoteID;
}
_ = Common.MachinePool.TryUpdateMachineID(remoteMachine, remoteID, true);
Common.UpdateMachinePoolStringSetting();
_ = MachineStuff.MachinePool.TryUpdateMachineID(remoteMachine, remoteID, true);
MachineStuff.UpdateMachinePoolStringSetting();
}
else
{
@@ -1475,7 +1475,7 @@ namespace MouseWithoutBorders.Class
if (!isClient)
{
Common.UpdateClientSockets("MainTCPRoutine");
MachineStuff.UpdateClientSockets("MainTCPRoutine");
}
}
else
@@ -1559,7 +1559,7 @@ namespace MouseWithoutBorders.Class
if (remoteID != ID.NONE)
{
_ = Common.RemoveDeadMachines(remoteID);
_ = MachineStuff.RemoveDeadMachines(remoteID);
}
}
@@ -1635,9 +1635,9 @@ namespace MouseWithoutBorders.Class
{
string remoteEndPoint = s.RemoteEndPoint.ToString();
Logger.LogDebug("SendClipboardData: Request accepted: " + s.LocalEndPoint.ToString() + "/" + remoteEndPoint);
Common.IsDropping = false;
Common.IsDragging = false;
Common.DragMachine = (ID)1;
DragDrop.IsDropping = false;
DragDrop.IsDragging = false;
DragDrop.DragMachine = (ID)1;
bool clientPushData = true;
ClipboardPostAction postAction = ClipboardPostAction.Other;
@@ -2064,7 +2064,7 @@ namespace MouseWithoutBorders.Class
if (string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':'))
{
tcp.MachineName = Common.NameFromID((ID)tcp.MachineId);
tcp.MachineName = MachineStuff.NameFromID((ID)tcp.MachineId);
}
if (string.IsNullOrEmpty(tcp.MachineName) || tcp.MachineName.Contains('.') || tcp.MachineName.Contains(':'))

View File

@@ -0,0 +1,404 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.PowerToys.Telemetry;
using MouseWithoutBorders.Class;
// <summary>
// Drag/Drop implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
/* Common.DragDrop.cs
* Drag&Drop is one complicated implementation of the tool with some tricks.
*
* SEQUENCE OF EVENTS:
* DragDropStep01: MachineX: Remember mouse down state since it could be a start of a dragging
* DragDropStep02: MachineY: Send an message to the MachineX to ask it to check if it is
* doing drag/drop
* DragDropStep03: MachineX: Got explorerDragDrop, send WM_CHECK_EXPLORER_DRAG_DROP to its mainForm
* DragDropStep04: MachineX: Show Mouse Without Borders Helper form at mouse cursor to get DragEnter event.
* DragDropStepXX: MachineX: Mouse Without Borders Helper: Called by DragEnter, check if dragging a single file,
* remember the file (set as its window caption)
* DragDropStep05: MachineX: Get the file name from Mouse Without Borders Helper, hide Mouse Without Borders Helper window
* DragDropStep06: MachineX: Broadcast a message saying that it has some drag file.
* DragDropStep08: MachineY: Got ClipboardDragDrop, isDropping set, get the MachineX name from the package.
* DragDropStep09: MachineY: Since isDropping is true, show up the drop form (looks like an icon).
* DragDropStep10: MachineY: MouseUp, set isDropping to false, hide the drop "icon" and get data.
* DragDropStep11: MachineX: Mouse move back without drop event, cancelling drag/dop
* SendClipboardBeatDragDropEnd
* DragDropStep12: MachineY: Hide the drop "icon" when received ClipboardDragDropEnd.
*
* FROM VERSION 1.6.3: Drag/Drop is temporary removed, Drop action cannot be done from a lower integrity app to a higher one.
* We have to run a helper process...
* http://forums.microsoft.com/MSDN/ShowPost.aspx?PageIndex=1&SiteID=1&PageID=1&PostID=736086
*
* 2008.10.28: Trying to restore the Drag/Drop feature by adding the drag/drop helper process. Coming in version
* 1.6.5
* */
internal static class DragDrop
{
private static bool isDragging;
internal static bool IsDragging
{
get => DragDrop.isDragging;
set => DragDrop.isDragging = value;
}
internal static void DragDropStep01(int wParam)
{
if (!Setting.Values.TransferFile)
{
return;
}
if (wParam == Common.WM_LBUTTONDOWN)
{
MouseDown = true;
DragMachine = MachineStuff.desMachineID;
MachineStuff.dropMachineID = ID.NONE;
Logger.LogDebug("DragDropStep01: MouseDown");
}
else if (wParam == Common.WM_LBUTTONUP)
{
MouseDown = false;
Logger.LogDebug("DragDropStep01: MouseUp");
}
if (wParam == Common.WM_RBUTTONUP && IsDropping)
{
IsDropping = false;
Common.LastIDWithClipboardData = ID.NONE;
}
}
internal static void DragDropStep02()
{
if (MachineStuff.desMachineID == Common.MachineID)
{
Logger.LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent to myself");
Common.DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(Common.MainForm.Handle, NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
else
{
SendCheckExplorerDragDrop();
Logger.LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent");
}
}
internal static void DragDropStep03(DATA package)
{
if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop)
{
return;
}
if (package.Des == Common.MachineID || package.Des == ID.ALL)
{
Logger.LogDebug("DragDropStep03: ExplorerDragDrop Received.");
MachineStuff.dropMachineID = package.Src; // Drop machine is the machine that sent ExplorerDragDrop
if (MouseDown || IsDropping)
{
Logger.LogDebug("DragDropStep03: Mouse is down, check if dragging...sending WM_CHECK_EXPLORER_DRAG_DROP to myself...");
Common.DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(Common.MainForm.Handle, NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
}
}
private static int dragDropStep05ExCalledByIpc;
internal static void DragDropStep04()
{
if (!IsDropping)
{
IntPtr h = (IntPtr)NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT);
if (h.ToInt32() > 0)
{
_ = Interlocked.Exchange(ref dragDropStep05ExCalledByIpc, 0);
Common.MainForm.Hide();
Common.MainFormVisible = false;
Point p = default;
// NativeMethods.SetWindowText(h, "");
_ = NativeMethods.SetWindowPos(h, NativeMethods.HWND_TOPMOST, 0, 0, 0, 0, NativeMethods.SWP_SHOWWINDOW);
for (int i = -10; i < 10; i++)
{
if (dragDropStep05ExCalledByIpc > 0)
{
Logger.LogDebug("DragDropStep04: DragDropStep05ExCalledByIpc.");
break;
}
_ = NativeMethods.GetCursorPos(ref p);
Logger.LogDebug("DragDropStep04: Moving Mouse Without Borders Helper to (" + p.X.ToString(CultureInfo.CurrentCulture) + ", " + p.Y.ToString(CultureInfo.CurrentCulture) + ")");
_ = NativeMethods.SetWindowPos(h, NativeMethods.HWND_TOPMOST, p.X - 100 + i, p.Y - 100 + i, 200, 200, 0);
_ = NativeMethods.SendMessage(h, 0x000F, IntPtr.Zero, IntPtr.Zero); // WM_PAINT
Thread.Sleep(20);
Application.DoEvents();
// if (GetText(h).Length > 1) break;
}
}
else
{
Logger.LogDebug("DragDropStep04: Mouse without Borders Helper not found!");
}
}
else
{
Logger.LogDebug("DragDropStep04: IsDropping == true, skip checking");
}
Logger.LogDebug("DragDropStep04: Got WM_CHECK_EXPLORER_DRAG_DROP, done with processing jump to DragDropStep05...");
}
internal static void DragDropStep05Ex(string dragFileName)
{
Logger.LogDebug("DragDropStep05 called.");
_ = Interlocked.Exchange(ref dragDropStep05ExCalledByIpc, 1);
if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop)
{
return;
}
if (!IsDropping)
{
_ = Common.ImpersonateLoggedOnUserAndDoSomething(() =>
{
if (!string.IsNullOrEmpty(dragFileName) && (File.Exists(dragFileName) || Directory.Exists(dragFileName)))
{
Common.LastDragDropFile = dragFileName;
/*
* possibleDropMachineID is used as desID sent in DragDropStep06();
* */
if (MachineStuff.dropMachineID == ID.NONE)
{
MachineStuff.dropMachineID = MachineStuff.newDesMachineID;
}
DragDropStep06();
Logger.LogDebug("DragDropStep05: File dragging: " + dragFileName);
_ = NativeMethods.PostMessage(Common.MainForm.Handle, NativeMethods.WM_HIDE_DD_HELPER, (IntPtr)1, (IntPtr)0);
}
else
{
Logger.LogDebug("DragDropStep05: File not found: [" + dragFileName + "]");
_ = NativeMethods.PostMessage(Common.MainForm.Handle, NativeMethods.WM_HIDE_DD_HELPER, (IntPtr)0, (IntPtr)0);
}
Logger.LogDebug("DragDropStep05: WM_HIDE_DDHelper sent");
});
}
else
{
Logger.LogDebug("DragDropStep05: IsDropping == true, change drop machine...");
IsDropping = false;
Common.MainFormVisible = true; // WM_HIDE_DRAG_DROP
SendDropBegin(); // To dropMachineID set in DragDropStep03
}
MouseDown = false;
}
private static void DragDropStep06()
{
IsDragging = true;
Logger.LogDebug("DragDropStep06: SendClipboardBeatDragDrop");
SendClipboardBeatDragDrop();
SendDropBegin();
}
internal static void DragDropStep08(DATA package)
{
Receiver.GetNameOfMachineWithClipboardData(package);
Logger.LogDebug("DragDropStep08: ClipboardDragDrop Received. machine with drag file was set");
}
internal static void DragDropStep08_2(DATA package)
{
if (package.Des == Common.MachineID && !Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
IsDropping = true;
MachineStuff.dropMachineID = Common.MachineID;
Logger.LogDebug("DragDropStep08_2: ClipboardDragDropOperation Received. IsDropping set");
}
}
internal static void DragDropStep09(int wParam)
{
if (wParam == Common.WM_MOUSEMOVE && IsDropping)
{
// Show/Move form
Common.DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(Common.MainForm.Handle, NativeMethods.WM_SHOW_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
else if (wParam == Common.WM_LBUTTONUP && (IsDropping || IsDragging))
{
if (IsDropping)
{
// Hide form, get data
DragDropStep10();
}
else
{
IsDragging = false;
Common.LastIDWithClipboardData = ID.NONE;
}
}
}
private static void DragDropStep10()
{
Logger.LogDebug("DragDropStep10: Hide the form and get data...");
IsDropping = false;
IsDragging = false;
Common.LastIDWithClipboardData = ID.NONE;
Common.DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(Common.MainForm.Handle, NativeMethods.WM_HIDE_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersDragAndDropEvent());
Common.GetRemoteClipboard("desktop");
}
internal static void DragDropStep11()
{
Logger.LogDebug("DragDropStep11: Mouse drag coming back, canceling drag/drop");
SendClipboardBeatDragDropEnd();
IsDropping = false;
IsDragging = false;
DragMachine = (ID)1;
Common.LastIDWithClipboardData = ID.NONE;
Common.LastDragDropFile = null;
MouseDown = false;
}
internal static void DragDropStep12()
{
Logger.LogDebug("DragDropStep12: ClipboardDragDropEnd received");
IsDropping = false;
Common.LastIDWithClipboardData = ID.NONE;
Common.DoSomethingInUIThread(() =>
{
_ = NativeMethods.PostMessage(Common.MainForm.Handle, NativeMethods.WM_HIDE_DRAG_DROP, (IntPtr)0, (IntPtr)0);
});
}
private static void SendCheckExplorerDragDrop()
{
DATA package = new();
package.Type = PackageType.ExplorerDragDrop;
/*
* package.src = newDesMachineID:
* sent from the master machine but the src must be the
* new des machine since the previous des machine will get this and set
* to possibleDropMachineID in DragDropStep3()
* */
package.Src = MachineStuff.newDesMachineID;
package.Des = MachineStuff.desMachineID;
package.MachineName = Common.MachineName;
Common.SkSend(package, null, false);
}
internal static void ChangeDropMachine()
{
// desMachineID = current drop machine
// newDesMachineID = new drop machine
// 1. Cancelling dropping in current drop machine
if (MachineStuff.dropMachineID == Common.MachineID)
{
// Drag/Drop coming through me
IsDropping = false;
}
else
{
// Drag/Drop coming back
SendClipboardBeatDragDropEnd();
}
// 2. SendClipboardBeatDragDrop to new drop machine
// new drop machine is not me
if (MachineStuff.newDesMachineID != Common.MachineID)
{
MachineStuff.dropMachineID = MachineStuff.newDesMachineID;
SendDropBegin();
}
// New drop machine is me
else
{
IsDropping = true;
}
}
private static void SendClipboardBeatDragDrop()
{
Common.SendPackage(ID.ALL, PackageType.ClipboardDragDrop);
}
private static void SendDropBegin()
{
Logger.LogDebug("SendDropBegin...");
Common.SendPackage(MachineStuff.dropMachineID, PackageType.ClipboardDragDropOperation);
}
private static void SendClipboardBeatDragDropEnd()
{
if (MachineStuff.desMachineID != Common.MachineID)
{
Common.SendPackage(MachineStuff.desMachineID, PackageType.ClipboardDragDropEnd);
}
}
private static bool isDropping;
private static ID dragMachine;
internal static ID DragMachine
{
get => DragDrop.dragMachine;
set => DragDrop.dragMachine = value;
}
internal static bool IsDropping
{
get => DragDrop.isDropping;
set => DragDrop.isDropping = value;
}
internal static bool MouseDown { get; set; }
}

View File

@@ -199,8 +199,11 @@ internal static class Logger
_ = Logger.PrivateDump(sb, AllLogs, "[Program logs]\r\n===============\r\n", 0, level, false);
_ = Logger.PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, level, false);
sb.AppendLine("[Logger]\r\n===============");
Logger.DumpType(sb, typeof(Logger), 0, level);
sb.AppendLine("[DragDrop]\r\n===============");
Logger.DumpType(sb, typeof(DragDrop), 0, level);
sb.AppendLine("[MachineStuff]\r\n===============");
Logger.DumpType(sb, typeof(MachineStuff), 0, level);
sb.AppendLine("[Receiver]\r\n===============");
Logger.DumpType(sb, typeof(Receiver), 0, level);

View File

@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <summary>
// Machine setup/switching implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal struct MachineInf
{
internal string Name;
internal ID Id;
internal long Time;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <summary>
// Machine setup/switching implementation.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal sealed class MyRectangle
{
internal int Left;
internal int Top;
internal int Right;
internal int Bottom;
}

View File

@@ -120,16 +120,16 @@ internal static class Receiver
if (package.Des == Common.MachineID || package.Des == ID.ALL)
{
if (Common.desMachineID != Common.MachineID)
if (MachineStuff.desMachineID != Common.MachineID)
{
Common.NewDesMachineID = Common.DesMachineID = Common.MachineID;
MachineStuff.NewDesMachineID = Common.DesMachineID = Common.MachineID;
}
// NOTE(@yuyoyuppe): disabled to drop elevation requirement
bool nonElevated = Common.RunWithNoAdminRight && false;
if (nonElevated && Setting.Values.OneWayControlMode && package.Md.dwFlags != Common.WM_MOUSEMOVE)
{
if (!Common.IsDropping)
if (!DragDrop.IsDropping)
{
if (package.Md.dwFlags is Common.WM_LBUTTONDOWN or Common.WM_RBUTTONDOWN)
{
@@ -138,7 +138,7 @@ internal static class Receiver
}
else if (package.Md.dwFlags is Common.WM_LBUTTONUP or Common.WM_RBUTTONUP)
{
Common.IsDropping = false;
DragDrop.IsDropping = false;
}
return;
@@ -153,7 +153,7 @@ internal static class Receiver
package.Md.Y < 0 ? package.Md.Y + Common.MOVE_MOUSE_RELATIVE : package.Md.Y - Common.MOVE_MOUSE_RELATIVE);
_ = NativeMethods.GetCursorPos(ref lastXY);
Point p = Common.MoveToMyNeighbourIfNeeded(lastXY.X, lastXY.Y, Common.MachineID);
Point p = MachineStuff.MoveToMyNeighbourIfNeeded(lastXY.X, lastXY.Y, Common.MachineID);
if (!p.IsEmpty)
{
@@ -162,11 +162,11 @@ internal static class Receiver
Logger.LogDebug(string.Format(
CultureInfo.CurrentCulture,
"***** Controlled Machine: newDesMachineIdEx set = [{0}]. Mouse is now at ({1},{2})",
Common.newDesMachineIdEx,
MachineStuff.newDesMachineIdEx,
lastXY.X,
lastXY.Y));
Common.SendNextMachine(package.Src, Common.newDesMachineIdEx, p);
Common.SendNextMachine(package.Src, MachineStuff.newDesMachineIdEx, p);
}
}
else
@@ -188,8 +188,8 @@ internal static class Receiver
CustomCursor.ShowFakeMouseCursor(Common.LastX, Common.LastY);
}
Common.DragDropStep01(package.Md.dwFlags);
Common.DragDropStep09(package.Md.dwFlags);
DragDrop.DragDropStep01(package.Md.dwFlags);
DragDrop.DragDropStep09(package.Md.dwFlags);
break;
case PackageType.NextMachine:
@@ -204,7 +204,7 @@ internal static class Receiver
case PackageType.ExplorerDragDrop:
Common.PackageReceived.ExplorerDragDrop++;
Common.DragDropStep03(package);
DragDrop.DragDropStep03(package);
break;
case PackageType.Heartbeat:
@@ -219,12 +219,12 @@ internal static class Receiver
Common.SendPackage(ID.ALL, PackageType.Heartbeat_ex_l2);
}
string desMachine = Common.AddToMachinePool(package);
string desMachine = MachineStuff.AddToMachinePool(package);
if (Setting.Values.FirstRun && !string.IsNullOrEmpty(desMachine))
{
Common.UpdateSetupMachineMatrix(desMachine);
Common.UpdateClientSockets("UpdateSetupMachineMatrix");
MachineStuff.UpdateClientSockets("UpdateSetupMachineMatrix");
}
break;
@@ -244,14 +244,14 @@ internal static class Receiver
case PackageType.Awake:
Common.PackageReceived.Heartbeat++;
_ = Common.AddToMachinePool(package);
_ = MachineStuff.AddToMachinePool(package);
Common.HumanBeingDetected();
break;
case PackageType.Hello:
Common.PackageReceived.Hello++;
Common.SendHeartBeat();
string newMachine = Common.AddToMachinePool(package);
string newMachine = MachineStuff.AddToMachinePool(package);
if (Setting.Values.MachineMatrixString == null)
{
string tip = newMachine + " saying Hello!";
@@ -345,17 +345,17 @@ internal static class Receiver
case PackageType.ClipboardDragDrop:
Common.PackageReceived.ClipboardDragDrop++;
Common.DragDropStep08(package);
DragDrop.DragDropStep08(package);
break;
case PackageType.ClipboardDragDropOperation:
Common.PackageReceived.ClipboardDragDrop++;
Common.DragDropStep08_2(package);
DragDrop.DragDropStep08_2(package);
break;
case PackageType.ClipboardDragDropEnd:
Common.PackageReceived.ClipboardDragDropEnd++;
Common.DragDropStep12();
DragDrop.DragDropStep12();
break;
case PackageType.ClipboardText:
@@ -391,7 +391,7 @@ internal static class Receiver
if ((package.Type & PackageType.Matrix) == PackageType.Matrix)
{
Common.PackageReceived.Matrix++;
Common.UpdateMachineMatrix(package);
MachineStuff.UpdateMachineMatrix(package);
break;
}
else
@@ -406,7 +406,7 @@ internal static class Receiver
internal static void GetNameOfMachineWithClipboardData(DATA package)
{
Common.LastIDWithClipboardData = package.Src;
List<MachineInf> matchingMachines = Common.MachinePool.TryFindMachineByID(Common.LastIDWithClipboardData);
List<MachineInf> matchingMachines = MachineStuff.MachinePool.TryFindMachineByID(Common.LastIDWithClipboardData);
if (matchingMachines.Count >= 1)
{
Common.LastMachineWithClipboardData = matchingMachines[0].Name.Trim();

View File

@@ -39,7 +39,7 @@ namespace MouseWithoutBorders
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
Common.Settings = null;
MachineStuff.Settings = null;
if (_currentPage != null)
{

View File

@@ -6,6 +6,7 @@ using System;
using System.Windows.Forms;
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Core;
using MouseWithoutBorders.Form.Settings;
namespace MouseWithoutBorders
@@ -59,8 +60,8 @@ namespace MouseWithoutBorders
MessageBoxDefaultButton.Button2) == DialogResult.Yes)
{
Setting.Values.FirstRun = false;
Common.CloseSetupForm();
Common.ShowMachineMatrix();
MachineStuff.CloseSetupForm();
MachineStuff.ShowMachineMatrix();
}
}
}

View File

@@ -4,6 +4,8 @@
using System;
using MouseWithoutBorders.Core;
namespace MouseWithoutBorders
{
public partial class SetupPage1 : SettingsFormPage
@@ -12,7 +14,7 @@ namespace MouseWithoutBorders
{
InitializeComponent();
Common.ClearComputerMatrix();
MachineStuff.ClearComputerMatrix();
}
private void NoButtonClick(object sender, EventArgs e)

View File

@@ -6,6 +6,7 @@ using System.Globalization;
using System.Text.RegularExpressions;
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Core;
namespace MouseWithoutBorders
{
@@ -92,12 +93,12 @@ namespace MouseWithoutBorders
SecurityCode = Common.MyKey;
}
Common.MachineMatrix = new string[Common.MAX_MACHINE] { ComputerNameField.Text.Trim().ToUpper(CultureInfo.CurrentCulture), Common.MachineName.Trim(), string.Empty, string.Empty };
MachineStuff.MachineMatrix = new string[MachineStuff.MAX_MACHINE] { ComputerNameField.Text.Trim().ToUpper(CultureInfo.CurrentCulture), Common.MachineName.Trim(), string.Empty, string.Empty };
string[] machines = Common.MachineMatrix;
Common.MachinePool.Initialize(machines);
string[] machines = MachineStuff.MachineMatrix;
MachineStuff.MachinePool.Initialize(machines);
Common.UpdateMachinePoolStringSetting();
MachineStuff.UpdateMachinePoolStringSetting();
SendNextPage(new SetupPage3a { ReturnToSettings = !Setting.Values.FirstRun });
}

View File

@@ -70,7 +70,7 @@ namespace MouseWithoutBorders
ShowStatus($"Connecting...");
Common.SwitchToMultipleMode(false, false);
MachineStuff.SwitchToMultipleMode(false, false);
Common.ReopenSockets(true);
int timeOut = 0;

View File

@@ -4,6 +4,8 @@
using System;
using MouseWithoutBorders.Core;
namespace MouseWithoutBorders
{
public partial class SetupPage5 : SettingsFormPage
@@ -16,8 +18,8 @@ namespace MouseWithoutBorders
private void DoneButtonClick(object sender, EventArgs e)
{
// SendNextPage(new SettingsPage1());
Common.CloseSetupForm();
Common.ShowMachineMatrix();
MachineStuff.CloseSetupForm();
MachineStuff.ShowMachineMatrix();
}
}
}

View File

@@ -76,8 +76,8 @@ namespace MouseWithoutBorders
return;
}
string[] st = new string[Common.MAX_MACHINE];
for (int i = 0; i < Common.MAX_MACHINE; i++)
string[] st = new string[MachineStuff.MAX_MACHINE];
for (int i = 0; i < MachineStuff.MAX_MACHINE; i++)
{
if (machines[i].MachineEnabled)
{
@@ -98,7 +98,7 @@ namespace MouseWithoutBorders
}
}
Common.MachineMatrix = st;
MachineStuff.MachineMatrix = st;
Setting.Values.MatrixOneRow = matrixOneRow = !checkBoxTwoRow.Checked;
if (Process.GetCurrentProcess().SessionId != NativeMethods.WTSGetActiveConsoleSessionId())
@@ -124,7 +124,7 @@ namespace MouseWithoutBorders
Common.MMSleep(0.2);
}
Common.SendMachineMatrix();
MachineStuff.SendMachineMatrix();
}
buttonOK.Enabled = true;
@@ -150,13 +150,13 @@ namespace MouseWithoutBorders
bool meAdded = false;
string machineName;
if (Common.MachineMatrix != null && Common.MachineMatrix.Length == Common.MAX_MACHINE)
if (MachineStuff.MachineMatrix != null && MachineStuff.MachineMatrix.Length == MachineStuff.MAX_MACHINE)
{
Logger.LogDebug("LoadMachines: Machine Matrix: " + Setting.Values.MachineMatrixString);
for (int i = 0; i < Common.MAX_MACHINE; i++)
for (int i = 0; i < MachineStuff.MAX_MACHINE; i++)
{
machineName = Common.MachineMatrix[i].Trim();
machineName = MachineStuff.MachineMatrix[i].Trim();
machines[i].MachineName = machineName;
if (string.IsNullOrEmpty(machineName))
@@ -168,7 +168,7 @@ namespace MouseWithoutBorders
machines[i].MachineEnabled = true;
}
bool found = Common.MachinePool.TryFindMachineByName(machineName, out MachineInf machineInfo);
bool found = MachineStuff.MachinePool.TryFindMachineByName(machineName, out MachineInf machineInfo);
if (found)
{
if (machineInfo.Id == Common.MachineID)
@@ -340,7 +340,7 @@ namespace MouseWithoutBorders
string newMachine;
Machine unUsedMachine;
foreach (MachineInf inf in Common.MachinePool.ListAllMachines())
foreach (MachineInf inf in MachineStuff.MachinePool.ListAllMachines())
{
bool found = false;
unUsedMachine = null;
@@ -519,7 +519,7 @@ namespace MouseWithoutBorders
return true;
}
private readonly Machine[] machines = new Machine[Common.MAX_MACHINE];
private readonly Machine[] machines = new Machine[MachineStuff.MAX_MACHINE];
private Machine dragDropMachine;
private Machine desMachine;
private Machine desMachineX;
@@ -530,7 +530,7 @@ namespace MouseWithoutBorders
private void CreateMachines()
{
for (int i = 0; i < Common.MAX_MACHINE; i++)
for (int i = 0; i < MachineStuff.MAX_MACHINE; i++)
{
Machine m = new();
m.MouseDown += Machine_MouseDown;
@@ -550,7 +550,7 @@ namespace MouseWithoutBorders
int dx = (groupBoxMachineMatrix.Width - 40) / 4;
int yOffset = groupBoxMachineMatrix.Height / 3;
for (int i = 0; i < Common.MAX_MACHINE; i++)
for (int i = 0; i < MachineStuff.MAX_MACHINE; i++)
{
machines[i].Left = matrixOneRow ? 22 + (i * dx) : 22 + dx + ((i % 2) * dx);
machines[i].Top = matrixOneRow ? yOffset : (yOffset / 2) + (i / 2 * (machines[i].Width + 2));
@@ -649,7 +649,7 @@ namespace MouseWithoutBorders
desMachineX = desMachineY = desMachine;
for (int i = 0; i < Common.MAX_MACHINE; i++)
for (int i = 0; i < MachineStuff.MAX_MACHINE; i++)
{
if (machines[i] == dragDropMachine)
{
@@ -703,9 +703,9 @@ namespace MouseWithoutBorders
dragDropMachine.Top = desMachinePos.Y;
Machine tmp;
for (int i = 0; i < Common.MAX_MACHINE - 1; i++)
for (int i = 0; i < MachineStuff.MAX_MACHINE - 1; i++)
{
for (int j = 0; j < Common.MAX_MACHINE - 1 - i; j++)
for (int j = 0; j < MachineStuff.MAX_MACHINE - 1 - i; j++)
{
if (machines[j + 1].Top < machines[j].Top || (machines[j + 1].Top == machines[j].Top && machines[j + 1].Left < machines[j].Left))
{
@@ -1041,7 +1041,7 @@ namespace MouseWithoutBorders
{
Setting.Values.MatrixCircle = checkBoxCircle.Checked;
ShowUpdateMessage();
Common.SendMachineMatrix();
MachineStuff.SendMachineMatrix();
}
}
@@ -1187,8 +1187,8 @@ namespace MouseWithoutBorders
ButtonCancel_Click(this, new EventArgs());
Setting.Values.FirstRun = true;
Setting.Values.EasyMouse = (int)EasyMouseOption.Enable;
Common.ClearComputerMatrix();
Common.ShowSetupForm(true);
MachineStuff.ClearComputerMatrix();
MachineStuff.ShowSetupForm(true);
}
}

View File

@@ -171,7 +171,7 @@ namespace MouseWithoutBorders
internal void MenuOnClick(object sender, EventArgs e)
{
string name = (sender as ToolStripMenuItem).Text;
Common.SwitchToMachine(name);
MachineStuff.SwitchToMachine(name);
}
internal void UpdateMenu()
@@ -199,11 +199,11 @@ namespace MouseWithoutBorders
menuGetScreenCapture.DropDown.Items.Count - 1]);
}
for (int i = 0; i < Common.MAX_MACHINE; i++)
for (int i = 0; i < MachineStuff.MAX_MACHINE; i++)
{
string newMachine = Common.MachineMatrix[i].Trim();
string newMachine = MachineStuff.MachineMatrix[i].Trim();
if (Common.MachinePool.TryFindMachineByName(newMachine, out MachineInf inf) && MachinePool.IsAlive(inf))
if (MachineStuff.MachinePool.TryFindMachineByName(newMachine, out MachineInf inf) && MachinePool.IsAlive(inf))
{
ToolStripMenuItem newItem = new(
newMachine,
@@ -372,14 +372,14 @@ namespace MouseWithoutBorders
Common.MyKey = Setting.Values.MyKey;
}
Common.UpdateMachinePoolStringSetting();
MachineStuff.UpdateMachinePoolStringSetting();
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && (Setting.Values.FirstRun || Common.KeyCorrupted))
{
if (!shownSetupFormOneTime)
{
shownSetupFormOneTime = true;
Common.ShowMachineMatrix();
MachineStuff.ShowMachineMatrix();
if (Common.KeyCorrupted && !Setting.Values.FirstRun)
{
@@ -439,7 +439,7 @@ namespace MouseWithoutBorders
Common.GetMachineName();
Logger.LogDebug("Common.pleaseReopenSocket: " + Common.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture));
Common.ReopenSockets(false);
Common.NewDesMachineID = Common.DesMachineID = Common.MachineID;
MachineStuff.NewDesMachineID = Common.DesMachineID = Common.MachineID;
}
}
else
@@ -457,7 +457,7 @@ namespace MouseWithoutBorders
{
Common.PleaseReopenSocket = 0;
Thread.Sleep(1000);
Common.UpdateClientSockets("REOPEN_WHEN_WSAECONNRESET");
MachineStuff.UpdateClientSockets("REOPEN_WHEN_WSAECONNRESET");
}
if (Common.RunOnLogonDesktop)
@@ -496,7 +496,7 @@ namespace MouseWithoutBorders
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
Common.ShowMachineMatrix();
MachineStuff.ShowMachineMatrix();
Common.MatrixForm?.UpdateKeyTextBox();
@@ -511,7 +511,7 @@ namespace MouseWithoutBorders
if (myKeyDaysToExpire <= 0)
{
Common.ShowMachineMatrix();
MachineStuff.ShowMachineMatrix();
Common.Sk?.Close(false);
@@ -532,7 +532,7 @@ namespace MouseWithoutBorders
// if (Common.RunOnLogonDesktop) ShowMouseWithoutBordersUiOnWinLogonDesktop(false);
#endif
Common.CheckForDesktopSwitchEvent(true);
Common.UpdateClientSockets("helperTimer_Tick"); // Sockets may be closed by the remote host when both machines switch desktop at the same time.
MachineStuff.UpdateClientSockets("helperTimer_Tick"); // Sockets may be closed by the remote host when both machines switch desktop at the same time.
}
count++;
@@ -553,11 +553,11 @@ namespace MouseWithoutBorders
Logger.LogAll();
// Need to review this code on why it is needed (moved from MoveToMyNeighbourIfNeeded(...))
for (int i = 0; i < Common.MachineMatrix.Length; i++)
for (int i = 0; i < MachineStuff.MachineMatrix.Length; i++)
{
if (string.IsNullOrEmpty(Common.MachineMatrix[i]) && !Common.InMachineMatrix(Common.MachineName))
if (string.IsNullOrEmpty(MachineStuff.MachineMatrix[i]) && !MachineStuff.InMachineMatrix(Common.MachineName))
{
Common.MachineMatrix[i] = Common.MachineName;
MachineStuff.MachineMatrix[i] = Common.MachineName;
}
}
@@ -567,7 +567,7 @@ namespace MouseWithoutBorders
if (Setting.Values.BlockScreenSaver || count % 3000 == 0)
{
Common.SendAwakeBeat();
Common.RemoveDeadMachines();
MachineStuff.RemoveDeadMachines();
// GC.Collect();
// GC.WaitForPendingFinalizers();
@@ -601,12 +601,12 @@ namespace MouseWithoutBorders
private void MenuMachineMatrix_Click(object sender, EventArgs e)
{
Common.ShowMachineMatrix();
MachineStuff.ShowMachineMatrix();
}
private void FrmScreen_MouseMove(object sender, MouseEventArgs e)
{
if (!Common.IsDropping)
if (!Core.DragDrop.IsDropping)
{
if (Cursor != dotCur)
{
@@ -702,7 +702,7 @@ namespace MouseWithoutBorders
internal void MenuAllPC_Click(object sender, EventArgs e)
{
Logger.LogDebug("menuAllPC_Click");
Common.SwitchToMultipleMode(MenuAllPC.Checked, true);
MachineStuff.SwitchToMultipleMode(MenuAllPC.Checked, true);
CurIcon = MenuAllPC.Checked ? Common.ICON_ALL : Common.ICON_ONE;
ChangeIcon(CurIcon);
}
@@ -711,7 +711,7 @@ namespace MouseWithoutBorders
{
Common.DoSomethingInUIThread(() =>
{
MenuAllPC.Checked = Common.NewDesMachineID == ID.ALL;
MenuAllPC.Checked = MachineStuff.NewDesMachineID == ID.ALL;
CurIcon = MenuAllPC.Checked ? Common.ICON_ALL : Common.ICON_ONE;
ChangeIcon(CurIcon);
});
@@ -819,7 +819,7 @@ namespace MouseWithoutBorders
case NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP:
Logger.LogDebug("Got WM_CHECK_EXPLORER_DRAG_DROP!");
Common.DragDropStep04();
Core.DragDrop.DragDropStep04();
break;
case NativeMethods.WM_QUIT:
@@ -841,7 +841,7 @@ namespace MouseWithoutBorders
case NativeMethods.WM_SHOW_SETTINGS_FORM:
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
Common.ShowMachineMatrix();
MachineStuff.ShowMachineMatrix();
}
break;
@@ -856,7 +856,7 @@ namespace MouseWithoutBorders
{
if (e.Button == MouseButtons.Left)
{
Common.ShowMachineMatrix();
MachineStuff.ShowMachineMatrix();
}
}
@@ -872,7 +872,7 @@ namespace MouseWithoutBorders
internal void UpdateNotifyIcon()
{
string[] x = Common.MachineMatrix;
string[] x = MachineStuff.MachineMatrix;
string iconText;
if (x != null && (x[0].Length > 0 || x[1].Length > 0 || x[2].Length > 0 || x[3].Length > 0))
{
@@ -943,13 +943,13 @@ namespace MouseWithoutBorders
string menuCaption = (sender as ToolStripMenuItem).Text;
// Send CaptureScreenCommand
ID des = menuCaption.Equals("All", StringComparison.OrdinalIgnoreCase) ? ID.ALL : Common.IdFromName(menuCaption);
ID des = menuCaption.Equals("All", StringComparison.OrdinalIgnoreCase) ? ID.ALL : MachineStuff.IdFromName(menuCaption);
Common.SendPackage(des, PackageType.CaptureScreenCommand);
}
private void FrmScreen_Shown(object sender, EventArgs e)
{
Common.AssertOneInstancePerDesktopSession();
MachineStuff.AssertOneInstancePerDesktopSession();
Common.MainForm = this;
Hide();
@@ -1057,11 +1057,11 @@ namespace MouseWithoutBorders
r.Right = Common.ScreenWidth - (Common.ScreenWidth / 5);
r.Bottom = 20;
for (int i = 0; i < Common.MAX_MACHINE; i++)
for (int i = 0; i < MachineStuff.MAX_MACHINE; i++)
{
string newMachine = Common.MachineMatrix[i].Trim();
string newMachine = MachineStuff.MachineMatrix[i].Trim();
if (Common.MachinePool.TryFindMachineByName(newMachine, out MachineInf inf) && MachinePool.IsAlive(inf))
if (MachineStuff.MachinePool.TryFindMachineByName(newMachine, out MachineInf inf) && MachinePool.IsAlive(inf))
{
machineMatrix += "[" + inf.Name.Trim() + "]";
}

View File

@@ -68,11 +68,6 @@ avgSendTime = 0
maxSendTime = 0
totalSendCount = 0
totalSendTime = 0
isDragging = False
dragDropStep05ExCalledByIpc = 0
isDropping = False
dragMachine = NONE
<MouseDown>k__BackingField = False
magicNumber = 0
ran = System.Random
--_impl = System.Random+XoshiroImpl
@@ -163,35 +158,6 @@ ReopenSocketDueToReadError = False
--MaxValue = 31/12/9999 23:59:59
--UnixEpoch = 01/01/1970 00:00:00
lastReleaseAllKeysCall = 0
McMatrixLock = Lock
--_owningThreadId = 0
--_state = 0
--_recursionCount = 0
--_spinCount = 22
--_waiterStartTimeMs = 0
--s_contentionCount = 0
--s_maxSpinCount = 22
--s_minSpinCountForAdaptiveSpin = -100
desMachineID = NONE
DesMachineName =
newDesMachineID = NONE
newDesMachineIdEx = NONE
dropMachineID = NONE
lastJump = ????????????
desktopBounds = MouseWithoutBorders.MyRectangle
--Left = 0
--Top = 0
--Right = 0
--Bottom = 0
primaryScreenBounds = MouseWithoutBorders.MyRectangle
--Left = 0
--Top = 0
--Right = 0
--Bottom = 0
SwitchLocation = MouseWithoutBorders.Class.MouseLocation
--<X>k__BackingField = 0
--<Y>k__BackingField = 0
--<Count>k__BackingField = 0
PackageSent = MouseWithoutBorders.PackageMonitor
--Keyboard = 0
--Mouse = 0
@@ -259,11 +225,6 @@ SymAlBlockSize = 16
PW_LENGTH = 16
HELPER_FORM_TEXT = Mouse without Borders Helper
HelperProcessName = PowerToys.MouseWithoutBordersHelper
MAX_MACHINE = 4
MAX_SOCKET = 8
HEARTBEAT_TIMEOUT = 1500000
SKIP_PIXELS = 1
JUMP_PIXELS = 2
PACKAGE_SIZE = 32
PACKAGE_SIZE_EX = 64
WP_PACKAGE_SIZE = 6
@@ -358,6 +319,49 @@ MAX_LOG = 10000
MaxLogExceptionPerHour = 1000
HeaderSENT = Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},Ie{12},Ni{13}
HeaderRECEIVED = Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},In{12},Ni{13},Pc{14}/{15}
[DragDrop]
===============
isDragging = False
dragDropStep05ExCalledByIpc = 0
isDropping = False
dragMachine = NONE
<MouseDown>k__BackingField = False
[MachineStuff]
===============
McMatrixLock = Lock
--_owningThreadId = 0
--_state = 0
--_recursionCount = 0
--_spinCount = 22
--_waiterStartTimeMs = 0
--s_contentionCount = 0
--s_maxSpinCount = 22
--s_minSpinCountForAdaptiveSpin = -100
desMachineID = NONE
DesMachineName =
newDesMachineID = NONE
newDesMachineIdEx = NONE
dropMachineID = NONE
lastJump = ????????????
desktopBounds = MouseWithoutBorders.Core.MyRectangle
--Left = 0
--Top = 0
--Right = 0
--Bottom = 0
primaryScreenBounds = MouseWithoutBorders.Core.MyRectangle
--Left = 0
--Top = 0
--Right = 0
--Bottom = 0
SwitchLocation = MouseWithoutBorders.Class.MouseLocation
--<X>k__BackingField = 0
--<Y>k__BackingField = 0
--<Count>k__BackingField = 0
MAX_MACHINE = 4
MAX_SOCKET = 8
HEARTBEAT_TIMEOUT = 1500000
SKIP_PIXELS = 1
JUMP_PIXELS = 2
[Receiver]
===============
QUEUE_SIZE = 50

View File

@@ -120,6 +120,10 @@ public static class LoggerTests
_ = Logger.PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, settingsDumpObjectsLevel, false);
sb.AppendLine("[Logger]\r\n===============");
Logger.DumpType(sb, typeof(Logger), 0, settingsDumpObjectsLevel);
sb.AppendLine("[DragDrop]\r\n===============");
Logger.DumpType(sb, typeof(DragDrop), 0, settingsDumpObjectsLevel);
sb.AppendLine("[MachineStuff]\r\n===============");
Logger.DumpType(sb, typeof(MachineStuff), 0, settingsDumpObjectsLevel);
sb.AppendLine("[Receiver]\r\n===============");
Logger.DumpType(sb, typeof(Receiver), 0, settingsDumpObjectsLevel);
var actual = sb.ToString();

View File

@@ -62,11 +62,10 @@ namespace PlacementHelper
else
{
placement.showCmd = SW_RESTORE;
ScreenToWorkAreaCoords(window, monitor, rect);
placement.rcNormalPosition = rect;
}
ScreenToWorkAreaCoords(window, monitor, rect);
placement.rcNormalPosition = rect;
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
auto result = ::SetWindowPlacement(window, &placement);
@@ -430,9 +429,11 @@ bool WindowArranger::moveWindow(HWND window, const WorkspacesData::WorkspacesPro
Logger::error(L"No monitor saved for launching the app");
return false;
}
UINT snapDPI = snapMonitorIter->dpi;
bool launchMinimized = app.isMinimized;
bool launchMaximized = app.isMaximized;
RECT rect = app.position.toRect();
HMONITOR currentMonitor{};
UINT currentDpi = DPIAware::DEFAULT_DPI;
@@ -446,12 +447,18 @@ bool WindowArranger::moveWindow(HWND window, const WorkspacesData::WorkspacesPro
{
currentMonitor = MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
DPIAware::GetScreenDPIForMonitor(currentMonitor, currentDpi);
snapDPI = DPIAware::DEFAULT_DPI;
launchMinimized = true;
launchMaximized = false;
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
if (GetMonitorInfo(currentMonitor, &monitorInfo))
{
rect = monitorInfo.rcWork;
}
}
RECT rect = app.position.toRect();
float mult = static_cast<float>(snapMonitorIter->dpi) / currentDpi;
float mult = static_cast<float>(snapDPI) / currentDpi;
rect.left = static_cast<long>(std::round(rect.left * mult));
rect.right = static_cast<long>(std::round(rect.right * mult));
rect.top = static_cast<long>(std::round(rect.top * mult));

View File

@@ -1820,7 +1820,7 @@ INT_PTR CALLBACK OptionsTabProc( HWND hDlg, UINT message,
break;
case WM_PAINT:
if( (hTextPreview = GetDlgItem( hDlg, IDC_TEXT_FONT ))) {
if( (hTextPreview = GetDlgItem( hDlg, IDC_TEXT_FONT )) != 0 ) {
// 16-pt preview
LOGFONT _lf = g_LogFont;

View File

@@ -83,9 +83,7 @@
<Border
x:Name="MonitorItem"
Width="{Binding DisplayWidth}"
Height="{Binding DisplayHeight}"
AutomationProperties.HelpText="{Binding Index}"
AutomationProperties.Name="{x:Static props:Resources.Monitor}">
Height="{Binding DisplayHeight}">
<Border.ToolTip>
<ToolTip>
<StackPanel>

View File

@@ -5,7 +5,9 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using FancyZonesEditor.Properties;
using FancyZonesEditor.ViewModels;
namespace FancyZonesEditor.Utils
@@ -14,6 +16,8 @@ namespace FancyZonesEditor.Utils
{
public event PropertyChangedEventHandler PropertyChanged;
private static readonly CompositeFormat MonitorIndexFormat = CompositeFormat.Parse(Resources.Monitor_Index);
public MonitorInfoModel(int index, int height, int width, int dpi, bool selected = false)
{
Index = index;
@@ -24,6 +28,10 @@ namespace FancyZonesEditor.Utils
Selected = selected;
}
public string AccessibleName => string.Format(CultureInfo.CurrentCulture, MonitorIndexFormat, Index);
public string AccessibleHelpText => $"{Resources.Dimensions} {Dimensions}, {Resources.Scaling} {Scaling}";
public int Index { get; set; }
public int ScreenBoundsHeight { get; set; }

View File

@@ -276,6 +276,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Dimensions.
/// </summary>
public static string Dimensions {
get {
return ResourceManager.GetString("Dimensions", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Highlight distance.
/// </summary>
@@ -539,7 +548,7 @@ namespace FancyZonesEditor.Properties {
/// <summary>
/// Looks up a localized string similar to
///• [Shift]+S to split currently focused zone.
///• Shift+S to split currently focused zone.
///• Ctrl+Tab to focus zones/resizers.
///• Tab to cycle zones and resizers.
///• Delete to remove the focused resizer.
@@ -554,8 +563,8 @@ namespace FancyZonesEditor.Properties {
/// <summary>
/// Looks up a localized string similar to
///• Ctrl+Tab to switch focus between dialog and zones.
///• [Shift]+Arrows to resize the focused zone by 10px (5px per edge).
///• Ctrl+[Shift]+Arrows to resize the focused zone by 2px (1px per edge).
///• Shift+Arrows to resize the focused zone by 10px (5px per edge).
///• Ctrl+Shift+Arrows to resize the focused zone by 2px (1px per edge).
///• Arrows to move the focused zone by 10px.
///• Ctrl+Arrows to move the focused zone by 1px..
/// </summary>
@@ -655,6 +664,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Monitor {0}.
/// </summary>
public static string Monitor_Index {
get {
return ResourceManager.GetString("Monitor_Index", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
@@ -790,6 +808,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Scaling.
/// </summary>
public static string Scaling {
get {
return ResourceManager.GetString("Scaling", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Set layout as a default for horizontal monitor orientation.
/// </summary>

View File

@@ -282,6 +282,15 @@
<data name="Monitor" xml:space="preserve">
<value>Monitor</value>
</data>
<data name="Monitor_Index" xml:space="preserve">
<value>Monitor {0}</value>
</data>
<data name="Dimensions" xml:space="preserve">
<value>Dimensions</value>
</data>
<data name="Scaling" xml:space="preserve">
<value>Scaling</value>
</data>
<data name="Settings" xml:space="preserve">
<value>Template settings</value>
</data>

View File

@@ -26,11 +26,11 @@
<Style x:Key="MonitorItemContainerStyle" TargetType="ui:GridViewItem">
<Setter Property="Background" Value="{DynamicResource LayoutItemBackgroundBrush}" />
<Setter Property="IsSelected" Value="{Binding Selected, Mode=OneWay}" />
<Setter Property="AutomationProperties.Name" Value="{Binding Index}" />
<Setter Property="AutomationProperties.Name" Value="{Binding AccessibleName}" />
<Setter Property="AutomationProperties.HelpText" Value="{Binding AccessibleHelpText}" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="Local" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<!--<Setter Property="IsHoldingEnabled" Value="True" />-->
<Setter Property="CornerRadius" Value="4" />
<Setter Property="Margin" Value="8" />
<Setter Property="UseSystemFocusVisuals" Value="{DynamicResource UseSystemFocusVisuals}" />

View File

@@ -14,6 +14,7 @@
</ResourceDictionary.MergedDictionaries>
<v:SizeTypeToVisibilityConverter x:Key="SizeTypeToVisibilityConverter" />
<v:SizeTypeToHelpTextConverter x:Key="SizeTypeToHelpTextConverter" />
<v:EnumValueConverter x:Key="EnumValueConverter" />
<v:AutoDoubleConverter x:Key="AutoDoubleConverter" />
<v:BoolValueConverter x:Key="BoolValueConverter" />

View File

@@ -254,6 +254,7 @@ namespace ImageResizer.Properties
{
_selectedSizeIndex = value;
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(SelectedSize));
}
}

View File

@@ -22,12 +22,19 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
AutomationProperties.HelpText="{Binding Settings.SelectedSize, Converter={StaticResource SizeTypeToHelpTextConverter}}"
AutomationProperties.Name="{x:Static p:Resources.Image_Sizes}"
ItemsSource="{Binding Settings.AllSizes}"
SelectedIndex="{Binding Settings.SelectedSizeIndex}">
<ComboBox.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type ComboBoxItem}}" TargetType="ComboBoxItem">
<Setter Property="AutomationProperties.Name" Value="{Binding Name}" />
<Setter Property="AutomationProperties.HelpText" Value="{Binding ., Converter={StaticResource SizeTypeToHelpTextConverter}}" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.Resources>
<DataTemplate DataType="{x:Type m:ResizeSize}">
<Grid VerticalAlignment="Center" AutomationProperties.Name="{Binding Name}">
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@@ -55,7 +62,7 @@
</DataTemplate>
<DataTemplate DataType="{x:Type m:CustomSize}">
<Grid VerticalAlignment="Center" AutomationProperties.Name="{Binding Name}">
<Grid VerticalAlignment="Center">
<TextBlock FontWeight="SemiBold" Text="{Binding Name}" />
</Grid>
</DataTemplate>

View File

@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using ImageResizer.Models;
namespace ImageResizer.Views;
[ValueConversion(typeof(ResizeSize), typeof(string))]
public sealed partial class SizeTypeToHelpTextConverter : IValueConverter
{
private const char MultiplicationSign = '\u00D7';
private readonly EnumValueConverter _enumConverter = new();
private readonly AutoDoubleConverter _autoDoubleConverter = new();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not ResizeSize size)
{
return DependencyProperty.UnsetValue;
}
string EnumToString(Enum value, string parameter = null) =>
_enumConverter.Convert(value, typeof(string), parameter, culture) as string;
string DoubleToString(double value) =>
_autoDoubleConverter.Convert(value, typeof(string), null, culture) as string;
var fit = EnumToString(size.Fit, "ThirdPersonSingular");
var width = DoubleToString(size.Width);
var unit = EnumToString(size.Unit);
return size.ShowHeight ?
$"{fit} {width} {MultiplicationSign} {DoubleToString(size.Height)} {unit}" :
$"{fit} {width} {unit}";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}

View File

@@ -0,0 +1,20 @@
#include "pch.h"
#include "KeyboardManagerEditorLibraryWrapper.h"
#include <common/utils/logger_helper.h>
#include <keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h>
// Test function to call the remapping helper function
bool CheckIfRemappingsAreValid()
{
RemapBuffer remapBuffer;
// Mock valid key to key remappings
remapBuffer.push_back(RemapBufferRow{ RemapBufferItem({ (DWORD)0x41, (DWORD)0x42 }), std::wstring() });
remapBuffer.push_back(RemapBufferRow{ RemapBufferItem({ (DWORD)0x42, (DWORD)0x43 }), std::wstring() });
auto result = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer);
return result == ShortcutErrorType::NoError;
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include <keyboardmanager/common/Helpers.h>
#include <keyboardmanager/common/Input.h>
#include <keyboardmanager/common/MappingConfiguration.h>
extern "C" __declspec(dllexport) bool CheckIfRemappingsAreValid();

View File

@@ -0,0 +1,260 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{4382a954-179a-4078-92af-715187dfff50}</ProjectGuid>
<RootNamespace>KeyboardManagerEditorLibraryWrapper</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<AllProjectBMIsArePublic>true</AllProjectBMIsArePublic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<AllProjectBMIsArePublic>true</AllProjectBMIsArePublic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;KEYBOARDMANAGEREDITORLIBRARYWRAPPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;KEYBOARDMANAGEREDITORLIBRARYWRAPPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;KEYBOARDMANAGEREDITORLIBRARYWRAPPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;KEYBOARDMANAGEREDITORLIBRARYWRAPPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;KEYBOARDMANAGEREDITORLIBRARYWRAPPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<SupportJustMyCode>true</SupportJustMyCode>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalLibraryDirectories>$(SolutionDir)$(Platform)\$(ConfigurationName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;KEYBOARDMANAGEREDITORLIBRARYWRAPPER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="KeyboardManagerEditorLibraryWrapper.h" />
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="KeyboardManagerEditorLibraryWrapper.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
</ProjectReference>
<ProjectReference Include="..\KeyboardManagerEditorLibrary\KeyboardManagerEditorLibrary.vcxproj">
<Project>{23d2070d-e4ad-4add-85a7-083d9c76ad49}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KeyboardManagerEditorLibraryWrapper.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KeyboardManagerEditorLibraryWrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,19 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
</packages>

View File

@@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

View File

@@ -0,0 +1,18 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#include <winrt/base.h>
#include "../KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h"
#include <common/logger/logger.h>
#include <common/utils/resources.h>
#endif //PCH_H

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<Application
x:Class="KeyboardManagerEditorUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:KeyboardManagerEditorUI">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using ManagedCommon;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.UI.Xaml.Shapes;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
namespace KeyboardManagerEditorUI
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
Logger.InitializeLogger("\\Keyboard Manager\\WinUI3Editor\\Logs");
Logger.LogInfo("keyboard-manager WinUI3 editor logger is initialized");
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
window = new MainWindow();
window.Activate();
Logger.LogInfo("keyboard-manager WinUI3 editor window is launched");
}
private Window? window;
}
}

View File

@@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\Common.SelfContained.props" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<RootNamespace>KeyboardManagerEditorUI</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<UseWinUI>true</UseWinUI>
<EnableMsixTooling>true</EnableMsixTooling>
<Nullable>enable</Nullable>
<WindowsPackageType>None</WindowsPackageType>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<AssemblyName>PowerToys.KeyboardManagerEditorUI</AssemblyName>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\$(MSBuildProjectName)</OutputPath>
</PropertyGroup>
<ItemGroup>
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<!--
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
Tools extension to be activated for this project even if the Windows App SDK Nuget
package has not yet been restored.
-->
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Assets\" />
</ItemGroup>
<!--
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
Explorer "Package and Publish" context menu entry to be enabled for this project even if
the Windows App SDK Nuget package has not yet been restored.
-->
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="KeyboardManagerEditorUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:KeyboardManagerEditorUI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="KeyboardManagerEditorUI"
mc:Ignorable="d">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Button x:Name="myButton" Click="MyButton_Click">Click Me</Button>
</StackPanel>
</Window>

View File

@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.Foundation.Collections;
namespace KeyboardManagerEditorUI
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
[DllImport("KeyboardManagerEditorLibraryWrapper.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool CheckIfRemappingsAreValid();
public MainWindow()
{
this.InitializeComponent();
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
// Call the C++ function to check if the current remappings are valid
myButton.Content = CheckIfRemappingsAreValid() ? "Valid" : "Invalid";
}
}
}

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity
Name="edb1d2cd-ef93-4f89-9db6-4edf04ff20a5"
Publisher="CN=haoliuu"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="edb1d2cd-ef93-4f89-9db6-4edf04ff20a5" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>KeyboardManagerEditorUI</DisplayName>
<PublisherDisplayName>haoliuu</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="KeyboardManagerEditorUI"
Description="KeyboardManagerEditorUI"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="KeyboardManagerEditorUI.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- The ID below informs the system that this application is compatible with OS features first introduced in Windows 10.
It is necessary to support features in unpackaged applications, for example the custom titlebar implementation.
For more info see https://docs.microsoft.com/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time#declare-os-compatibility-in-your-application-manifest -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>

View File

@@ -341,5 +341,92 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests
Assert.IsNotNull(result);
Assert.AreEqual(expectedResult, result);
}
[DataTestMethod]
[DataRow("rad(30)", "(180 / pi) * (30)")]
[DataRow("rad( 30 )", "(180 / pi) * ( 30 )")]
[DataRow("deg(30)", "(30)")]
[DataRow("grad(30)", "(9 / 10) * (30)")]
[DataRow("rad( 30)", "(180 / pi) * ( 30)")]
[DataRow("rad(30 )", "(180 / pi) * (30 )")]
[DataRow("rad( 30 )", "(180 / pi) * ( 30 )")]
[DataRow("rad(deg(30))", "(180 / pi) * ((30))")]
[DataRow("deg(rad(30))", "((180 / pi) * (30))")]
[DataRow("grad(rad(30))", "(9 / 10) * ((180 / pi) * (30))")]
[DataRow("rad(grad(30))", "(180 / pi) * ((9 / 10) * (30))")]
[DataRow("rad(30) + deg(45)", "(180 / pi) * (30) + (45)")]
[DataRow("sin(rad(30))", "sin((180 / pi) * (30))")]
[DataRow("cos( rad( 45 ) )", "cos( (180 / pi) * ( 45 ) )")]
[DataRow("tan(rad(grad(90)))", "tan((180 / pi) * ((9 / 10) * (90)))")]
[DataRow("rad(30) + rad(45)", "(180 / pi) * (30) + (180 / pi) * (45)")]
[DataRow("rad(30) * grad(90)", "(180 / pi) * (30) * (9 / 10) * (90)")]
[DataRow("rad(30)/rad(45)", "(180 / pi) * (30)/(180 / pi) * (45)")]
public void ExpandTrigConversions_Degrees(string input, string expectedResult)
{
// Call ExpandTrigConversions in degrees mode
string result = CalculateHelper.ExpandTrigConversions(input, CalculateEngine.TrigMode.Degrees);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(expectedResult, result);
}
[DataTestMethod]
[DataRow("rad(30)", "(30)")]
[DataRow("rad( 30 )", "( 30 )")]
[DataRow("deg(30)", "(pi / 180) * (30)")]
[DataRow("grad(30)", "(pi / 200) * (30)")]
[DataRow("rad( 30)", "( 30)")]
[DataRow("rad(30 )", "(30 )")]
[DataRow("rad( 30 )", "( 30 )")]
[DataRow("rad(deg(30))", "((pi / 180) * (30))")]
[DataRow("deg(rad(30))", "(pi / 180) * ((30))")]
[DataRow("grad(rad(30))", "(pi / 200) * ((30))")]
[DataRow("rad(grad(30))", "((pi / 200) * (30))")]
[DataRow("rad(30) + deg(45)", "(30) + (pi / 180) * (45)")]
[DataRow("sin(rad(30))", "sin((30))")]
[DataRow("cos( rad( 45 ) )", "cos( ( 45 ) )")]
[DataRow("tan(rad(grad(90)))", "tan(((pi / 200) * (90)))")]
[DataRow("rad(30) + rad(45)", "(30) + (45)")]
[DataRow("rad(30) * grad(90)", "(30) * (pi / 200) * (90)")]
[DataRow("rad(30)/rad(45)", "(30)/(45)")]
public void ExpandTrigConversions_Radians(string input, string expectedResult)
{
// Call ExpandTrigConversions in radians mode
string result = CalculateHelper.ExpandTrigConversions(input, CalculateEngine.TrigMode.Radians);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(expectedResult, result);
}
[DataTestMethod]
[DataRow("rad(30)", "(200 / pi) * (30)")]
[DataRow("rad( 30 )", "(200 / pi) * ( 30 )")]
[DataRow("deg(30)", "(10 / 9) * (30)")]
[DataRow("grad(30)", "(30)")]
[DataRow("rad( 30)", "(200 / pi) * ( 30)")]
[DataRow("rad(30 )", "(200 / pi) * (30 )")]
[DataRow("rad( 30 )", "(200 / pi) * ( 30 )")]
[DataRow("rad(deg(30))", "(200 / pi) * ((10 / 9) * (30))")]
[DataRow("deg(rad(30))", "(10 / 9) * ((200 / pi) * (30))")]
[DataRow("grad(rad(30))", "((200 / pi) * (30))")]
[DataRow("rad(grad(30))", "(200 / pi) * ((30))")]
[DataRow("rad(30) + deg(45)", "(200 / pi) * (30) + (10 / 9) * (45)")]
[DataRow("sin(rad(30))", "sin((200 / pi) * (30))")]
[DataRow("cos( rad( 45 ) )", "cos( (200 / pi) * ( 45 ) )")]
[DataRow("tan(rad(grad(90)))", "tan((200 / pi) * ((90)))")]
[DataRow("rad(30) + rad(45)", "(200 / pi) * (30) + (200 / pi) * (45)")]
[DataRow("rad(30) * grad(90)", "(200 / pi) * (30) * (90)")]
[DataRow("rad(30)/rad(45)", "(200 / pi) * (30)/(200 / pi) * (45)")]
public void ExpandTrigConversions_Gradians(string input, string expectedResult)
{
// Call ExpandTrigConversions in gradians mode
string result = CalculateHelper.ExpandTrigConversions(input, CalculateEngine.TrigMode.Gradians);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(expectedResult, result);
}
}
}

View File

@@ -74,6 +74,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests
[DataRow("5,2+6", "5.2+6")]
[DataRow("round(2,5)", "round(2.5)")]
[DataRow("3,3333", "3.3333")]
[DataRow("max(2;3)", "max(2,3)")]
public void Translate_NoErrors_WhenCalled(string input, string expectedResult)
{
// Arrange

View File

@@ -59,8 +59,14 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
input = CalculateHelper.FixHumanMultiplicationExpressions(input);
// Get the user selected trigonometry unit
TrigMode trigMode = Main.GetTrigMode();
// Modify trig functions depending on angle unit setting
input = CalculateHelper.UpdateTrigFunctions(input, Main.GetTrigMode());
input = CalculateHelper.UpdateTrigFunctions(input, trigMode);
// Expand conversions between trig units
input = CalculateHelper.ExpandTrigConversions(input, trigMode);
var result = _magesEngine.Interpret(input);

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using static Microsoft.PowerToys.Run.Plugin.Calculator.CalculateEngine;
namespace Microsoft.PowerToys.Run.Plugin.Calculator
{
@@ -18,6 +19,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
@"factorial\s*\(|sign\s*\(|round\s*\(|rand\s*\(\)|randi\s*\([^\)]|" +
@"sin\s*\(|cos\s*\(|tan\s*\(|arcsin\s*\(|arccos\s*\(|arctan\s*\(|" +
@"sinh\s*\(|cosh\s*\(|tanh\s*\(|arsinh\s*\(|arcosh\s*\(|artanh\s*\(|" +
@"rad\s*\(|deg\s*\(|grad\s*\(|" + /* trigonometry unit conversion macros */
@"pi|" +
@"==|~=|&&|\|\||" +
@"((-?(\d+(\.\d*)?)|-?(\.\d+))[Ee](-?\d+))|" + /* expression from CheckScientificNotation between parenthesis */
@@ -26,7 +28,9 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
RegexOptions.Compiled);
private const string DegToRad = "(pi / 180) * ";
private const string DegToGrad = "(10 / 9) * ";
private const string GradToRad = "(pi / 200) * ";
private const string GradToDeg = "(9 / 10) * ";
private const string RadToDeg = "(180 / pi) * ";
private const string RadToGrad = "(200 / pi) * ";
@@ -266,10 +270,10 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
return input;
}
public static string UpdateTrigFunctions(string input, CalculateEngine.TrigMode mode)
public static string UpdateTrigFunctions(string input, TrigMode mode)
{
string modifiedInput = input;
if (mode == CalculateEngine.TrigMode.Degrees)
if (mode == TrigMode.Degrees)
{
modifiedInput = ModifyTrigFunction(modifiedInput, "sin", DegToRad);
modifiedInput = ModifyTrigFunction(modifiedInput, "cos", DegToRad);
@@ -278,7 +282,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
modifiedInput = ModifyTrigFunction(modifiedInput, "arccos", RadToDeg);
modifiedInput = ModifyTrigFunction(modifiedInput, "arctan", RadToDeg);
}
else if (mode == CalculateEngine.TrigMode.Gradians)
else if (mode == TrigMode.Gradians)
{
modifiedInput = ModifyTrigFunction(modifiedInput, "sin", GradToRad);
modifiedInput = ModifyTrigFunction(modifiedInput, "cos", GradToRad);
@@ -290,5 +294,39 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
return modifiedInput;
}
private static string ModifyMathFunction(string input, string function, string modification)
{
// Create the pattern to match the function, opening bracket, and any spaces in between
string pattern = $@"{function}\s*\(";
return Regex.Replace(input, pattern, modification + "(");
}
public static string ExpandTrigConversions(string input, TrigMode mode)
{
string modifiedInput = input;
// Expand "rad", "deg" and "grad" to their respective conversions for the current trig unit
if (mode == TrigMode.Radians)
{
modifiedInput = ModifyMathFunction(modifiedInput, "deg", DegToRad);
modifiedInput = ModifyMathFunction(modifiedInput, "grad", GradToRad);
modifiedInput = ModifyMathFunction(modifiedInput, "rad", string.Empty);
}
else if (mode == TrigMode.Degrees)
{
modifiedInput = ModifyMathFunction(modifiedInput, "deg", string.Empty);
modifiedInput = ModifyMathFunction(modifiedInput, "grad", GradToDeg);
modifiedInput = ModifyMathFunction(modifiedInput, "rad", RadToDeg);
}
else if (mode == TrigMode.Gradians)
{
modifiedInput = ModifyMathFunction(modifiedInput, "deg", DegToGrad);
modifiedInput = ModifyMathFunction(modifiedInput, "grad", string.Empty);
modifiedInput = ModifyMathFunction(modifiedInput, "rad", RadToGrad);
}
return modifiedInput;
}
}
}

View File

@@ -123,7 +123,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator
outputBuilder.Append(
decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number)
? (new string('0', leadingZeroCount) + number.ToString(cultureTo))
: token);
: token.Replace(cultureFrom.TextInfo.ListSeparator, cultureTo.TextInfo.ListSeparator));
}
}

View File

@@ -1,72 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Linq;
using ManagedCommon;
using Microsoft.Win32;
namespace PowerLauncher.Helper
{
public static class ThemeExtensions
{
public static Theme GetCurrentTheme()
{
// Check for high-contrast mode
Theme highContrastTheme = GetHighContrastBaseType();
if (highContrastTheme != Theme.Light)
{
return highContrastTheme;
}
// Check if the system is using dark or light mode
return IsSystemDarkMode() ? Theme.Dark : Theme.Light;
}
private static bool IsSystemDarkMode()
{
const string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
const string registryValue = "AppsUseLightTheme";
// Retrieve the registry value, which is a DWORD (0 or 1)
object registryValueObj = Registry.GetValue(registryKey, registryValue, null);
if (registryValueObj != null)
{
// 0 = Dark mode, 1 = Light mode
bool isLightMode = Convert.ToBoolean((int)registryValueObj, CultureInfo.InvariantCulture);
return !isLightMode; // Invert because 0 = Dark
}
else
{
// Default to Light theme if the registry key is missing
return false; // Default to dark mode assumption
}
}
public static Theme GetHighContrastBaseType()
{
const string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
const string registryValue = "CurrentTheme";
string themePath = (string)Registry.GetValue(registryKey, registryValue, string.Empty);
if (string.IsNullOrEmpty(themePath))
{
return Theme.Light; // Default to light theme if missing
}
string theme = themePath.Split('\\').Last().Split('.').First().ToLowerInvariant();
return theme switch
{
"hc1" => Theme.HighContrastOne,
"hc2" => Theme.HighContrastTwo,
"hcwhite" => Theme.HighContrastWhite,
"hcblack" => Theme.HighContrastBlack,
_ => Theme.Light,
};
}
}
}

View File

@@ -0,0 +1,137 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using ManagedCommon;
using PowerLauncher.Services;
[assembly: InternalsVisibleTo("Wox.Test")]
namespace PowerLauncher.Helper;
/// <summary>
/// Provides functionality for determining the application's theme based on system settings, user
/// preferences, and High Contrast mode detection.
/// </summary>
public class ThemeHelper
{
private const string ThemesKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
private const string PersonalizeKey = ThemesKey + "\\Personalize";
internal const int AppsUseLightThemeLight = 1;
internal const int AppsUseLightThemeDark = 0;
/// <summary>
/// Default value for the "AppsUseLightTheme" registry setting. This value represents Light
/// mode and will be used if the registry value is invalid or cannot be read.
/// </summary>
internal const int AppsUseLightThemeDefault = AppsUseLightThemeLight;
private readonly IRegistryService _registryService;
private readonly Dictionary<string, Theme> _highContrastThemeMap =
new(StringComparer.InvariantCultureIgnoreCase)
{
{ "hc1", Theme.HighContrastOne },
{ "hc2", Theme.HighContrastTwo },
{ "hcwhite", Theme.HighContrastWhite },
{ "hcblack", Theme.HighContrastBlack },
};
/// <summary>
/// Initializes a new instance of the <see cref="ThemeHelper"/> class.
/// </summary>
/// <param name="registryService">The service used to query registry values. If <c>null</c>, a
/// default implementation is used, which queries the Windows registry. This allows for
/// dependency injection and unit testing.</param>
public ThemeHelper(IRegistryService registryService = null)
{
_registryService = registryService ?? RegistryServiceFactory.Create();
}
/// <summary>
/// Determines the theme to apply, prioritizing an active High Contrast theme.
/// </summary>
/// <param name="settingsTheme">The theme selected in application settings.</param>
/// <returns>The resolved <see cref="Theme"/> based on the following priority order:
/// 1. If a default High Contrast Windows theme is active, return the corresponding High
/// Contrast <see cref="Theme"/>.
/// 2. If "Windows default" is selected in application settings, return the Windows app theme
/// (<see cref="Theme.Dark"/> or <see cref="Theme.Light"/>).
/// 3. If the user explicitly selected "Light" or "Dark", return their chosen theme.
/// 4. If the theme cannot be determined, return <see cref="Theme.Light"/>.
/// </returns>
public Theme DetermineTheme(Theme settingsTheme) =>
GetHighContrastTheme() ??
(settingsTheme == Theme.System ? GetAppsTheme() : ValidateTheme(settingsTheme));
/// <summary>
/// Ensures the provided <see cref="Theme"/> value is valid.
/// </summary>
/// <param name="theme">The <see cref="Theme"/> value to validate.</param>
/// <returns>The provided theme if it is a defined enum value; otherwise, defaults to
/// <see cref="Theme.Light"/>.
private Theme ValidateTheme(Theme theme) => Enum.IsDefined(theme) ? theme : Theme.Light;
/// <summary>
/// Determines if a High Contrast theme is currently active and returns the corresponding
/// <see cref="Theme"/>.
/// </summary>
/// <returns>The detected High Contrast <see cref="Theme"/> (e.g.
/// <see cref="Theme.HighContrastOne"/>, or <c>null</c> if no recognized High Contrast theme
/// is active.
/// </returns>
internal Theme? GetHighContrastTheme()
{
try
{
var themePath = Convert.ToString(
_registryService.GetValue(ThemesKey, "CurrentTheme", string.Empty),
CultureInfo.InvariantCulture);
if (!string.IsNullOrEmpty(themePath) && _highContrastThemeMap.TryGetValue(
Path.GetFileNameWithoutExtension(themePath), out var theme))
{
return theme;
}
}
catch
{
// Fall through to return null. Ignore exception.
}
return null;
}
/// <summary>
/// Retrieves the Windows app theme preference from the registry.
/// </summary>
/// <returns><see cref="Theme.Dark"/> if the user has selected Dark mode for apps,
/// <see cref="Theme.Light"/> otherwise. If the registry value cannot be read or is invalid,
/// the default value (<see cref="Theme.Light"/>) is used.
/// </returns>
internal Theme GetAppsTheme()
{
try
{
// "AppsUseLightTheme" registry value:
// - 0 = Dark mode
// - 1 (or missing/invalid) = Light mode
var regValue = _registryService.GetValue(
PersonalizeKey,
"AppsUseLightTheme",
AppsUseLightThemeDefault);
return regValue is int intValue && intValue == 0 ? Theme.Dark : Theme.Light;
}
catch
{
return Theme.Light;
}
}
}

View File

@@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using ManagedCommon;
@@ -17,10 +17,11 @@ namespace PowerLauncher.Helper
{
private readonly PowerToysRunSettings _settings;
private readonly MainWindow _mainWindow;
private ManagedCommon.Theme _currentTheme;
private readonly ThemeHelper _themeHelper = new();
private bool _disposed;
public ManagedCommon.Theme CurrentTheme => _currentTheme;
public Theme CurrentTheme { get; private set; }
public event Common.UI.ThemeChangedHandler ThemeChanged;
@@ -40,23 +41,25 @@ namespace PowerLauncher.Helper
}
}
private void SetSystemTheme(ManagedCommon.Theme theme)
private void SetSystemTheme(Theme theme)
{
_mainWindow.Background = OSVersionHelper.IsWindows11() is false ? SystemColors.WindowBrush : null;
_mainWindow.Background = !OSVersionHelper.IsWindows11() ? SystemColors.WindowBrush : null;
// Need to disable WPF0001 since setting Application.Current.ThemeMode is experimental
// https://learn.microsoft.com/en-us/dotnet/desktop/wpf/whats-new/net90#set-in-code
#pragma warning disable WPF0001
Application.Current.ThemeMode = theme is ManagedCommon.Theme.Light ? ThemeMode.Light : ThemeMode.Dark;
if (theme is ManagedCommon.Theme.Dark or ManagedCommon.Theme.Light)
Application.Current.ThemeMode = theme == Theme.Light ? ThemeMode.Light : ThemeMode.Dark;
#pragma warning restore WPF0001
if (theme is Theme.Dark or Theme.Light)
{
if (!OSVersionHelper.IsWindows11())
{
// Apply background only on Windows 10
// Windows theme does not work properly for dark and light mode so right now set the background color manual.
// Windows theme does not work properly for dark and light mode so right now set the background color manually.
_mainWindow.Background = new SolidColorBrush
{
Color = theme is ManagedCommon.Theme.Dark ? (Color)ColorConverter.ConvertFromString("#202020") : (Color)ColorConverter.ConvertFromString("#fafafa"),
Color = (Color)ColorConverter.ConvertFromString(theme == Theme.Dark ? "#202020" : "#fafafa"),
};
}
}
@@ -64,49 +67,46 @@ namespace PowerLauncher.Helper
{
string styleThemeString = theme switch
{
ManagedCommon.Theme.Light => "Themes/Light.xaml",
ManagedCommon.Theme.Dark => "Themes/Dark.xaml",
ManagedCommon.Theme.HighContrastOne => "Themes/HighContrast1.xaml",
ManagedCommon.Theme.HighContrastTwo => "Themes/HighContrast2.xaml",
ManagedCommon.Theme.HighContrastWhite => "Themes/HighContrastWhite.xaml",
_ => "Themes/HighContrastBlack.xaml",
Theme.HighContrastOne => "Themes/HighContrast1.xaml",
Theme.HighContrastTwo => "Themes/HighContrast2.xaml",
Theme.HighContrastWhite => "Themes/HighContrastWhite.xaml",
Theme.HighContrastBlack => "Themes/HighContrastBlack.xaml",
_ => "Themes/Light.xaml",
};
_mainWindow.Resources.MergedDictionaries.Clear();
_mainWindow.Resources.MergedDictionaries.Add(new ResourceDictionary
{
Source = new Uri(styleThemeString, UriKind.Relative),
});
ResourceDictionary test = new ResourceDictionary
{
Source = new Uri(styleThemeString, UriKind.Relative),
};
if (OSVersionHelper.IsWindows11())
{
// Apply background only on Windows 11 to keep the same style as WPFUI
_mainWindow.Background = new SolidColorBrush
{
Color = (Color)_mainWindow.FindResource("LauncherBackgroundColor"), // Use your DynamicResource key here
Color = (Color)_mainWindow.FindResource("LauncherBackgroundColor"),
};
}
}
ImageLoader.UpdateIconPath(theme);
ThemeChanged?.Invoke(_currentTheme, theme);
_currentTheme = theme;
ThemeChanged?.Invoke(CurrentTheme, theme);
CurrentTheme = theme;
}
/// <summary>
/// Updates the application's theme based on system settings and user preferences.
/// </summary>
/// <remarks>
/// This considers:
/// - Whether a High Contrast theme is active in Windows.
/// - The system-wide app mode preference (Light or Dark).
/// - The user's preference override for Light or Dark mode in the application settings.
/// </remarks>
public void UpdateTheme()
{
ManagedCommon.Theme newTheme = _settings.Theme;
ManagedCommon.Theme theme = ThemeExtensions.GetHighContrastBaseType();
if (theme != ManagedCommon.Theme.Light)
{
newTheme = theme;
}
else if (_settings.Theme == ManagedCommon.Theme.System)
{
newTheme = ThemeExtensions.GetCurrentTheme();
}
Theme newTheme = _themeHelper.DetermineTheme(_settings.Theme);
_mainWindow.Dispatcher.Invoke(() =>
{

View File

@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Win32;
namespace PowerLauncher.Services;
#nullable enable
/// <summary>
/// Provides methods for interacting with the Windows Registry or an equivalent key-value data
/// store.
/// </summary>
public interface IRegistryService
{
/// <summary>
/// Retrieves the value associated with the specified name, in the specified registry key.
/// If the name is not found in the specified key, returns the specified default value, or
/// <c>null</c> if the specified key does not exist.
/// </summary>
/// <param name="keyName">The full registry path of the key, beginning with a valid registry
/// root, such as "HKEY_CURRENT_USER".</param>
/// <param name="valueName">The name of the name/value pair.</param>
/// <param name="defaultValue">The value to return if <see cref="valueName"/> does not exist.
/// </param>
/// <returns><c>null</c> if the subkey specified by <paramref name="keyName"/> does not exist;
/// otherwise, the value associated with <paramref name="valueName"/>, or
/// <paramref name="defaultValue"/> if <paramref name="valueName"/> is not found.</returns>
/// <exception cref="ArgumentException"><paramref name="keyName"/> does not begin with a valid
/// registry root.</exception>
/// <exception cref="UnauthorizedAccessException">Thrown if access to the registry or
/// equivalent store is denied.</exception>
/// <remarks>Implementations may throw additional exceptions depending on their internal
/// storage mechanism.</remarks>
object? GetValue(string keyName, string? valueName, object? defaultValue);
/// <summary>
/// Sets the specified name/value pair on the specified registry key. If the specified key does
/// not exist, it is created.
/// </summary>
/// <param name="keyName">The full registry path of the key, beginning with a valid registry
/// root, such as "HKEY_CURRENT_USER".</param>
/// <param name="valueName">The name of the name/value pair.</param>
/// <param name="value">The value to be stored.</param>
/// <exception cref="ArgumentException">
/// <paramref name="keyName"/> does not begin with a valid registry root.
///
/// -or-
///
/// <paramref name="keyName"> is longer than the maximum length allowed (255 characters).
/// </exception>
/// <exception cref="UnauthorizedAccessException">Access to the key is denied; for example,
/// it is a root-level node, or the key has not been opened with write access.</exception>
void SetValue(string keyName, string? valueName, object value);
/// <summary>
/// Sets the specified name/value pair on the specified registry key. If the specified key does
/// not exist, it is created.
/// </summary>
/// <param name="keyName">The full registry path of the key, beginning with a valid registry
/// root, such as "HKEY_CURRENT_USER".</param>
/// <param name="valueName">The name of the name/value pair.</param>
/// <param name="value">The value to be stored.</param>
/// <param name="valueKind">The registry data type to use when storing the data.</param>
/// <exception cref="ArgumentException">
/// <paramref name="keyName"/> does not begin with a valid registry root.
///
/// -or-
///
/// <paramref name="keyName"> is longer than the maximum length allowed (255 characters).
///
/// -or-
///
/// The type of <paramref name="value"/> did not match the registry data type specified by
/// <paramref name="valueKind"/>, therefore the data could not be converted properly.
/// </exception>
/// <exception cref="UnauthorizedAccessException">Access to the key is denied; for example,
/// it is a root-level node, or the key has not been opened with write access.</exception>
void SetValue(string keyName, string? valueName, object value, RegistryValueKind valueKind);
}

View File

@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.IO;
using System.Security;
using Microsoft.Win32;
namespace PowerLauncher.Services;
#nullable enable
public class RegistryService : IRegistryService
{
/// <inheritdoc/>
/// <exception cref="SecurityException">The user does not have the permissions required to read
/// from the registry key.</exception>
/// <exception cref="IOException">The <see cref="RegistryKey"/> that contains the specified
/// value has been marked for deletion.</exception>
public object? GetValue(string keyName, string? valueName, object? defaultValue) =>
Registry.GetValue(keyName, valueName, defaultValue);
/// <inheritdoc/>
/// <exception cref="SecurityException">The user does not have the permissions required to
/// create or modify registry keys.</exception>"
public void SetValue(string keyName, string? valueName, object value) =>
Registry.SetValue(keyName, valueName, value);
/// <inheritdoc/>
/// <exception cref="SecurityException">The user does not have the permissions required to
/// create or modify registry keys.</exception>
public void SetValue(string keyName, string? valueName, object value, RegistryValueKind valueKind) =>
Registry.SetValue(keyName, valueName, value, valueKind);
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace PowerLauncher.Services;
/// <summary>
/// Factory for creating instances of <see cref="IRegistryService"/>.
/// </summary>
public static class RegistryServiceFactory
{
/// <summary>
/// Creates the default implementation of <see cref="IRegistryService"/>.
/// </summary>
/// <returns>An instance of the default <see cref="IRegistryService"/> implementation.</returns>
public static IRegistryService Create() => new RegistryService();
}

Some files were not shown because too many files have changed in this diff Show More