2023-05-16 06:32:26 +08:00
// 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 ( ) ;
}
}
}
2023-05-25 22:55:11 +08:00
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 ( ) ;
}
}
}
2023-05-16 06:32:26 +08:00
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" ) ;
}
}
}