mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
1518 lines
52 KiB
C#
1518 lines
52 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.PowerToys.Settings.UI.SerializationContext;
|
|
using Microsoft.UI;
|
|
using Microsoft.UI.Dispatching;
|
|
using Microsoft.UI.Xaml.Media;
|
|
#if !BUILD_INFO_PUBLISH_AOT
|
|
using StreamJsonRpc;
|
|
#endif
|
|
using Windows.ApplicationModel.DataTransfer;
|
|
|
|
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|
{
|
|
public partial class MouseWithoutBordersViewModel : PageViewModelBase
|
|
{
|
|
protected override string ModuleName => MouseWithoutBordersSettings.ModuleName;
|
|
|
|
// 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 Lock _machineMatrixStringLock = new();
|
|
|
|
private bool _disposed;
|
|
|
|
private static readonly Dictionary<SocketStatus, Brush> StatusColors = new Dictionary<SocketStatus, Brush>()
|
|
{
|
|
{ SocketStatus.NA, new SolidColorBrush(ColorHelper.FromArgb(0, 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
|
|
{
|
|
if (_useOriginalUserInterfaceGpoConfiguration == GpoRuleConfigured.Disabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Settings.Properties.ShowOriginalUI;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (!_useOriginalUserInterfaceIsGPOConfigured && (Settings.Properties.ShowOriginalUI != value))
|
|
{
|
|
Settings.Properties.ShowOriginalUI = value;
|
|
NotifyPropertyChanged(nameof(ShowOriginalUI));
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CardForOriginalUiSettingIsEnabled => _useOriginalUserInterfaceIsGPOConfigured == false;
|
|
|
|
public bool ShowPolicyConfiguredInfoForOriginalUiSetting => IsEnabled && _useOriginalUserInterfaceIsGPOConfigured;
|
|
|
|
public bool UseService
|
|
{
|
|
get
|
|
{
|
|
if (_allowServiceModeGpoConfiguration == GpoRuleConfigured.Disabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Settings.Properties.UseService;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (_allowServiceModeIsGPOConfigured)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var valueChanged = Settings.Properties.UseService != value;
|
|
|
|
// Set the UI property itself instantly
|
|
if (valueChanged)
|
|
{
|
|
Settings.Properties.UseService = value;
|
|
OnPropertyChanged(nameof(UseService));
|
|
OnPropertyChanged(nameof(CanUninstallService));
|
|
OnPropertyChanged(nameof(ShowInfobarRunAsAdminText));
|
|
|
|
// 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 UseServiceSettingIsEnabled => _allowServiceModeIsGPOConfigured == false;
|
|
|
|
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 SettingsUtils SettingsUtils { get; set; }
|
|
|
|
private GeneralSettings GeneralSettingsConfig { get; set; }
|
|
|
|
private GpoRuleConfigured _enabledGpoRuleConfiguration;
|
|
private bool _enabledStateIsGPOConfigured;
|
|
private bool _isEnabled;
|
|
|
|
// Configuration policy variables
|
|
private GpoRuleConfigured _clipboardSharingEnabledGpoConfiguration;
|
|
private bool _clipboardSharingEnabledIsGPOConfigured;
|
|
private GpoRuleConfigured _fileTransferEnabledGpoConfiguration;
|
|
private bool _fileTransferEnabledIsGPOConfigured;
|
|
private GpoRuleConfigured _useOriginalUserInterfaceGpoConfiguration;
|
|
private bool _useOriginalUserInterfaceIsGPOConfigured;
|
|
private GpoRuleConfigured _disallowBlockingScreensaverGpoConfiguration;
|
|
private bool _disallowBlockingScreensaverIsGPOConfigured;
|
|
private GpoRuleConfigured _allowServiceModeGpoConfiguration;
|
|
private bool _allowServiceModeIsGPOConfigured;
|
|
private GpoRuleConfigured _sameSubnetOnlyGpoConfiguration;
|
|
private bool _sameSubnetOnlyIsGPOConfigured;
|
|
private GpoRuleConfigured _validateRemoteIpGpoConfiguration;
|
|
private bool _validateRemoteIpIsGPOConfigured;
|
|
private GpoRuleConfigured _disableUserDefinedIpMappingRulesGpoConfiguration;
|
|
private bool _disableUserDefinedIpMappingRulesIsGPOConfigured;
|
|
private string _policyDefinedIpMappingRulesGPOData;
|
|
private bool _policyDefinedIpMappingRulesIsGPOConfigured;
|
|
|
|
public string MachineHostName
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
return Dns.GetHostName();
|
|
}
|
|
catch
|
|
{
|
|
return string.Empty;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsEnabledGpoConfigured
|
|
{
|
|
get => _enabledStateIsGPOConfigured;
|
|
}
|
|
|
|
// SocketStatus enum is now defined in Settings.UI.Library\MouseWithoutBordersIpcModels.cs
|
|
#if !BUILD_INFO_PUBLISH_AOT
|
|
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();
|
|
}
|
|
#endif
|
|
|
|
private static CancellationTokenSource _cancellationTokenSource;
|
|
|
|
private static Task _machinePollingThreadTask;
|
|
|
|
private static SemaphoreSlim _ipcSemaphore = new SemaphoreSlim(1, 1);
|
|
private static NamedPipeClientStream syncHelperStream;
|
|
|
|
#if BUILD_INFO_PUBLISH_AOT
|
|
// AOT-compatible IPC client wrapper
|
|
private sealed partial class SyncHelper : IDisposable
|
|
{
|
|
public SyncHelper(NamedPipeClientStream stream)
|
|
{
|
|
Stream = stream;
|
|
Client = new MouseWithoutBordersIpcClient(stream);
|
|
}
|
|
|
|
public NamedPipeClientStream Stream { get; }
|
|
|
|
public MouseWithoutBordersIpcClient Client { get; private set; }
|
|
|
|
public void Dispose()
|
|
{
|
|
Client?.Dispose();
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (IsEnabled)
|
|
{
|
|
Logger.LogError($"Couldn't create SettingsSync (AOT): {ex}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
#else
|
|
// StreamJsonRpc-based IPC client wrapper (non-AOT builds)
|
|
private sealed partial 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 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)
|
|
{
|
|
if (IsEnabled)
|
|
{
|
|
Logger.LogError($"Couldn't create SettingsSync: {ex}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
public async Task SubmitShutdownRequestAsync()
|
|
{
|
|
await _ipcSemaphore.WaitAsync();
|
|
try
|
|
{
|
|
using (var syncHelper = await GetSettingsSyncHelperAsync())
|
|
{
|
|
if (syncHelper != null)
|
|
{
|
|
#if BUILD_INFO_PUBLISH_AOT
|
|
await syncHelper.Client.ShutdownAsync();
|
|
#else
|
|
syncHelper.Endpoint?.Shutdown();
|
|
var task = syncHelper.Stream.FlushAsync();
|
|
if (task != null)
|
|
{
|
|
await task;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_ipcSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
public async Task SubmitReconnectRequestAsync()
|
|
{
|
|
await _ipcSemaphore.WaitAsync();
|
|
try
|
|
{
|
|
using (var syncHelper = await GetSettingsSyncHelperAsync())
|
|
{
|
|
if (syncHelper != null)
|
|
{
|
|
#if BUILD_INFO_PUBLISH_AOT
|
|
await syncHelper.Client.ReconnectAsync();
|
|
#else
|
|
syncHelper.Endpoint?.Reconnect();
|
|
var task = syncHelper.Stream.FlushAsync();
|
|
if (task != null)
|
|
{
|
|
await task;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_ipcSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
public async Task SubmitNewKeyRequestAsync()
|
|
{
|
|
await _ipcSemaphore.WaitAsync();
|
|
try
|
|
{
|
|
using (var syncHelper = await GetSettingsSyncHelperAsync())
|
|
{
|
|
if (syncHelper != null)
|
|
{
|
|
#if BUILD_INFO_PUBLISH_AOT
|
|
await syncHelper.Client.GenerateNewKeyAsync();
|
|
#else
|
|
syncHelper.Endpoint?.GenerateNewKey();
|
|
var task = syncHelper.Stream.FlushAsync();
|
|
if (task != null)
|
|
{
|
|
await task;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_ipcSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
public async Task SubmitConnectionRequestAsync(string pcName, string securityKey)
|
|
{
|
|
await _ipcSemaphore.WaitAsync();
|
|
try
|
|
{
|
|
using (var syncHelper = await GetSettingsSyncHelperAsync())
|
|
{
|
|
if (syncHelper != null)
|
|
{
|
|
#if BUILD_INFO_PUBLISH_AOT
|
|
await syncHelper.Client.ConnectToMachineAsync(pcName, securityKey);
|
|
#else
|
|
syncHelper.Endpoint?.ConnectToMachine(pcName, securityKey);
|
|
var task = syncHelper.Stream.FlushAsync();
|
|
if (task != null)
|
|
{
|
|
await task;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_ipcSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
private async Task<MachineSocketState[]> PollMachineSocketStateAsync()
|
|
{
|
|
await _ipcSemaphore.WaitAsync();
|
|
try
|
|
{
|
|
using (var syncHelper = await GetSettingsSyncHelperAsync())
|
|
{
|
|
if (syncHelper != null)
|
|
{
|
|
#if BUILD_INFO_PUBLISH_AOT
|
|
return await syncHelper.Client.RequestMachineSocketStateAsync();
|
|
#else
|
|
var task = syncHelper.Endpoint?.RequestMachineSocketStateAsync();
|
|
if (task != null)
|
|
{
|
|
var oldStates = await task;
|
|
|
|
// Convert from ISettingsSyncHelper.MachineSocketState to MachineSocketState
|
|
return oldStates.Select(s => new MachineSocketState
|
|
{
|
|
Name = s.Name,
|
|
Status = (SocketStatus)s.Status,
|
|
}).ToArray();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return Array.Empty<MachineSocketState>();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_ipcSemaphore.Release();
|
|
}
|
|
}
|
|
|
|
private MouseWithoutBordersSettings Settings { get; set; }
|
|
|
|
private DispatcherQueue _uiDispatcherQueue;
|
|
|
|
public MouseWithoutBordersViewModel(SettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc, DispatcherQueue uiDispatcherQueue)
|
|
{
|
|
SettingsUtils = settingsUtils;
|
|
|
|
_uiDispatcherQueue = uiDispatcherQueue;
|
|
|
|
// To obtain the general settings configurations of PowerToys Settings.
|
|
ArgumentNullException.ThrowIfNull(settingsRepository);
|
|
|
|
GeneralSettingsConfig = settingsRepository.SettingsConfig;
|
|
|
|
InitializeEnabledValue();
|
|
InitializePolicyValues();
|
|
|
|
// MouseWithoutBorders settings may be changed by the logic in the utility as machines connect. We need to get a fresh version every time 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, MachineSocketState> states = null;
|
|
try
|
|
{
|
|
states = (await PollMachineSocketStateAsync())?.ToDictionary(s => s.Name, StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogInfo($"Poll 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 InitializePolicyValues()
|
|
{
|
|
// Policies supporting only enabled state
|
|
_disallowBlockingScreensaverGpoConfiguration = GPOWrapper.GetConfiguredMwbDisallowBlockingScreensaverValue();
|
|
_disallowBlockingScreensaverIsGPOConfigured = _disallowBlockingScreensaverGpoConfiguration == GpoRuleConfigured.Enabled;
|
|
_disableUserDefinedIpMappingRulesGpoConfiguration = GPOWrapper.GetConfiguredMwbDisableUserDefinedIpMappingRulesValue();
|
|
_disableUserDefinedIpMappingRulesIsGPOConfigured = _disableUserDefinedIpMappingRulesGpoConfiguration == GpoRuleConfigured.Enabled;
|
|
|
|
// Policies supporting only disabled state
|
|
_allowServiceModeGpoConfiguration = GPOWrapper.GetConfiguredMwbAllowServiceModeValue();
|
|
_allowServiceModeIsGPOConfigured = _allowServiceModeGpoConfiguration == GpoRuleConfigured.Disabled;
|
|
_clipboardSharingEnabledGpoConfiguration = GPOWrapper.GetConfiguredMwbClipboardSharingEnabledValue();
|
|
_clipboardSharingEnabledIsGPOConfigured = _clipboardSharingEnabledGpoConfiguration == GpoRuleConfigured.Disabled;
|
|
_fileTransferEnabledGpoConfiguration = GPOWrapper.GetConfiguredMwbFileTransferEnabledValue();
|
|
_fileTransferEnabledIsGPOConfigured = _fileTransferEnabledGpoConfiguration == GpoRuleConfigured.Disabled;
|
|
_useOriginalUserInterfaceGpoConfiguration = GPOWrapper.GetConfiguredMwbUseOriginalUserInterfaceValue();
|
|
_useOriginalUserInterfaceIsGPOConfigured = _useOriginalUserInterfaceGpoConfiguration == GpoRuleConfigured.Disabled;
|
|
|
|
// Policies supporting enabled and disabled state
|
|
_sameSubnetOnlyGpoConfiguration = GPOWrapper.GetConfiguredMwbSameSubnetOnlyValue();
|
|
_sameSubnetOnlyIsGPOConfigured = _sameSubnetOnlyGpoConfiguration == GpoRuleConfigured.Enabled || _sameSubnetOnlyGpoConfiguration == GpoRuleConfigured.Disabled;
|
|
_validateRemoteIpGpoConfiguration = GPOWrapper.GetConfiguredMwbValidateRemoteIpValue();
|
|
_validateRemoteIpIsGPOConfigured = _validateRemoteIpGpoConfiguration == GpoRuleConfigured.Enabled || _validateRemoteIpGpoConfiguration == GpoRuleConfigured.Disabled;
|
|
|
|
// Special policies
|
|
_policyDefinedIpMappingRulesGPOData = GPOWrapper.GetConfiguredMwbPolicyDefinedIpMappingRules();
|
|
_policyDefinedIpMappingRulesIsGPOConfigured = !string.IsNullOrWhiteSpace(_policyDefinedIpMappingRulesGPOData);
|
|
}
|
|
|
|
public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()
|
|
{
|
|
var hotkeysDict = new Dictionary<string, HotkeySettings[]>
|
|
{
|
|
[ModuleName] = [
|
|
ToggleEasyMouseShortcut,
|
|
LockMachinesShortcut,
|
|
HotKeySwitch2AllPC,
|
|
ReconnectShortcut],
|
|
};
|
|
|
|
return hotkeysDict;
|
|
}
|
|
|
|
private void LoadViewModelFromSettings(MouseWithoutBordersSettings moduleSettings)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(moduleSettings);
|
|
|
|
Settings = moduleSettings;
|
|
/* TODO: Error handling */
|
|
_selectedSwitchBetweenMachineShortcutOptionsIndex = Array.IndexOf(_switchBetweenMachineShortcutOptions, moduleSettings.Properties.HotKeySwitchMachine.Value);
|
|
_easyMouseOptionIndex = (EasyMouseOption)moduleSettings.Properties.EasyMouse.Value;
|
|
|
|
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));
|
|
OnPropertyChanged(nameof(ShowInfobarRunAsAdminText));
|
|
OnPropertyChanged(nameof(ShowInfobarCannotDragDropAsAdmin));
|
|
OnPropertyChanged(nameof(ShowPolicyConfiguredInfoForBehaviorSettings));
|
|
OnPropertyChanged(nameof(ShowPolicyConfiguredInfoForName2IPSetting));
|
|
OnPropertyChanged(nameof(ShowPolicyConfiguredInfoForOriginalUiSetting));
|
|
OnPropertyChanged(nameof(Name2IpListPolicyIsConfigured));
|
|
|
|
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
|
|
{
|
|
if (_clipboardSharingEnabledGpoConfiguration == GpoRuleConfigured.Disabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Settings.Properties.ShareClipboard;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (!_clipboardSharingEnabledIsGPOConfigured && (Settings.Properties.ShareClipboard != value))
|
|
{
|
|
Settings.Properties.ShareClipboard = value;
|
|
NotifyPropertyChanged();
|
|
OnPropertyChanged(nameof(TransferFile));
|
|
OnPropertyChanged(nameof(CardForTransferFileSettingIsEnabled));
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CardForShareClipboardSettingIsEnabled => _clipboardSharingEnabledIsGPOConfigured == false;
|
|
|
|
public bool TransferFile
|
|
{
|
|
get
|
|
{
|
|
if (_fileTransferEnabledGpoConfiguration == GpoRuleConfigured.Disabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Settings.Properties.TransferFile && Settings.Properties.ShareClipboard;
|
|
}
|
|
|
|
set
|
|
{
|
|
// If ShareClipboard is disabled the file transfer does not work and the setting is disabled. => Don't save toggle state.
|
|
// If FileTransferGpo is configured the file transfer does not work and the setting is disabled. => Don't save toggle state.
|
|
if (!ShareClipboard || _fileTransferEnabledIsGPOConfigured)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Settings.Properties.TransferFile != value)
|
|
{
|
|
Settings.Properties.TransferFile = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CardForTransferFileSettingIsEnabled
|
|
{
|
|
get => ShareClipboard && !_fileTransferEnabledIsGPOConfigured;
|
|
}
|
|
|
|
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
|
|
{
|
|
if (_validateRemoteIpGpoConfiguration == GpoRuleConfigured.Enabled)
|
|
{
|
|
return true;
|
|
}
|
|
else if (_validateRemoteIpGpoConfiguration == GpoRuleConfigured.Disabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Settings.Properties.ValidateRemoteMachineIP;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (!_validateRemoteIpIsGPOConfigured && (Settings.Properties.ValidateRemoteMachineIP != value))
|
|
{
|
|
Settings.Properties.ValidateRemoteMachineIP = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CardForValidateRemoteIpSettingIsEnabled => _validateRemoteIpIsGPOConfigured == false;
|
|
|
|
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
|
|
{
|
|
if (_disableUserDefinedIpMappingRulesGpoConfiguration == GpoRuleConfigured.Enabled)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return Settings.Properties.Name2IP.Value.Replace("\r\n", "\r");
|
|
}
|
|
|
|
set
|
|
{
|
|
if (_disableUserDefinedIpMappingRulesIsGPOConfigured)
|
|
{
|
|
return;
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
public string Name2IpListPolicyData
|
|
{
|
|
// 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 => GPOWrapper.GetConfiguredMwbPolicyDefinedIpMappingRules().Replace("\r\n", "\r");
|
|
get => _policyDefinedIpMappingRulesGPOData.Replace("\r\n", "\r");
|
|
}
|
|
|
|
public bool Name2IpListPolicyIsConfigured => _policyDefinedIpMappingRulesIsGPOConfigured && IsEnabled;
|
|
|
|
public bool SameSubnetOnly
|
|
{
|
|
get
|
|
{
|
|
if (_sameSubnetOnlyGpoConfiguration == GpoRuleConfigured.Enabled)
|
|
{
|
|
return true;
|
|
}
|
|
else if (_sameSubnetOnlyGpoConfiguration == GpoRuleConfigured.Disabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Settings.Properties.SameSubnetOnly;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (!_sameSubnetOnlyIsGPOConfigured && (Settings.Properties.SameSubnetOnly != value))
|
|
{
|
|
Settings.Properties.SameSubnetOnly = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CardForSameSubnetOnlySettingIsEnabled => _sameSubnetOnlyIsGPOConfigured == false;
|
|
|
|
public bool BlockScreenSaverOnOtherMachines
|
|
{
|
|
get
|
|
{
|
|
if (_disallowBlockingScreensaverGpoConfiguration == GpoRuleConfigured.Enabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Settings.Properties.BlockScreenSaverOnOtherMachines;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (_disallowBlockingScreensaverIsGPOConfigured)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Settings.Properties.BlockScreenSaverOnOtherMachines != value)
|
|
{
|
|
Settings.Properties.BlockScreenSaverOnOtherMachines = value;
|
|
NotifyPropertyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CardForBlockScreensaverSettingIsEnabled => _disallowBlockingScreensaverIsGPOConfigured == false;
|
|
|
|
// 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 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;
|
|
|
|
set
|
|
{
|
|
if (Settings.Properties.ToggleEasyMouseShortcut != value)
|
|
{
|
|
Settings.Properties.ToggleEasyMouseShortcut = value ?? MouseWithoutBordersProperties.DefaultHotKeyToggleEasyMouse;
|
|
NotifyPropertyChanged();
|
|
NotifyModuleUpdatedSettings();
|
|
}
|
|
}
|
|
}
|
|
|
|
public HotkeySettings LockMachinesShortcut
|
|
{
|
|
get => Settings.Properties.LockMachineShortcut;
|
|
|
|
set
|
|
{
|
|
if (Settings.Properties.LockMachineShortcut != value)
|
|
{
|
|
Settings.Properties.LockMachineShortcut = value;
|
|
Settings.Properties.LockMachineShortcut = value ?? MouseWithoutBordersProperties.DefaultHotKeyLockMachine;
|
|
NotifyPropertyChanged();
|
|
NotifyModuleUpdatedSettings();
|
|
}
|
|
}
|
|
}
|
|
|
|
public HotkeySettings ReconnectShortcut
|
|
{
|
|
get => Settings.Properties.ReconnectShortcut;
|
|
|
|
set
|
|
{
|
|
if (Settings.Properties.ReconnectShortcut != value)
|
|
{
|
|
Settings.Properties.ReconnectShortcut = value;
|
|
Settings.Properties.ReconnectShortcut = value ?? MouseWithoutBordersProperties.DefaultHotKeyReconnect;
|
|
NotifyPropertyChanged();
|
|
NotifyModuleUpdatedSettings();
|
|
}
|
|
}
|
|
}
|
|
|
|
public HotkeySettings HotKeySwitch2AllPC
|
|
{
|
|
get => Settings.Properties.Switch2AllPCShortcut;
|
|
|
|
set
|
|
{
|
|
if (Settings.Properties.Switch2AllPCShortcut != value)
|
|
{
|
|
Settings.Properties.Switch2AllPCShortcut = value;
|
|
Settings.Properties.Switch2AllPCShortcut = value ?? MouseWithoutBordersProperties.DefaultHotKeySwitch2AllPC;
|
|
NotifyPropertyChanged();
|
|
NotifyModuleUpdatedSettings();
|
|
}
|
|
}
|
|
}
|
|
|
|
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 partial 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, SourceGenerationContextContext.Default.MouseWithoutBordersSettings)));
|
|
}
|
|
|
|
public void NotifyUpdatedSettings()
|
|
{
|
|
OnPropertyChanged(null); // Notify all properties might have changed.
|
|
}
|
|
|
|
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
|
|
{
|
|
OnPropertyChanged(propertyName);
|
|
|
|
// Skip saving settings for UI properties
|
|
if (propertyName == nameof(ShowInfobarCannotDragDropAsAdmin) ||
|
|
propertyName == nameof(ShowInfobarRunAsAdminText))
|
|
{
|
|
return;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
if (disposing)
|
|
{
|
|
// Cancel the cancellation token source
|
|
_cancellationTokenSource?.Cancel();
|
|
_cancellationTokenSource?.Dispose();
|
|
|
|
// Wait for the machine polling task to complete
|
|
try
|
|
{
|
|
_machinePollingThreadTask?.Wait(TimeSpan.FromSeconds(1));
|
|
}
|
|
catch (AggregateException)
|
|
{
|
|
// Task was cancelled, which is expected
|
|
}
|
|
|
|
// Dispose the named pipe stream
|
|
try
|
|
{
|
|
syncHelperStream?.Dispose();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"Error disposing sync helper stream: {ex}");
|
|
}
|
|
finally
|
|
{
|
|
syncHelperStream = null;
|
|
}
|
|
|
|
// Dispose the semaphore
|
|
_ipcSemaphore?.Dispose();
|
|
}
|
|
|
|
_disposed = true;
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
internal void UninstallService()
|
|
{
|
|
SendCustomAction("uninstall_service");
|
|
}
|
|
|
|
public bool ShowPolicyConfiguredInfoForServiceSettings
|
|
{
|
|
get
|
|
{
|
|
return IsEnabled && _allowServiceModeIsGPOConfigured;
|
|
}
|
|
}
|
|
|
|
public bool ShowPolicyConfiguredInfoForBehaviorSettings
|
|
{
|
|
get
|
|
{
|
|
return IsEnabled && (_disallowBlockingScreensaverIsGPOConfigured
|
|
|| _clipboardSharingEnabledIsGPOConfigured || _fileTransferEnabledIsGPOConfigured
|
|
|| _sameSubnetOnlyIsGPOConfigured || _validateRemoteIpIsGPOConfigured);
|
|
}
|
|
}
|
|
|
|
public bool ShowInfobarCannotDragDropAsAdmin
|
|
{
|
|
get { return IsElevated && IsEnabled; }
|
|
}
|
|
|
|
public bool ShowInfobarRunAsAdminText
|
|
{
|
|
get { return !CanToggleUseService && IsEnabled && !ShowPolicyConfiguredInfoForServiceSettings; }
|
|
}
|
|
}
|
|
}
|