// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Security.Cryptography;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using System.Windows.Forms;
using global::PowerToys.GPOWrapper;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
//
// Application settings.
//
//
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
//
using Microsoft.Win32;
using MouseWithoutBorders.Core;
using Settings.UI.Library.Attributes;
using Lock = System.Threading.Lock;
using SettingsHelper = Microsoft.PowerToys.Settings.UI.Library.Utilities.Helper;
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Properties.Setting.Values.#LoadIntSetting(System.String,System.Int32)", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Properties.Setting.Values.#SaveSetting(System.String,System.Object)", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Properties.Setting.Values.#LoadStringSetting(System.String,System.String)", Justification = "Dotnet port with style preservation")]
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Properties.Setting.Values.#SaveSettingQWord(System.String,System.Int64)", Justification = "Dotnet port with style preservation")]
namespace MouseWithoutBorders.Class
{
internal class Settings
{
internal bool Changed;
private readonly SettingsUtils _settingsUtils;
private readonly Lock _loadingSettingsLock = new Lock();
private readonly IFileSystemWatcher _watcher;
private MouseWithoutBordersProperties _properties;
private MouseWithoutBordersSettings _settings;
// Avoid instantly saving every change to the file when updating properties.
public bool PauseInstantSaving { get; set; }
private void UpdateSettingsFromJson()
{
try
{
if (!_settingsUtils.SettingsExists("MouseWithoutBorders"))
{
var defaultSettings = new MouseWithoutBordersSettings();
if (!Common.RunOnLogonDesktop)
{
defaultSettings.Save(_settingsUtils);
}
}
var settings = _settingsUtils.GetSettingsOrDefault("MouseWithoutBorders");
if (settings != null)
{
PauseInstantSaving = true;
var last_properties = _properties;
_settings = settings;
_properties = settings.Properties;
// Keep track of the need to resend the machine matrix.
bool shouldSendMachineMatrix = false;
// Keep track of the need to save into the settings file.
bool shouldSaveNewSettingsValues = false;
if (last_properties != null)
{
// Same as in CheckBoxCircle_CheckedChanged
if (last_properties.WrapMouse != _settings.Properties.WrapMouse)
{
shouldSendMachineMatrix = true;
}
// Same as CheckBoxDrawMouse_CheckedChanged
if (last_properties.DrawMouseCursor != _settings.Properties.DrawMouseCursor && !_settings.Properties.DrawMouseCursor)
{
CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue);
}
if (!Enumerable.SequenceEqual(last_properties.MachineMatrixString, _settings.Properties.MachineMatrixString))
{
_properties.MachineMatrixString = _settings.Properties.MachineMatrixString;
MachineStuff.MachineMatrix = null; // Forces read next time it's needed.
shouldSendMachineMatrix = true;
}
var shouldReopenSockets = false;
if (Common.MyKey != _properties.SecurityKey.Value)
{
Common.MyKey = _properties.SecurityKey.Value;
shouldReopenSockets = true;
}
if (shouldReopenSockets)
{
SocketStuff.InvalidKeyFound = false;
InitAndCleanup.ReopenSocketDueToReadError = true;
Common.ReopenSockets(true);
}
if (shouldSendMachineMatrix)
{
MachineStuff.SendMachineMatrix();
shouldSaveNewSettingsValues = true;
}
if (shouldSaveNewSettingsValues)
{
SaveSettings();
}
}
}
}
catch (IOException ex)
{
EventLogger.LogEvent($"Failed to read settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error);
}
PauseInstantSaving = false;
}
public void SaveSettings()
{
if (!Common.RunOnLogonDesktop)
{
SaveSettingsToJson((MouseWithoutBordersProperties)_properties.Clone());
}
}
private void SaveSettingsToJson(MouseWithoutBordersProperties properties_to_save)
{
_settings.Properties = properties_to_save;
_ = Task.Factory.StartNew(
() =>
{
bool saved = false;
for (int i = 0; i < 5; ++i)
{
try
{
lock (_loadingSettingsLock)
{
_settings.Save(_settingsUtils);
}
saved = true;
}
catch (IOException ex)
{
EventLogger.LogEvent($"Failed to write settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error);
}
if (saved)
{
break;
}
else
{
Thread.Sleep(500);
}
}
},
System.Threading.CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
}
internal Settings()
{
_settingsUtils = new SettingsUtils();
_watcher = SettingsHelper.GetFileWatcher("MouseWithoutBorders", "settings.json", () =>
{
try
{
UpdateSettingsFromJson();
}
catch (Exception ex)
{
EventLogger.LogEvent($"Failed to update settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error);
}
});
UpdateSettingsFromJson();
}
internal string Username { get; set; }
internal bool IsMyKeyRandom { get; set; }
internal string MachineMatrixString
{
get
{
lock (_loadingSettingsLock)
{
return string.Join(",", _properties.MachineMatrixString);
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.MachineMatrixString = new List(value.Split(","));
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
}
internal string MachinePoolString
{
get
{
lock (_loadingSettingsLock)
{
return _properties.MachinePool.Value;
}
}
set
{
lock (_loadingSettingsLock)
{
if (!value.Equals(_properties.MachinePool.Value, StringComparison.OrdinalIgnoreCase))
{
_properties.MachinePool.Value = value;
}
}
}
}
internal string MyID => Application.ProductName + " Application";
internal string MyIdEx => Application.ProductName + " Application-Ex";
internal bool ShareClipboard
{
get
{
if (GPOWrapper.GetConfiguredMwbClipboardSharingEnabledValue() == GpoRuleConfigured.Disabled)
{
return false;
}
lock (_loadingSettingsLock)
{
return _properties.ShareClipboard;
}
}
set
{
if (ShareClipboardIsGpoConfigured)
{
return;
}
lock (_loadingSettingsLock)
{
_properties.ShareClipboard = value;
}
}
}
[CmdConfigureIgnore]
[JsonIgnore]
internal bool ShareClipboardIsGpoConfigured => GPOWrapper.GetConfiguredMwbClipboardSharingEnabledValue() == GpoRuleConfigured.Disabled;
internal bool TransferFile
{
get
{
if (GPOWrapper.GetConfiguredMwbFileTransferEnabledValue() == GpoRuleConfigured.Disabled)
{
return false;
}
lock (_loadingSettingsLock)
{
return _properties.TransferFile;
}
}
set
{
if (TransferFileIsGpoConfigured)
{
return;
}
_properties.TransferFile = value;
}
}
[CmdConfigureIgnore]
[JsonIgnore]
internal bool TransferFileIsGpoConfigured => GPOWrapper.GetConfiguredMwbFileTransferEnabledValue() == GpoRuleConfigured.Disabled;
internal bool MatrixOneRow
{
get
{
lock (_loadingSettingsLock)
{
return _properties.MatrixOneRow;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.MatrixOneRow = value;
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
}
internal bool MatrixCircle
{
get
{
lock (_loadingSettingsLock)
{
return _properties.WrapMouse;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.WrapMouse = value;
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
}
internal int EasyMouse
{
get
{
lock (_loadingSettingsLock)
{
return _properties.EasyMouse.Value;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.EasyMouse.Value = value;
if (!PauseInstantSaving)
{
// Easy Mouse can be enabled or disabled through a shortcut, so a save is required.
SaveSettings();
}
}
}
}
internal bool BlockMouseAtCorners
{
get
{
lock (_loadingSettingsLock)
{
return _properties.BlockMouseAtScreenCorners;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.BlockMouseAtScreenCorners = value;
}
}
}
internal bool DisableEasyMouseWhenForegroundWindowIsFullscreen
{
get
{
lock (_loadingSettingsLock)
{
return _properties.DisableEasyMouseWhenForegroundWindowIsFullscreen;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.DisableEasyMouseWhenForegroundWindowIsFullscreen = value;
}
}
}
internal HashSet EasyMouseFullscreenSwitchBlockExcludedApps
{
get
{
lock (_loadingSettingsLock)
{
return _properties.EasyMouseFullscreenSwitchBlockExcludedApps.Value;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.EasyMouseFullscreenSwitchBlockExcludedApps.Value = value;
}
}
}
internal string Enc(string st, bool dec, DataProtectionScope protectionScope)
{
if (st == null || st.Length < 1)
{
return string.Empty;
}
byte[] ep = Common.GetBytesU(st);
byte[] rv, st2;
if (dec)
{
st2 = Convert.FromBase64String(st);
rv = ProtectedData.Unprotect(st2, ep, protectionScope);
return Common.GetStringU(rv);
}
else
{
st2 = Common.GetBytesU(st);
rv = ProtectedData.Protect(st2, ep, protectionScope);
return Convert.ToBase64String(rv);
}
}
internal string MyKey
{
get
{
lock (_loadingSettingsLock)
{
if (_properties.SecurityKey.Value.Length != 0)
{
Logger.LogDebug("GETSECKEY: Key was already loaded/set: " + _properties.SecurityKey.Value);
return _properties.SecurityKey.Value;
}
else
{
string randomKey = Common.CreateDefaultKey();
_properties.SecurityKey.Value = randomKey;
return randomKey;
}
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.SecurityKey.Value = value;
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
}
internal int MyKeyDaysToExpire
{
get
{
lock (_loadingSettingsLock)
{
return int.MaxValue; // TODO(@yuyoyuppe): do we still need expiration mechanics now?
}
}
}
// Note(@htcfreek): Settings UI CheckBox is disabled in frmMatrix.cs > FrmMatrix_Load()
// Note(@htcfreek): If this settings gets implemented in the future we need a Group Policy for it!
internal bool DisableCAD
{
get
{
return false;
}
}
// Note(@htcfreek): Settings UI CheckBox is disabled in frmMatrix.cs > FrmMatrix_Load()
// Note(@htcfreek): If this settings gets implemented in the future we need a Group Policy for it!
internal bool HideLogonLogo
{
get
{
return false;
}
}
internal bool HideMouse
{
get
{
lock (_loadingSettingsLock)
{
return _properties.HideMouseAtScreenEdge;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.HideMouseAtScreenEdge = value;
}
}
}
internal bool BlockScreenSaver
{
get
{
if (GPOWrapper.GetConfiguredMwbDisallowBlockingScreensaverValue() == GpoRuleConfigured.Enabled)
{
return false;
}
lock (_loadingSettingsLock)
{
return _properties.BlockScreenSaverOnOtherMachines;
}
}
set
{
if (BlockScreenSaverIsGpoConfigured)
{
return;
}
lock (_loadingSettingsLock)
{
_properties.BlockScreenSaverOnOtherMachines = value;
}
}
}
[CmdConfigureIgnore]
[JsonIgnore]
internal bool BlockScreenSaverIsGpoConfigured => GPOWrapper.GetConfiguredMwbDisallowBlockingScreensaverValue() == GpoRuleConfigured.Enabled;
internal bool MoveMouseRelatively
{
get
{
lock (_loadingSettingsLock)
{
return _properties.MoveMouseRelatively;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.MoveMouseRelatively = value;
}
}
}
internal string LastPersonalizeLogonScr
{
get
{
return string.Empty;
}
}
internal uint DesMachineID
{
get
{
lock (_loadingSettingsLock)
{
return (uint)_properties.MachineID.Value;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.MachineID.Value = (int)value;
}
}
}
internal int LastX
{
get
{
lock (_loadingSettingsLock)
{
return _properties.LastX.Value;
}
}
set
{
lock (_loadingSettingsLock)
{
Common.LastX = value;
_properties.LastX.Value = value;
}
}
}
internal int LastY
{
get
{
lock (_loadingSettingsLock)
{
return _properties.LastY.Value;
}
}
set
{
lock (_loadingSettingsLock)
{
Common.LastY = value;
_properties.LastY.Value = value;
}
}
}
internal int PackageID
{
get
{
lock (_loadingSettingsLock)
{
return _properties.PackageID.Value;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.PackageID.Value = value;
}
}
}
internal bool FirstRun
{
get
{
lock (_loadingSettingsLock)
{
return _properties.FirstRun;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.FirstRun = value;
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
}
internal int HotKeySwitchMachine
{
get
{
lock (_loadingSettingsLock)
{
return _properties.HotKeySwitchMachine.Value;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.HotKeySwitchMachine.Value = value;
}
}
}
internal HotkeySettings HotKeySwitch2AllPC
{
get
{
lock (_loadingSettingsLock)
{
return _properties.Switch2AllPCShortcut;
}
}
}
internal HotkeySettings HotKeyToggleEasyMouse
{
get
{
lock (_loadingSettingsLock)
{
return _properties.ToggleEasyMouseShortcut;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.ToggleEasyMouseShortcut = value;
}
}
}
internal HotkeySettings HotKeyLockMachine
{
get
{
lock (_loadingSettingsLock)
{
return _properties.LockMachineShortcut;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.LockMachineShortcut = value;
}
}
}
internal HotkeySettings HotKeyReconnect
{
get
{
lock (_loadingSettingsLock)
{
return _properties.ReconnectShortcut;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.ReconnectShortcut = value;
}
}
}
internal int HotKeyExitMM
{
get
{
return 0;
}
}
private int switchCount;
internal int SwitchCount
{
get
{
return switchCount;
}
set
{
switchCount = value;
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
internal int DumpObjectsLevel => 6;
internal int TcpPort => _properties.TCPPort.Value;
internal bool DrawMouse
{
get
{
lock (_loadingSettingsLock)
{
return _properties.DrawMouseCursor;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.DrawMouseCursor = value;
}
}
}
[CmdConfigureIgnore]
internal bool DrawMouseEx
{
get
{
lock (_loadingSettingsLock)
{
return _properties.DrawMouseEx;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.DrawMouseEx = value;
}
}
}
internal bool ReverseLookup
{
get
{
if (GPOWrapper.GetConfiguredMwbValidateRemoteIpValue() == GpoRuleConfigured.Enabled)
{
return true;
}
else if (GPOWrapper.GetConfiguredMwbValidateRemoteIpValue() == GpoRuleConfigured.Disabled)
{
return false;
}
lock (_loadingSettingsLock)
{
return _properties.ValidateRemoteMachineIP;
}
}
set
{
if (ReverseLookupIsGpoConfigured)
{
return;
}
lock (_loadingSettingsLock)
{
_properties.ValidateRemoteMachineIP = value;
}
}
}
[CmdConfigureIgnore]
[JsonIgnore]
internal bool ReverseLookupIsGpoConfigured => GPOWrapper.GetConfiguredMwbValidateRemoteIpValue() == GpoRuleConfigured.Enabled || GPOWrapper.GetConfiguredMwbValidateRemoteIpValue() == GpoRuleConfigured.Disabled;
internal bool SameSubNetOnly
{
get
{
if (GPOWrapper.GetConfiguredMwbSameSubnetOnlyValue() == GpoRuleConfigured.Enabled)
{
return true;
}
else if (GPOWrapper.GetConfiguredMwbSameSubnetOnlyValue() == GpoRuleConfigured.Disabled)
{
return false;
}
lock (_loadingSettingsLock)
{
return _properties.SameSubnetOnly;
}
}
set
{
if (SameSubNetOnlyIsGpoConfigured)
{
return;
}
lock (_loadingSettingsLock)
{
_properties.SameSubnetOnly = value;
}
}
}
[CmdConfigureIgnore]
[JsonIgnore]
internal bool SameSubNetOnlyIsGpoConfigured => GPOWrapper.GetConfiguredMwbSameSubnetOnlyValue() == GpoRuleConfigured.Enabled || GPOWrapper.GetConfiguredMwbSameSubnetOnlyValue() == GpoRuleConfigured.Disabled;
internal string Name2IP
{
get
{
if (GPOWrapper.GetConfiguredMwbDisableUserDefinedIpMappingRulesValue() == GpoRuleConfigured.Enabled)
{
return string.Empty;
}
lock (_loadingSettingsLock)
{
return _properties.Name2IP.Value;
}
}
set
{
if (Name2IpIsGpoConfigured)
{
return;
}
lock (_loadingSettingsLock)
{
_properties.Name2IP.Value = value;
}
}
}
[CmdConfigureIgnore]
[JsonIgnore]
internal bool Name2IpIsGpoConfigured => GPOWrapper.GetConfiguredMwbDisableUserDefinedIpMappingRulesValue() == GpoRuleConfigured.Enabled;
[CmdConfigureIgnore]
[JsonIgnore]
internal string Name2IpPolicyList => GPOWrapper.GetConfiguredMwbPolicyDefinedIpMappingRules();
[CmdConfigureIgnore]
[JsonIgnore]
internal bool Name2IpPolicyListIsGpoConfigured => !string.IsNullOrWhiteSpace(Name2IpPolicyList);
internal bool FirstCtrlShiftS
{
get
{
lock (_loadingSettingsLock)
{
return _properties.FirstCtrlShiftS;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.FirstCtrlShiftS = value;
}
}
}
// Was a value read from registry on original Mouse Without Border, but default should be true. We wrongly released it as false, so we're forcing true here.
// This value wasn't changeable from UI, anyway.
internal bool StealFocusWhenSwitchingMachine => true;
private string deviceId;
internal string DeviceId
{
get
{
string newGuid = Guid.NewGuid().ToString();
if (deviceId == null || deviceId.Length != newGuid.Length)
{
string defaultId = newGuid;
lock (_loadingSettingsLock)
{
_properties.DeviceID = defaultId;
deviceId = _properties.DeviceID.Value;
if (deviceId.Equals(defaultId, StringComparison.OrdinalIgnoreCase))
{
return _properties.DeviceID.Value;
}
}
}
return deviceId;
}
}
private int? machineId;
internal int MachineId
{
get
{
lock (_loadingSettingsLock)
{
machineId ??= (machineId = _properties.MachineID.Value).Value;
if (machineId == 0)
{
var newMachineId = Common.Ran.Next();
_properties.MachineID.Value = newMachineId;
machineId = newMachineId;
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
return machineId.Value;
}
set
{
lock (_loadingSettingsLock)
{
_properties.MachineID.Value = value;
machineId = value;
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
}
internal bool OneWayControlMode => false;
internal bool OneWayClipboardMode => false;
internal bool ShowClipNetStatus
{
get
{
lock (_loadingSettingsLock)
{
return _properties.ShowClipboardAndNetworkStatusMessages;
}
}
set
{
lock (_loadingSettingsLock)
{
_properties.ShowClipboardAndNetworkStatusMessages = value;
}
}
}
internal bool ShowOriginalUI
{
get
{
if (GPOWrapper.GetConfiguredMwbUseOriginalUserInterfaceValue() == GpoRuleConfigured.Disabled)
{
return false;
}
lock (_loadingSettingsLock)
{
return _properties.ShowOriginalUI;
}
}
set
{
if (GPOWrapper.GetConfiguredMwbUseOriginalUserInterfaceValue() == GpoRuleConfigured.Disabled)
{
return;
}
lock (_loadingSettingsLock)
{
_properties.ShowOriginalUI = value;
}
}
}
// If starting the service fails, work in not service mode.
internal bool UseService
{
get
{
if (GPOWrapper.GetConfiguredMwbAllowServiceModeValue() == GpoRuleConfigured.Disabled)
{
return false;
}
lock (_loadingSettingsLock)
{
return _properties.UseService;
}
}
set
{
if (AllowServiceModeIsGpoConfigured)
{
return;
}
lock (_loadingSettingsLock)
{
_properties.UseService = value;
if (!PauseInstantSaving)
{
SaveSettings();
}
}
}
}
[CmdConfigureIgnore]
[JsonIgnore]
internal bool AllowServiceModeIsGpoConfigured => GPOWrapper.GetConfiguredMwbAllowServiceModeValue() == GpoRuleConfigured.Disabled;
// Note(@htcfreek): Settings UI CheckBox is disabled in frmMatrix.cs > FrmMatrix_Load()
internal bool SendErrorLogV2
{
get
{
return false;
}
}
}
public static class Setting
{
internal static Settings Values = new Settings();
}
}