PowerToys/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs
Andrey Nekrasov dbc11b8920
[MWB] add Name2IP field to Settings (#26290)
* [MWB] add Name2IP field to Settings

* f: add explanation

* f: spelling

* f: comment
2023-05-25 15:55:11 +01:00

1131 lines
36 KiB
C#

// 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.IO.Pipes;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using global::PowerToys.GPOWrapper;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
using Microsoft.UI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Media;
using StreamJsonRpc;
using Windows.ApplicationModel.DataTransfer;
namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
public class MouseWithoutBordersViewModel : Observable, IDisposable
{
// These should be in the same order as the ComboBoxItems in MouseWithoutBordersPage.xaml switch machine shortcut options
private readonly int[] _switchBetweenMachineShortcutOptions =
{
112,
49,
0,
};
private readonly object _machineMatrixStringLock = new();
private static readonly Dictionary<SocketStatus, Brush> StatusColors = new Dictionary<SocketStatus, Brush>()
{
{ SocketStatus.NA, new SolidColorBrush(ColorHelper.FromArgb(0x71, 0x71, 0x71, 0x71)) },
{ SocketStatus.Resolving, new SolidColorBrush(Colors.Yellow) },
{ SocketStatus.Connecting, new SolidColorBrush(Colors.Orange) },
{ SocketStatus.Handshaking, new SolidColorBrush(Colors.Blue) },
{ SocketStatus.Error, new SolidColorBrush(Colors.Red) },
{ SocketStatus.ForceClosed, new SolidColorBrush(Colors.Purple) },
{ SocketStatus.InvalidKey, new SolidColorBrush(Colors.Brown) },
{ SocketStatus.Timeout, new SolidColorBrush(Colors.Pink) },
{ SocketStatus.SendError, new SolidColorBrush(Colors.Maroon) },
{ SocketStatus.Connected, new SolidColorBrush(Colors.Green) },
};
private bool _connectFieldsVisible;
public bool IsElevated { get => GeneralSettingsConfig.IsElevated; }
public bool CanUninstallService { get => GeneralSettingsConfig.IsElevated && !UseService; }
public ButtonClickCommand AddFirewallRuleEventHandler => new ButtonClickCommand(AddFirewallRule);
public ButtonClickCommand UninstallServiceEventHandler => new ButtonClickCommand(UninstallService);
public bool ShowOriginalUI
{
get => Settings.Properties.ShowOriginalUI;
set
{
if (Settings.Properties.ShowOriginalUI != value)
{
Settings.Properties.ShowOriginalUI = value;
NotifyPropertyChanged(nameof(ShowOriginalUI));
}
}
}
public bool UseService
{
get => Settings.Properties.UseService;
set
{
var valueChanged = Settings.Properties.UseService != value;
// Set the UI property itself instantly
if (valueChanged)
{
Settings.Properties.UseService = value;
OnPropertyChanged(nameof(UseService));
// Must block here until the process exits
Task.Run(async () =>
{
await SubmitShutdownRequestAsync();
_uiDispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
Settings.Properties.UseService = value;
NotifyPropertyChanged(nameof(UseService));
});
});
}
}
}
public bool ConnectFieldsVisible
{
get => _connectFieldsVisible;
set
{
if (_connectFieldsVisible != value)
{
_connectFieldsVisible = value;
OnPropertyChanged(nameof(ConnectFieldsVisible));
}
}
}
private string _connectSecurityKey;
public string ConnectSecurityKey
{
get => _connectSecurityKey;
set
{
if (_connectSecurityKey != value)
{
_connectSecurityKey = value;
OnPropertyChanged(nameof(ConnectSecurityKey));
}
}
}
private string _connectPCName;
public string ConnectPCName
{
get => _connectPCName;
set
{
if (_connectPCName != value)
{
_connectPCName = value;
OnPropertyChanged(nameof(ConnectPCName));
}
}
}
private ISettingsUtils SettingsUtils { get; set; }
private GeneralSettings GeneralSettingsConfig { get; set; }
private GpoRuleConfigured _enabledGpoRuleConfiguration;
private bool _enabledStateIsGPOConfigured;
private bool _isEnabled;
public string MachineHostName
{
get
{
try
{
return Dns.GetHostName();
}
catch
{
return string.Empty;
}
}
}
public bool IsEnabledGpoConfigured
{
get => _enabledStateIsGPOConfigured;
}
private enum SocketStatus : int
{
NA = 0,
Resolving = 1,
Connecting = 2,
Handshaking = 3,
Error = 4,
ForceClosed = 5,
InvalidKey = 6,
Timeout = 7,
SendError = 8,
Connected = 9,
}
private interface ISettingsSyncHelper
{
[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
public struct MachineSocketState
{
// Disable false-positive warning due to IPC
#pragma warning disable CS0649
[Newtonsoft.Json.JsonProperty]
public string Name;
[Newtonsoft.Json.JsonProperty]
public SocketStatus Status;
#pragma warning restore CS0649
}
void Shutdown();
void Reconnect();
void GenerateNewKey();
void ConnectToMachine(string machineName, string securityKey);
Task<MachineSocketState[]> RequestMachineSocketStateAsync();
}
private static CancellationTokenSource _cancellationTokenSource;
private static Task _machinePollingThreadTask;
private static VisualStudio.Threading.AsyncSemaphore _ipcSemaphore = new VisualStudio.Threading.AsyncSemaphore(1);
private sealed class SyncHelper : IDisposable
{
public SyncHelper(NamedPipeClientStream stream)
{
Stream = stream;
Endpoint = JsonRpc.Attach<ISettingsSyncHelper>(Stream);
}
public NamedPipeClientStream Stream { get; }
public ISettingsSyncHelper Endpoint { get; private set; }
public void Dispose()
{
((IDisposable)Endpoint).Dispose();
}
}
private static NamedPipeClientStream syncHelperStream;
private async Task<SyncHelper> GetSettingsSyncHelperAsync()
{
try
{
var recreateStream = false;
if (syncHelperStream == null)
{
recreateStream = true;
}
else
{
if (!syncHelperStream.IsConnected || !syncHelperStream.CanWrite)
{
await syncHelperStream.DisposeAsync();
recreateStream = true;
}
}
if (recreateStream)
{
syncHelperStream = new NamedPipeClientStream(".", "MouseWithoutBorders/SettingsSync", PipeDirection.InOut, PipeOptions.Asynchronous);
await syncHelperStream.ConnectAsync(10000);
}
return new SyncHelper(syncHelperStream);
}
catch (Exception ex)
{
Logger.LogError($"Couldn't create SettingsSync: {ex}");
return null;
}
}
public async Task SubmitShutdownRequestAsync()
{
using (await _ipcSemaphore.EnterAsync())
{
using (var syncHelper = await GetSettingsSyncHelperAsync())
{
syncHelper?.Endpoint?.Shutdown();
var task = syncHelper?.Stream.FlushAsync();
if (task != null)
{
await task;
}
}
}
}
public async Task SubmitReconnectRequestAsync()
{
using (await _ipcSemaphore.EnterAsync())
{
using (var syncHelper = await GetSettingsSyncHelperAsync())
{
syncHelper?.Endpoint?.Reconnect();
var task = syncHelper?.Stream.FlushAsync();
if (task != null)
{
await task;
}
}
}
}
public async Task SubmitNewKeyRequestAsync()
{
using (await _ipcSemaphore.EnterAsync())
{
using (var syncHelper = await GetSettingsSyncHelperAsync())
{
syncHelper?.Endpoint?.GenerateNewKey();
var task = syncHelper?.Stream.FlushAsync();
if (task != null)
{
await task;
}
}
}
}
public async Task SubmitConnectionRequestAsync(string pcName, string securityKey)
{
using (await _ipcSemaphore.EnterAsync())
{
using (var syncHelper = await GetSettingsSyncHelperAsync())
{
syncHelper?.Endpoint?.ConnectToMachine(pcName, securityKey);
var task = syncHelper?.Stream.FlushAsync();
if (task != null)
{
await task;
}
}
}
}
private async Task<ISettingsSyncHelper.MachineSocketState[]> PollMachineSocketStateAsync()
{
using (await _ipcSemaphore.EnterAsync())
{
using (var syncHelper = await GetSettingsSyncHelperAsync())
{
var task = syncHelper?.Endpoint?.RequestMachineSocketStateAsync();
if (task != null)
{
return await task;
}
else
{
return null;
}
}
}
}
private MouseWithoutBordersSettings Settings { get; set; }
private DispatcherQueue _uiDispatcherQueue;
public MouseWithoutBordersViewModel(ISettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc, DispatcherQueue uiDispatcherQueue)
{
SettingsUtils = settingsUtils;
_uiDispatcherQueue = uiDispatcherQueue;
// To obtain the general settings configurations of PowerToys Settings.
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
InitializeEnabledValue();
// MouseWithoutBorders settings may be changed by the logic in the utility as machines connect. We need to get a fresh version everytime instead of using a repository.
MouseWithoutBordersSettings moduleSettings;
moduleSettings = SettingsUtils.GetSettingsOrDefault<MouseWithoutBordersSettings>("MouseWithoutBorders");
LoadViewModelFromSettings(moduleSettings);
// set the callback functions value to handle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
_machinePollingThreadTask = StartMachineStatusPollingThread(_machinePollingThreadTask, _cancellationTokenSource.Token);
}
private Task StartMachineStatusPollingThread(Task previousThreadTask, CancellationToken token)
{
return Task.Run(
async () =>
{
previousThreadTask?.Wait();
while (!token.IsCancellationRequested)
{
Dictionary<string, ISettingsSyncHelper.MachineSocketState> states = null;
try
{
states = (await PollMachineSocketStateAsync())?.ToDictionary(s => s.Name, StringComparer.OrdinalIgnoreCase);
}
catch (Exception ex)
{
Logger.LogInfo($"Poll ISettingsSyncHelper.MachineSocketState error: {ex}");
continue;
}
if (states != null)
{
lock (_machineMatrixStringLock)
{
foreach (var machine in machineMatrixString)
{
if (states.TryGetValue(machine.Item.Name, out var state))
{
_uiDispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
try
{
machine.Item.StatusBrush = StatusColors[state.Status];
}
catch (Exception)
{
}
});
}
}
}
}
Thread.Sleep(500);
}
},
_cancellationTokenSource.Token);
}
private void InitializeEnabledValue()
{
_enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue();
if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
{
// Get the enabled state from GPO.
_enabledStateIsGPOConfigured = true;
_isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
}
else
{
_isEnabled = GeneralSettingsConfig.Enabled.MouseWithoutBorders;
}
}
private void LoadViewModelFromSettings(MouseWithoutBordersSettings moduleSettings)
{
if (moduleSettings == null)
{
throw new ArgumentNullException(nameof(moduleSettings));
}
Settings = moduleSettings;
/* TODO: Error handling */
_selectedSwitchBetweenMachineShortcutOptionsIndex = Array.IndexOf(_switchBetweenMachineShortcutOptions, moduleSettings.Properties.HotKeySwitchMachine.Value);
_easyMouseOptionIndex = (EasyMouseOption)moduleSettings.Properties.EasyMouse.Value;
_toggleEasyMouseShortcutIndex = ConvertMouseWithoutBordersHotKeyValueToIndex(moduleSettings.Properties.HotKeyToggleEasyMouse.Value);
_lockMachinesShortcutIndex = ConvertMouseWithoutBordersHotKeyValueToIndex(moduleSettings.Properties.HotKeyLockMachine.Value);
_reconnectShortcutIndex = ConvertMouseWithoutBordersHotKeyValueToIndex(moduleSettings.Properties.HotKeyReconnect.Value);
_switch2AllPcShortcutIndex = ConvertMouseWithoutBordersHotKeyValueToIndex(moduleSettings.Properties.HotKeySwitch2AllPC.Value, 1);
LoadMachineMatrixString();
}
// Loads the machine matrix, taking into account changes to the machine pool.
private void LoadMachineMatrixString()
{
List<string> loadMachineMatrixString = Settings.Properties.MachineMatrixString ?? new List<string>() { string.Empty, string.Empty, string.Empty, string.Empty };
if (loadMachineMatrixString.Count < 4)
{
// Current logic of MWB assumes there are always 4 slots. Any other configuration means data corruption here.
loadMachineMatrixString = new List<string>() { string.Empty, string.Empty, string.Empty, string.Empty };
}
bool editedTheMatrix = false; // keep track of changes to the matrix because of changes to the available machine pool.
if (!string.IsNullOrEmpty(Settings.Properties.MachinePool?.Value))
{
List<string> availableMachines = new List<string>();
// Format of this field is "NAME1:ID1,NAME2:ID2,..."
// Load the available machines
foreach (string availableMachineIdPair in Settings.Properties.MachinePool.Value.Split(","))
{
string availableMachineName = availableMachineIdPair.Split(':')[0];
availableMachines.Add(availableMachineName);
}
// Start by removing the machines from the matrix that are no longer available to pick.
for (int i = 0; i < loadMachineMatrixString.Count; i++)
{
if (!availableMachines.Contains(loadMachineMatrixString[i]))
{
editedTheMatrix = true;
loadMachineMatrixString[i] = string.Empty;
}
}
// If an available machine is not in the matrix already, fill it in the first available spot.
foreach (string availableMachineName in availableMachines)
{
if (!loadMachineMatrixString.Contains(availableMachineName))
{
int availableIndex = loadMachineMatrixString.FindIndex(name => string.IsNullOrEmpty(name));
if (availableIndex >= 0)
{
loadMachineMatrixString[availableIndex] = availableMachineName;
editedTheMatrix = true;
}
}
}
}
// Dragging while elevated crashes on WinUI3: https://github.com/microsoft/microsoft-ui-xaml/issues/7690
machineMatrixString = new IndexedObservableCollection<DeviceViewModel>(loadMachineMatrixString.Select(name => new DeviceViewModel { Name = name, CanDragDrop = !IsElevated }));
if (editedTheMatrix)
{
// Set the property directly to save the new matrix right away with the new available machines.
MachineMatrixString = machineMatrixString;
}
}
public bool CanBeEnabled
{
get => !_enabledStateIsGPOConfigured;
}
public bool CanToggleUseService
{
get
{
return IsEnabled && !(!IsElevated && !UseService);
}
}
public bool IsEnabled
{
get => _isEnabled;
set
{
if (_enabledStateIsGPOConfigured)
{
// If it's GPO configured, shouldn't be able to change this state.
return;
}
if (_isEnabled != value)
{
_isEnabled = value;
GeneralSettingsConfig.Enabled.MouseWithoutBorders = value;
OnPropertyChanged(nameof(IsEnabled));
Task.Run(async () =>
{
if (!value)
{
try
{
await SubmitShutdownRequestAsync();
}
catch (Exception ex)
{
Logger.LogError($"Failed to shutdown MWB via SettingsSync: {ex}");
}
}
_uiDispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
{
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
SendConfigMSG(outgoing.ToString());
NotifyPropertyChanged();
// Disable service mode if we're not elevated, because we cannot register service in that case
if (value == true && !IsElevated && UseService)
{
UseService = false;
}
});
});
}
}
}
public string SecurityKey
{
get => Settings.Properties.SecurityKey.Value;
set
{
if (value != Settings.Properties.SecurityKey.Value)
{
Settings.Properties.SecurityKey.Value = value;
NotifyPropertyChanged();
}
}
}
public bool WrapMouse
{
get
{
return Settings.Properties.WrapMouse;
}
set
{
if (Settings.Properties.WrapMouse != value)
{
Settings.Properties.WrapMouse = value;
NotifyPropertyChanged();
}
}
}
public bool MatrixOneRow
{
get
{
return Settings.Properties.MatrixOneRow;
}
set
{
if (Settings.Properties.MatrixOneRow != value)
{
Settings.Properties.MatrixOneRow = value;
NotifyPropertyChanged();
}
}
}
public bool ShareClipboard
{
get
{
return Settings.Properties.ShareClipboard;
}
set
{
if (Settings.Properties.ShareClipboard != value)
{
Settings.Properties.ShareClipboard = value;
NotifyPropertyChanged();
}
}
}
public bool TransferFile
{
get
{
return Settings.Properties.TransferFile;
}
set
{
if (Settings.Properties.TransferFile != value)
{
Settings.Properties.TransferFile = value;
NotifyPropertyChanged();
}
}
}
public bool HideMouseAtScreenEdge
{
get
{
return Settings.Properties.HideMouseAtScreenEdge;
}
set
{
if (Settings.Properties.HideMouseAtScreenEdge != value)
{
Settings.Properties.HideMouseAtScreenEdge = value;
NotifyPropertyChanged();
}
}
}
public bool DrawMouseCursor
{
get
{
return Settings.Properties.DrawMouseCursor;
}
set
{
if (Settings.Properties.DrawMouseCursor != value)
{
Settings.Properties.DrawMouseCursor = value;
NotifyPropertyChanged();
}
}
}
public bool ValidateRemoteMachineIP
{
get
{
return Settings.Properties.ValidateRemoteMachineIP;
}
set
{
if (Settings.Properties.ValidateRemoteMachineIP != value)
{
Settings.Properties.ValidateRemoteMachineIP = value;
NotifyPropertyChanged();
}
}
}
public string Name2IP
{
// Due to https://github.com/microsoft/microsoft-ui-xaml/issues/1826, we must
// add back \n chars on set and remove them on get for the widget
// to make its behavior consistent with the old UI and MWB internal code.
get
{
return Settings.Properties.Name2IP.Value.Replace("\r\n", "\r");
}
set
{
var newValue = value.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n");
if (Settings.Properties.Name2IP.Value != newValue)
{
Settings.Properties.Name2IP.Value = newValue;
NotifyPropertyChanged();
}
}
}
public bool SameSubnetOnly
{
get
{
return Settings.Properties.SameSubnetOnly;
}
set
{
if (Settings.Properties.SameSubnetOnly != value)
{
Settings.Properties.SameSubnetOnly = value;
NotifyPropertyChanged();
}
}
}
public bool BlockScreenSaverOnOtherMachines
{
get
{
return Settings.Properties.BlockScreenSaverOnOtherMachines;
}
set
{
if (Settings.Properties.BlockScreenSaverOnOtherMachines != value)
{
Settings.Properties.BlockScreenSaverOnOtherMachines = value;
NotifyPropertyChanged();
}
}
}
// Should match EasyMouseOption enum from MouseWithoutBorders and the ComboBox in the MouseWithoutBordersView.cs
private enum EasyMouseOption
{
Disable = 0,
Enable = 1,
Ctrl = 2,
Shift = 3,
}
private EasyMouseOption _easyMouseOptionIndex;
public int EasyMouseOptionIndex
{
get
{
return (int)_easyMouseOptionIndex;
}
set
{
if (value != (int)_easyMouseOptionIndex)
{
_easyMouseOptionIndex = (EasyMouseOption)value;
Settings.Properties.EasyMouse.Value = value;
NotifyPropertyChanged(nameof(EasyMouseOptionIndex));
}
}
}
public int ConvertMouseWithoutBordersHotKeyValueToIndex(int value, int additionalOptions = 0)
{
if (value >= 0x41 && value <= 0x5A)
{
return value - 0x40 + additionalOptions; /* VK_A <= value <= VK_Z */
}
if (value <= additionalOptions)
{
return value;
}
return 0; /* Disabled */
}
public int ConvertMouseWithoutBordersHotKeyIndexToValue(int index, int additionalOptions = 0)
{
if (index >= additionalOptions + 1 && index <= additionalOptions + 26)
{
return index + 0x40 - additionalOptions; /* VK_A to VK_Z */
}
if (index <= additionalOptions)
{
return index;
}
return 0; /* Disabled */
}
private int _toggleEasyMouseShortcutIndex;
public int ToggleEasyMouseShortcutIndex
{
get
{
return _toggleEasyMouseShortcutIndex;
}
set
{
if (_toggleEasyMouseShortcutIndex != value)
{
_toggleEasyMouseShortcutIndex = value;
Settings.Properties.HotKeyToggleEasyMouse.Value = ConvertMouseWithoutBordersHotKeyIndexToValue(value);
NotifyPropertyChanged();
}
}
}
private int _lockMachinesShortcutIndex;
public int LockMachinesShortcutIndex
{
get
{
return _lockMachinesShortcutIndex;
}
set
{
if (_lockMachinesShortcutIndex != value)
{
_lockMachinesShortcutIndex = value;
Settings.Properties.HotKeyLockMachine.Value = ConvertMouseWithoutBordersHotKeyIndexToValue(value);
NotifyPropertyChanged();
}
}
}
private int _reconnectShortcutIndex;
public int ReconnectShortcutIndex
{
get
{
return _reconnectShortcutIndex;
}
set
{
if (_reconnectShortcutIndex != value)
{
_reconnectShortcutIndex = value;
Settings.Properties.HotKeyReconnect.Value = ConvertMouseWithoutBordersHotKeyIndexToValue(value);
NotifyPropertyChanged();
}
}
}
private int _switch2AllPcShortcutIndex;
public int Switch2AllPcShortcutIndex
{
get
{
return _switch2AllPcShortcutIndex;
}
set
{
if (_switch2AllPcShortcutIndex != value)
{
_switch2AllPcShortcutIndex = value;
Settings.Properties.HotKeySwitch2AllPC.Value = ConvertMouseWithoutBordersHotKeyIndexToValue(value, 1);
NotifyPropertyChanged();
}
}
}
private int _selectedSwitchBetweenMachineShortcutOptionsIndex;
public int SelectedSwitchBetweenMachineShortcutOptionsIndex
{
get
{
return _selectedSwitchBetweenMachineShortcutOptionsIndex;
}
set
{
if (_selectedSwitchBetweenMachineShortcutOptionsIndex != value)
{
_selectedSwitchBetweenMachineShortcutOptionsIndex = value;
Settings.Properties.HotKeySwitchMachine.Value = _switchBetweenMachineShortcutOptions[value];
NotifyPropertyChanged();
}
}
}
public bool MoveMouseRelatively
{
get
{
return Settings.Properties.MoveMouseRelatively;
}
set
{
if (Settings.Properties.MoveMouseRelatively != value)
{
Settings.Properties.MoveMouseRelatively = value;
NotifyPropertyChanged();
}
}
}
public bool BlockMouseAtScreenCorners
{
get
{
return Settings.Properties.BlockMouseAtScreenCorners;
}
set
{
if (Settings.Properties.BlockMouseAtScreenCorners != value)
{
Settings.Properties.BlockMouseAtScreenCorners = value;
NotifyPropertyChanged();
}
}
}
private IndexedObservableCollection<DeviceViewModel> machineMatrixString;
public class DeviceViewModel : Observable
{
public string Name { get; set; }
public bool CanDragDrop { get; set; }
private Brush _statusBrush = StatusColors[SocketStatus.NA];
public Brush StatusBrush
{
get
{
return _statusBrush;
}
set
{
if (_statusBrush != value)
{
_statusBrush = value;
OnPropertyChanged(nameof(StatusBrush));
}
}
}
}
public IndexedObservableCollection<DeviceViewModel> MachineMatrixString
{
get
{
lock (_machineMatrixStringLock)
{
return machineMatrixString;
}
}
set
{
lock (_machineMatrixStringLock)
{
machineMatrixString = value;
}
Settings.Properties.MachineMatrixString = new List<string>(value.ToEnumerable().Select(d => d.Name));
NotifyPropertyChanged();
}
}
public bool ShowClipboardAndNetworkStatusMessages
{
get
{
return Settings.Properties.ShowClipboardAndNetworkStatusMessages;
}
set
{
if (Settings.Properties.ShowClipboardAndNetworkStatusMessages != value)
{
Settings.Properties.ShowClipboardAndNetworkStatusMessages = value;
NotifyPropertyChanged();
}
}
}
public bool LoadUpdatedSettings()
{
try
{
LoadViewModelFromSettings(SettingsUtils.GetSettings<MouseWithoutBordersSettings>("MouseWithoutBorders"));
return true;
}
catch (System.Exception ex)
{
Logger.LogError(ex.Message);
return false;
}
}
private void SendCustomAction(string actionName)
{
SendConfigMSG("{\"action\":{\"MouseWithoutBorders\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}");
}
public void AddFirewallRule()
{
SendCustomAction("add_firewall");
}
public void RefreshEnabledState()
{
InitializeEnabledValue();
OnPropertyChanged(nameof(IsEnabled));
}
private void NotifyModuleUpdatedSettings()
{
SendConfigMSG(
string.Format(
CultureInfo.InvariantCulture,
"{{ \"powertoys\": {{ \"{0}\": {1} }} }}",
MouseWithoutBordersSettings.ModuleName,
JsonSerializer.Serialize(Settings)));
}
public void NotifyUpdatedSettings()
{
OnPropertyChanged(null); // Notify all properties might have changed.
}
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(propertyName);
SettingsUtils.SaveSettings(Settings.ToJsonString(), MouseWithoutBordersSettings.ModuleName);
if (propertyName == nameof(UseService))
{
NotifyModuleUpdatedSettings();
}
}
private Func<string, int> SendConfigMSG { get; }
public void CopyMachineNameToClipboard()
{
var data = new DataPackage();
data.SetText(Dns.GetHostName());
Clipboard.SetContent(data);
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
internal void UninstallService()
{
SendCustomAction("uninstall_service");
}
}
}