feat(MouseWithoutBorders): Prevent Easy Mouse from moving to another machine when an application is running in fullscreen mode. (#39854)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This PR adds a new feature to Easy Mouse, it is now possible to toggle a
setting that will prevent Easy Mouse to switch away from the host
machine when the foreground application is running in full screen mode,
requiring the user to first alt tab out of the application before
performing the switch, this also comes with a way to allow the switch on
specific apps.


![image](https://github.com/user-attachments/assets/e45bbfa7-89c9-4051-8f1a-f2ac2648a6ca)

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #32197
- [x] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end user facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [x] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: MicrosoftDocs/windows-dev-docs#5470

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

This PR changes the way Easy Mouse checks wherever it should move to
another machine, after checking that the corresponding setting is
enabled and that we are trying to move away from the host machine, it
will run a test using native WinAPI methods to get the foreground window
and check if it is running in full screen.

If it is, it will then check the name of the executable against a list
of ignored app configured by the user, if the executable is found in
that list, the switch will be allowed despite the application running in
full screen.

These new settings were moved along with the original Easy Mouse toggle
to a new "Easy Mouse" setting group to avoid cluttering the Keyboard
shortcuts group.

This feature will only work when used from the controller machine, as I
didn't find a way to easily check for running application on a remote
machine that didn't involved touching the sockets, I felt like such a
change would be out of scope for this issue.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

I had a hard time writing tests and didn't achieve anything meaningful
enough to be included, I may require some guidance on how to properly
write tests for this project.

I tested my changes by running my modified version of
MouseWithoutBorders on my machines, which I did for a few days now, It
allowed me to catch a few bugs, but it has been running smoothly
otherwise.

My changes didn't seemed to have caused any automated tests to fail.

It may require some additional testing for setups including more than
two machines.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kai Tao (from Dev Box) <kaitao@microsoft.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
This commit is contained in:
Tibère B.
2025-08-22 14:42:36 +02:00
committed by GitHub
parent da36d410e3
commit d90575b8da
8 changed files with 338 additions and 23 deletions

View File

@@ -92,6 +92,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public IntProperty EasyMouse { get; set; }
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool DisableEasyMouseWhenForegroundWindowIsFullscreen { get; set; }
// Apps that are to be excluded when using DisableEasyMouseWhenForegroundWindowIsFullscreen
// meaning that it is possible to switch screen when these apps are running in fullscreen.
[CmdConfigureIgnore]
public GenericProperty<HashSet<string>> EasyMouseFullscreenSwitchBlockExcludedApps { get; set; }
[CmdConfigureIgnore]
public IntProperty MachineID { get; set; }
@@ -173,6 +181,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
ShowOriginalUI = false;
UseService = false;
DisableEasyMouseWhenForegroundWindowIsFullscreen = true;
EasyMouseFullscreenSwitchBlockExcludedApps = new GenericProperty<HashSet<string>>(new HashSet<string>(StringComparer.OrdinalIgnoreCase));
HotKeySwitchMachine = new IntProperty(0x70); // VK.F1
ToggleEasyMouseShortcut = DefaultHotKeyToggleEasyMouse;
LockMachineShortcut = DefaultHotKeyLockMachine;

View File

@@ -267,7 +267,8 @@
<ToggleSwitch x:Uid="MouseWithoutBorders_ShowClipboardAndNetworkStatusMessages_ToggleSwitch" IsOn="{x:Bind ViewModel.ShowClipboardAndNetworkStatusMessages, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="MouseWithoutBorders_KeyboardShortcuts_Group" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<controls:SettingsGroup x:Uid="MouseWithoutBorders_EasyMouseSettings_Group" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<tkcontrols:SettingsCard x:Uid="MouseWithoutBorders_EasyMouseOption" HeaderIcon="{ui:FontIcon Glyph=&#xE962;}">
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind Path=ViewModel.EasyMouseOptionIndex, Mode=TwoWay}">
<ComboBoxItem x:Uid="MouseWithoutBorders_EasyMouseOption_Disabled" />
@@ -276,7 +277,46 @@
<ComboBoxItem x:Uid="MouseWithoutBorders_EasyMouseOption_Shift" />
</ComboBox>
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen"
HeaderIcon="{ui:FontIcon Glyph=&#xE962;}"
IsEnabled="{x:Bind ViewModel.EasyMouseEnabled, Mode=OneWay}">
<ToggleSwitch x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_ToggleSwitch" IsOn="{x:Bind ViewModel.DisableEasyMouseWhenForegroundWindowIsFullscreen, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<InfoBar
x:Uid="MouseWithoutBorders_CanOnlyStopEasyMouseIfMovingFromHostMachine"
IsClosable="False"
IsOpen="True"
Severity="Informational" />
<tkcontrols:SettingsExpander
x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_Expander"
HeaderIcon="{ui:FontIcon Glyph=&#xECE4;}"
IsEnabled="{x:Bind ViewModel.IsEasyMouseBlockingOnFullscreenEnabled, Mode=OneWay}"
IsExpanded="False">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard
x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_ExcludedApps"
HorizontalContentAlignment="Stretch"
ContentAlignment="Vertical">
<TextBox
x:Uid="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_TextBoxControl"
MinWidth="240"
MinHeight="160"
AcceptsReturn="True"
IsSpellCheckEnabled="False"
ScrollViewer.IsVerticalRailEnabled="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollMode="Enabled"
Text="{x:Bind ViewModel.EasyMouseFullscreenSwitchBlockExcludedApps, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="MouseWithoutBorders_KeyboardShortcuts_Group" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
<tkcontrols:SettingsCard x:Uid="MouseWithoutBorders_ToggleEasyMouseShortcut" HeaderIcon="{ui:FontIcon Glyph=&#xEDA7;}">
<controls:ShortcutControl
MinWidth="{StaticResource SettingActionControlMinWidth}"

View File

@@ -398,6 +398,9 @@
<data name="MouseWithoutBorders_AdvancedSettings_Group.Header" xml:space="preserve">
<value>Advanced Settings</value>
</data>
<data name="MouseWithoutBorders_EasyMouseSettings_Group.Header" xml:space="preserve">
<value>Easy Mouse</value>
</data>
<data name="MouseWithoutBorders_EasyMouseOption.Header" xml:space="preserve">
<value>Easy Mouse: move between machines by moving the mouse pointer to the screen edges.</value>
</data>
@@ -419,6 +422,27 @@
<value>Shift</value>
<comment>This is the Shift keyboard key</comment>
</data>
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen.Header" xml:space="preserve">
<value>Disable Easy Mouse when an application is running in full screen.</value>
</data>
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen.Description" xml:space="preserve">
<value>Prevent Easy Mouse from moving to another machine when an application is in full-screen mode.</value>
</data>
<data name="MouseWithoutBorders_CanOnlyStopEasyMouseIfMovingFromHostMachine.Title" xml:space="preserve">
<value>Disabling Easy Mouse in full-screen mode only affects the host PC. It wont stop the mouse from moving away from remote machines.</value>
</data>
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_TextBoxControl.PlaceholderText" xml:space="preserve">
<value>msedge.exe
firefox.exe
opera.exe</value>
<comment>Allow easy mouse when chrome is in fullscreen mode.</comment>
</data>
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_Expander.Header" xml:space="preserve">
<value>Ignored fullscreen applications</value>
</data>
<data name="MouseWithoutBorders_DisableEasyMouseWhenForegroundWindowIsFullscreen_Expander.Description" xml:space="preserve">
<value>Allow Easy Mouse to move between machines even if one of these applications is running in full screen, separate each executable with a new line.</value>
</data>
<data name="MouseWithoutBorders_LockMachinesShortcut.Header" xml:space="preserve">
<value>Shortcut to lock all machines.</value>
</data>

View File

@@ -904,6 +904,43 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
private string _easyMouseIgnoredFullscreenAppsString;
public string EasyMouseFullscreenSwitchBlockExcludedApps
{
// Convert the list of excluded apps retrieved from the settings
// to a single string that can be displayed in the bound textbox
get
{
if (_easyMouseIgnoredFullscreenAppsString == null)
{
var excludedApps = Settings.Properties.EasyMouseFullscreenSwitchBlockExcludedApps.Value;
_easyMouseIgnoredFullscreenAppsString = excludedApps.Count == 0 ? string.Empty : string.Join('\r', excludedApps);
}
return _easyMouseIgnoredFullscreenAppsString;
}
set
{
if (EasyMouseFullscreenSwitchBlockExcludedApps == value)
{
return;
}
_easyMouseIgnoredFullscreenAppsString = value;
var ignoredAppsSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (value != string.Empty)
{
ignoredAppsSet.UnionWith(value.Split('\r', StringSplitOptions.RemoveEmptyEntries));
}
Settings.Properties.EasyMouseFullscreenSwitchBlockExcludedApps.Value = ignoredAppsSet;
NotifyPropertyChanged();
}
}
public bool CardForName2IpSettingIsEnabled => _disableUserDefinedIpMappingRulesIsGPOConfigured == false;
public bool ShowPolicyConfiguredInfoForName2IPSetting => _disableUserDefinedIpMappingRulesIsGPOConfigured && IsEnabled;
@@ -1005,6 +1042,30 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public bool EasyMouseEnabled => (EasyMouseOption)EasyMouseOptionIndex != EasyMouseOption.Disable;
public bool IsEasyMouseBlockingOnFullscreenEnabled =>
EasyMouseEnabled && DisableEasyMouseWhenForegroundWindowIsFullscreen;
public bool DisableEasyMouseWhenForegroundWindowIsFullscreen
{
get
{
return Settings.Properties.DisableEasyMouseWhenForegroundWindowIsFullscreen;
}
set
{
if (Settings.Properties.DisableEasyMouseWhenForegroundWindowIsFullscreen == value)
{
return;
}
Settings.Properties.DisableEasyMouseWhenForegroundWindowIsFullscreen = value;
NotifyPropertyChanged();
}
}
public HotkeySettings ToggleEasyMouseShortcut
{
get => Settings.Properties.ToggleEasyMouseShortcut;