aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2020-03-04 21:32:42 +0200
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2020-03-04 21:32:42 +0200
commitf17d39f37cac50861467e07a7bee40534d20100a (patch)
tree1d6ba97191b866eee6ffc6ef904fe3803a84decd /Software/Visual_Studio
parent565e48de649d3d14e6b82012b6aa2e3819a3c82c (diff)
downloadTango-f17d39f37cac50861467e07a7bee40534d20100a.tar.gz
Tango-f17d39f37cac50861467e07a7bee40534d20100a.zip
Improved "Notify Continuous Requests About Disconnection".
Integrated FSE/PPC Remote Desktop. Implemented RemoteDesktopService / RemoteDesktopProvider. Implemented Mouse/Keyboard gestures.
Diffstat (limited to 'Software/Visual_Studio')
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs108
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml6
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml.cs42
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs7
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs4
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteDesktop/DesktopFrameReceivedEventArgs.cs14
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteDesktop/IRemoteDesktopProvider.cs27
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj8
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs28
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs341
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj27
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs5
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml4
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config6
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs124
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs2
-rw-r--r--Software/Visual_Studio/Tango.Core/Tango.Core.csproj4
-rw-r--r--Software/Visual_Studio/Tango.Core/Threading/IntervalMessageDispatcher.cs119
-rw-r--r--Software/Visual_Studio/Tango.Core/Threading/SequencerThread.cs71
-rw-r--r--Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs6
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Input/KeyboardController.cs79
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Input/MouseController.cs80
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardEventType.cs14
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardStateRequest.cs18
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardStateResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseEventType.cs16
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseStateRequest.cs17
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseStateResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/StopRemoteDesktopSessionRequest.cs12
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Network/StopRemoteDesktopSessionResponse.cs12
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs6
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/Tango.RemoteDesktop.csproj13
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/packages.config1
-rw-r--r--Software/Visual_Studio/Tango.Transport/TransporterBase.cs17
34 files changed, 1082 insertions, 180 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs
index eb5d57447..c8365af3b 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/ViewModels/RemoteDesktopViewVM.cs
@@ -6,10 +6,13 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
using System.Windows.Media.Imaging;
using Tango.Core.Commands;
using Tango.Core.Threading;
using Tango.FSE.Common;
+using Tango.FSE.Common.RemoteDesktop;
using Tango.RemoteDesktop.Frames;
using Tango.RemoteDesktop.Network;
@@ -17,8 +20,6 @@ namespace Tango.FSE.PPCConsole.ViewModels
{
public class RemoteDesktopViewVM : FSEViewModel
{
- private RasterFrame _currentFrame;
-
private BitmapSource _source;
public BitmapSource Source
{
@@ -31,7 +32,7 @@ namespace Tango.FSE.PPCConsole.ViewModels
public RemoteDesktopViewVM()
{
- StartCommand = new RelayCommand(StartRemoteDesktop, () => MachineProvider.IsConnected);
+ StartCommand = new RelayCommand(StartRemoteDesktop);
StopCommand = new RelayCommand(StopRemoteDesktop);
}
@@ -39,71 +40,70 @@ namespace Tango.FSE.PPCConsole.ViewModels
{
base.OnApplicationStarted();
- MachineProvider.MachineConnected += (_, __) => InvalidateRelayCommands();
- MachineProvider.MachineDisconnected += (_, __) => InvalidateRelayCommands();
+ RemoteDesktopProvider.FrameReceived += RemoteDesktopProvider_FrameReceived;
}
- private void StartRemoteDesktop()
+ private async void StartRemoteDesktop()
{
- SequencerThread<StartRemoteDesktopSessionResponse> sequencer = null;
- sequencer = new SequencerThread<StartRemoteDesktopSessionResponse>((response) =>
+ try
+ {
+ await RemoteDesktopProvider.StartSession();
+ }
+ catch (Exception ex)
{
- if (response.Packet == null)
- {
- sequencer.FrameRate = 1000 / response.FrameRate;
- return; //Returned just to notice that there was no timeout..
- }
+ await NotificationProvider.ShowError($"Error starting remote desktop session.\n{ex.FlattenMessage()}");
+ }
+ }
- try
- {
- if (!response.Packet.IsPartial)
- {
- using (MemoryStream ms = new MemoryStream(response.Packet.Bitmap))
- {
- if (_currentFrame != null)
- {
- _currentFrame.Dispose();
- }
+ private async void StopRemoteDesktop()
+ {
+ try
+ {
+ await RemoteDesktopProvider.EndSession();
+ }
+ catch (Exception ex)
+ {
+ await NotificationProvider.ShowError($"Error stopping remote desktop session.\n{ex.FlattenMessage()}");
+ }
+ }
- _currentFrame = new RasterFrame(new Bitmap(ms));
- }
- }
- else
- {
- using (MemoryStream ms = new MemoryStream(response.Packet.Bitmap))
- {
- var diffFrame = new RasterFrame(new Bitmap(ms), response.Packet.PartialRegion.Left, response.Packet.PartialRegion.Top);
- diffFrame.Apply(_currentFrame);
- diffFrame.Dispose();
- }
- }
+ private void RemoteDesktopProvider_FrameReceived(object sender, DesktopFrameReceivedEventArgs e)
+ {
+ Source = e.Source;
+ }
- Source = _currentFrame.ToBitmapSource();
- }
- catch (Exception ex)
- {
- Debug.WriteLine($"Error on remote desktop packet received: {ex.Message}");
- }
- });
+ #region Mouse & Keyboard Handlers From View
- sequencer.Start();
+ public void OnMouseDown(MouseButton changedButton, System.Windows.Point point, System.Windows.Size size)
+ {
+ RemoteDesktopProvider.MouseDown(changedButton, point, size);
+ }
- MachineProvider.MachineOperator.SendGenericContinuousRequest<StartRemoteDesktopSessionRequest, StartRemoteDesktopSessionResponse>(new StartRemoteDesktopSessionRequest() { }, new Transport.TransportContinuousRequestConfig()
- {
- ContinuousTimeout = TimeSpan.FromSeconds(30),
- }).Subscribe((response) =>
- {
- sequencer.Push(response);
+ public void OnMouseUp(MouseButton changedButton, System.Windows.Point point, System.Windows.Size size)
+ {
+ RemoteDesktopProvider.MouseUp(changedButton, point, size);
+ }
- }, (ex) =>
- {
- Debug.WriteLine(ex);
- }, () => { });
+ public void OnMouseMove(System.Windows.Point point, System.Windows.Size size)
+ {
+ //RemoteDesktopProvider.MouseMove(point, size);
}
- private void StopRemoteDesktop()
+ public void OnMouseDoubleClick(MouseButton changedButton, System.Windows.Point point, System.Windows.Size size)
{
+ RemoteDesktopProvider.MouseDoubleClick(changedButton, point, size);
+ }
+ public void OnKeyboardDown(Key key, bool ctrlDown, bool shitDown, bool altDown)
+ {
+ throw new NotImplementedException();
}
+
+ public void OnKeyboardUp(Key key, bool ctrlDown, bool shitDown, bool altDown)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
}
}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml
index b6a4eec62..9d3308741 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml
@@ -19,8 +19,8 @@
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
- <Button Command="{Binding StopCommand}" Grid.Column="1" Style="{StaticResource FSE_RaisedButton_Dark_Hover}">STOP</Button>
- <Button Command="{Binding StartCommand}" Grid.Column="2" Margin="10 0 0 0" Style="{StaticResource FSE_RaisedButton_Dark_Hover}">START</Button>
+ <Button Command="{Binding StopCommand}" IsEnabled="{Binding RemoteDesktopProvider.InSession}" Grid.Column="1" Style="{StaticResource FSE_RaisedButton_Dark_Hover}">STOP</Button>
+ <Button Command="{Binding StartCommand}" IsEnabled="{Binding RemoteDesktopProvider.CanStartSession}" Grid.Column="2" Margin="10 0 0 0" Style="{StaticResource FSE_RaisedButton_Dark_Hover}">START</Button>
</Grid>
<Border CornerRadius="15" BorderBrush="{StaticResource FSE_BorderBrush}" BorderThickness="1">
@@ -42,7 +42,7 @@
<Border.Background>
<ImageBrush ImageSource="{StaticResource FSE_PPC_Back}" />
</Border.Background>
- <Image Source="{Binding Source}" RenderOptions.BitmapScalingMode="Fant" Stretch="Fill"></Image>
+ <Image x:Name="img" Focusable="True" Source="{Binding Source}" RenderOptions.BitmapScalingMode="Fant" Stretch="Fill" Visibility="{Binding RemoteDesktopProvider.InSession,Converter={StaticResource BooleanToVisibilityConverter}}"></Image>
</Border>
</Border>
</Border>
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml.cs
index 27be82c5d..be7c280c8 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml.cs
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml.cs
@@ -12,6 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
+using Tango.FSE.PPCConsole.ViewModels;
namespace Tango.FSE.PPCConsole.Views
{
@@ -20,9 +21,50 @@ namespace Tango.FSE.PPCConsole.Views
/// </summary>
public partial class RemoteDesktopView : UserControl
{
+ private RemoteDesktopViewVM _vm;
+
public RemoteDesktopView()
{
InitializeComponent();
+ Loaded += (_, __) => _vm = DataContext as RemoteDesktopViewVM;
+
+ img.PreviewMouseDown += Img_PreviewMouseDown;
+ img.PreviewMouseUp += Img_PreviewMouseUp;
+ img.PreviewMouseMove += Img_PreviewMouseMove;
+ img.PreviewKeyDown += Img_PreviewKeyDown;
+ img.PreviewKeyUp += Img_PreviewKeyUp;
+ }
+
+ private void Img_PreviewKeyDown(object sender, KeyEventArgs e)
+ {
+ _vm.OnKeyboardDown(e.Key, Keyboard.IsKeyDown(Key.LeftCtrl), Keyboard.IsKeyDown(Key.LeftShift), Keyboard.IsKeyDown(Key.LeftAlt));
+ }
+
+ private void Img_PreviewKeyUp(object sender, KeyEventArgs e)
+ {
+ _vm.OnKeyboardUp(e.Key, Keyboard.IsKeyDown(Key.LeftCtrl), Keyboard.IsKeyDown(Key.LeftShift), Keyboard.IsKeyDown(Key.LeftAlt));
+ }
+
+ private void Img_PreviewMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.ClickCount == 2)
+ {
+ _vm.OnMouseDoubleClick(e.ChangedButton, e.GetPosition(img), new Size(img.ActualWidth, img.ActualHeight));
+ }
+ else
+ {
+ _vm.OnMouseDown(e.ChangedButton, e.GetPosition(img), new Size(img.ActualWidth, img.ActualHeight));
+ }
+ }
+
+ private void Img_PreviewMouseUp(object sender, MouseButtonEventArgs e)
+ {
+ _vm.OnMouseUp(e.ChangedButton, e.GetPosition(img), new Size(img.ActualWidth, img.ActualHeight));
+ }
+
+ private void Img_PreviewMouseMove(object sender, MouseEventArgs e)
+ {
+ _vm.OnMouseMove(e.GetPosition(img), new Size(img.ActualWidth, img.ActualHeight));
}
}
}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs
index c0abe6dab..bde374597 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/FSEViewModel.cs
@@ -21,6 +21,7 @@ using Tango.FSE.Common.FSEApplication;
using Tango.FSE.Common.Gateway;
using Tango.FSE.Common.Navigation;
using Tango.FSE.Common.Notifications;
+using Tango.FSE.Common.RemoteDesktop;
using Tango.Settings;
using Tango.SharedUI;
using static Tango.SharedUI.Controls.NavigationControl;
@@ -84,6 +85,12 @@ namespace Tango.FSE.Common
public IDiagnosticsProvider DiagnosticsProvider { get; set; }
/// <summary>
+ /// Gets or sets the remote desktop provider.
+ /// </summary>
+ [TangoInject]
+ public IRemoteDesktopProvider RemoteDesktopProvider { get; set; }
+
+ /// <summary>
/// Gets or sets the FSE service.
/// </summary>
[TangoInject]
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs
index 832b69d92..6246d0ce1 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Notifications/SnackbarItem.cs
@@ -79,9 +79,9 @@ namespace Tango.FSE.Common.Notifications
Task.Delay(TimeSpan.FromSeconds(2)).ContinueWith((y) =>
{
Close();
- }, TaskScheduler.FromCurrentSynchronizationContext());
+ });
}
- }, TaskScheduler.FromCurrentSynchronizationContext());
+ });
}
}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteDesktop/DesktopFrameReceivedEventArgs.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteDesktop/DesktopFrameReceivedEventArgs.cs
new file mode 100644
index 000000000..1589c2b99
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteDesktop/DesktopFrameReceivedEventArgs.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+
+namespace Tango.FSE.Common.RemoteDesktop
+{
+ public class DesktopFrameReceivedEventArgs : EventArgs
+ {
+ public BitmapSource Source { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteDesktop/IRemoteDesktopProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteDesktop/IRemoteDesktopProvider.cs
new file mode 100644
index 000000000..716739d0a
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/RemoteDesktop/IRemoteDesktopProvider.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media.Imaging;
+
+namespace Tango.FSE.Common.RemoteDesktop
+{
+ public interface IRemoteDesktopProvider
+ {
+ event EventHandler<DesktopFrameReceivedEventArgs> FrameReceived;
+ bool InSession { get; }
+ bool CanStartSession { get; }
+ TimeSpan MouseMoveInterval { get; set; }
+ Task StartSession();
+ Task EndSession();
+ void MouseDown(MouseButton button, Point location, Size viewSize);
+ void MouseUp(MouseButton button, Point location, Size viewSize);
+ void MouseMove(Point location, Size viewSize);
+ void MouseDoubleClick(MouseButton button, Point location, Size viewSize);
+ void KeyboardDown(Key key, bool ctrlDown, bool shitDown, bool altDown);
+ void KeyboardUp(Key key, bool ctrlDown, bool shitDown, bool altDown);
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj b/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj
index 664643f95..6e219c67a 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Tango.FSE.Common.csproj
@@ -62,6 +62,8 @@
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Data" />
<Reference Include="System.Runtime.Serialization" />
+ <Reference Include="System.Windows" />
+ <Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\ControlzEx.3.0.2.4\lib\net45\System.Windows.Interactivity.dll</HintPath>
</Reference>
@@ -116,6 +118,8 @@
<Compile Include="Notifications\SnackbarItem.cs" />
<Compile Include="Notifications\TaskItem.cs" />
<Compile Include="FSEModuleBase.cs" />
+ <Compile Include="RemoteDesktop\DesktopFrameReceivedEventArgs.cs" />
+ <Compile Include="RemoteDesktop\IRemoteDesktopProvider.cs" />
<Compile Include="Threading\IDispatcherProvider.cs" />
<Compile Include="Web\FSEWebClient.cs" />
<Compile Include="Web\FSEWebClientBase.cs" />
@@ -183,7 +187,9 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
- <None Include="App.config" />
+ <None Include="App.config">
+ <SubType>Designer</SubType>
+ </None>
<Resource Include="Fonts\digital-7.ttf" />
<Resource Include="Fonts\Flexo-Black.otf" />
<Resource Include="Fonts\Flexo-BlackIt.otf" />
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs
index f76e8751d..40d2e58e7 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Notifications/DefaultNotificationProvider.cs
@@ -542,8 +542,11 @@ namespace Tango.FSE.UI.Notifications
/// <returns></returns>
public TaskItem PushTaskItem(TaskItem taskBarItem)
{
- TaskItems.Add(taskBarItem);
- RaisePropertyChanged(nameof(HasTaskItems));
+ InvokeUI(() =>
+ {
+ TaskItems.Add(taskBarItem);
+ RaisePropertyChanged(nameof(HasTaskItems));
+ });
return taskBarItem;
}
@@ -573,8 +576,11 @@ namespace Tango.FSE.UI.Notifications
/// <param name="taskBarItem"></param>
public void PopTaskItem(TaskItem taskBarItem)
{
- TaskItems.Remove(taskBarItem);
- RaisePropertyChanged(nameof(HasTaskItems));
+ InvokeUI(() =>
+ {
+ TaskItems.Remove(taskBarItem);
+ RaisePropertyChanged(nameof(HasTaskItems));
+ });
}
/// <summary>
@@ -605,8 +611,11 @@ namespace Tango.FSE.UI.Notifications
/// <returns></returns>
public SnackbarItem PushSnackbarItem(SnackbarItem snackbarItem)
{
- SnackbarItems.Insert(0, snackbarItem);
- RaisePropertyChanged(nameof(HasSnackbarItems));
+ InvokeUI(() =>
+ {
+ SnackbarItems.Insert(0, snackbarItem);
+ RaisePropertyChanged(nameof(HasSnackbarItems));
+ });
return snackbarItem;
}
@@ -632,8 +641,11 @@ namespace Tango.FSE.UI.Notifications
/// <param name="snackbarItem">The snack bar item.</param>
public void PopSnackbarItem(SnackbarItem snackbarItem)
{
- SnackbarItems.Remove(snackbarItem);
- RaisePropertyChanged(nameof(HasSnackbarItems));
+ InvokeUI(() =>
+ {
+ SnackbarItems.Remove(snackbarItem);
+ RaisePropertyChanged(nameof(HasSnackbarItems));
+ });
}
}
}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs
new file mode 100644
index 000000000..3fc553127
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/RemoteDesktop/DefaultRemoteDesktopProvider.cs
@@ -0,0 +1,341 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media.Imaging;
+using Tango.Core;
+using Tango.Core.DI;
+using Tango.Core.Threading;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Notifications;
+using Tango.FSE.Common.RemoteDesktop;
+using Tango.RemoteDesktop.Frames;
+using Tango.RemoteDesktop.Network;
+using Tango.Transport;
+
+namespace Tango.FSE.UI.RemoteDesktop
+{
+ public class DefaultRemoteDesktopProvider : ExtendedObject, IRemoteDesktopProvider
+ {
+ private class MouseMovement
+ {
+ public Point Location { get; set; }
+ public Size ViewSize { get; set; }
+ }
+
+ private IMachineProvider _machineProvider;
+ private RasterFrame _currentFrame;
+ private Size? _frameSize;
+ private IntervalMessageDispatcher<StartRemoteDesktopSessionResponse> _frameDispatcher;
+ private Thread _mouseMoveThread;
+ private ProducerConsumerQueue<MouseMovement> _mouseMovements;
+
+ public event EventHandler<DesktopFrameReceivedEventArgs> FrameReceived;
+
+ [TangoInject]
+ private INotificationProvider NotificationProvider { get; set; }
+
+ private bool _inSession;
+ public bool InSession
+ {
+ get { return _inSession; }
+ set { _inSession = value; RaisePropertyChangedAuto(); RaisePropertyChanged(nameof(CanStartSession)); }
+ }
+
+ public TimeSpan MouseMoveInterval { get; set; }
+
+ public bool CanStartSession
+ {
+ get { return _machineProvider.IsConnected && !InSession; }
+ }
+
+ public DefaultRemoteDesktopProvider()
+ {
+ MouseMoveInterval = TimeSpan.FromMilliseconds(100);
+ }
+
+ public DefaultRemoteDesktopProvider(IMachineProvider machineProvider) : this()
+ {
+ _machineProvider = machineProvider;
+ _machineProvider.MachineConnected += (_, __) =>
+ {
+ InSession = false;
+ };
+ _machineProvider.MachineDisconnected += (_, __) =>
+ {
+ InSession = false;
+ OnFrameReceived(null);
+ };
+ }
+
+ public Task StartSession()
+ {
+ TaskCompletionSource<bool> completionSource = new TaskCompletionSource<bool>();
+
+ if (!CanStartSession)
+ {
+ throw new InvalidOperationException("Unable to start a remote desktop session at the moment.");
+ }
+
+ if (!InSession)
+ {
+ InSession = true;
+
+ var taskItem = NotificationProvider.PushTaskItem("Starting remote desktop session...");
+
+ if (_frameDispatcher != null)
+ {
+ _frameDispatcher.Dispose();
+ }
+
+ _frameDispatcher = new IntervalMessageDispatcher<StartRemoteDesktopSessionResponse>(OnRemoteDesktopResponse);
+ _frameDispatcher.Start();
+
+ bool taskCompleted = false;
+
+ _machineProvider.MachineOperator.SendGenericContinuousRequest<StartRemoteDesktopSessionRequest, StartRemoteDesktopSessionResponse>(new StartRemoteDesktopSessionRequest() { }, new TransportContinuousRequestConfig()
+ {
+ Timeout = TimeSpan.FromSeconds(10),
+ }).Subscribe((response) =>
+ {
+ if (!taskCompleted)
+ {
+ taskCompleted = true;
+ taskItem.Dispose();
+ completionSource.SetResult(true);
+ }
+
+ _frameDispatcher.Push(response);
+
+ }, (ex) =>
+ {
+ if (!taskCompleted)
+ {
+ taskCompleted = true;
+ taskItem.Dispose();
+ completionSource.SetException(ex);
+ }
+ else if (InSession)
+ {
+ OnFailed(ex);
+ }
+
+ InSession = false;
+
+ //TODO: handle exception.
+ }, () =>
+ {
+ //This cannot happen!
+ InSession = false;
+ OnFrameReceived(null);
+ });
+
+ return completionSource.Task;
+ }
+ else
+ {
+ return Task.FromResult(true);
+ }
+ }
+
+ public async Task EndSession()
+ {
+ if (InSession)
+ {
+ using (NotificationProvider.PushTaskItem("Stopping remote desktop session..."))
+ {
+ await _machineProvider.MachineOperator.SendGenericRequest<StopRemoteDesktopSessionRequest, StopRemoteDesktopSessionResponse>(new StopRemoteDesktopSessionRequest());
+ InSession = false;
+ }
+ }
+ }
+
+ private void OnFailed(Exception exception)
+ {
+ InSession = false;
+ OnFrameReceived(null);
+
+ if (!(exception is TransporterDisconnectedException))
+ {
+ NotificationProvider.PushSnackbarItem(MessageType.Error, "Remote desktop session terminated unexpectedly.", true, null, TimeSpan.FromSeconds(5));
+ }
+ }
+
+ private void OnRemoteDesktopResponse(StartRemoteDesktopSessionResponse response)
+ {
+ if (!InSession) return; //To stop frame delivery immediately.
+
+ if (response.Packet == null)
+ {
+ _frameDispatcher.Interval = 1000 / response.FrameRate;
+ return; //This is the first message with empty bitmap and only the frame rate.
+ }
+
+ try
+ {
+ if (!response.Packet.IsPartial)
+ {
+ using (MemoryStream ms = new MemoryStream(response.Packet.Bitmap))
+ {
+ if (_currentFrame != null)
+ {
+ _currentFrame.Dispose();
+ }
+
+ _currentFrame = new RasterFrame(new System.Drawing.Bitmap(ms));
+ _frameSize = new Size(_currentFrame.Width, _currentFrame.Height);
+ }
+ }
+ else
+ {
+ using (MemoryStream ms = new MemoryStream(response.Packet.Bitmap))
+ {
+ var diffFrame = new RasterFrame(new System.Drawing.Bitmap(ms), response.Packet.PartialRegion.Left, response.Packet.PartialRegion.Top);
+ diffFrame.Apply(_currentFrame);
+ diffFrame.Dispose();
+ }
+ }
+
+ OnFrameReceived(_currentFrame.ToBitmapSource());
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error on remote desktop packet received: {ex.Message}");
+ }
+ }
+
+ protected virtual void OnFrameReceived(BitmapSource source)
+ {
+ FrameReceived?.Invoke(this, new DesktopFrameReceivedEventArgs()
+ {
+ Source = source,
+ });
+ }
+
+ public async void MouseDown(MouseButton button, Point location, Size viewSize)
+ {
+ if (!InSession || _frameSize == null) return;
+
+ await _machineProvider.MachineOperator.SendGenericRequest<MouseStateRequest, MouseStateResponse>(new MouseStateRequest()
+ {
+ Button = button,
+ EventType = MouseEventType.Down,
+ Location = TranslateLocation(location, viewSize)
+ });
+ }
+
+ public async void MouseUp(MouseButton button, Point location, Size viewSize)
+ {
+ if (!InSession || _frameSize == null) return;
+
+ await _machineProvider.MachineOperator.SendGenericRequest<MouseStateRequest, MouseStateResponse>(new MouseStateRequest()
+ {
+ Button = button,
+ EventType = MouseEventType.Up,
+ Location = TranslateLocation(location, viewSize)
+ });
+ }
+
+ public void MouseMove(Point location, Size viewSize)
+ {
+ if (!InSession || _frameSize == null) return;
+
+ if (_mouseMoveThread == null)
+ {
+ _mouseMovements = new ProducerConsumerQueue<MouseMovement>();
+ _mouseMoveThread = new Thread(async () =>
+ {
+ while (InSession)
+ {
+ MouseMovement movement = _mouseMovements.BlockDequeue();
+
+ while (_mouseMovements.Count > 0)
+ {
+ movement = _mouseMovements.BlockDequeue();
+ }
+
+ try
+ {
+ await _machineProvider.MachineOperator.SendGenericRequest<MouseStateRequest, MouseStateResponse>(new MouseStateRequest()
+ {
+ Button = MouseButton.Left,
+ EventType = MouseEventType.Move,
+ Location = TranslateLocation(movement.Location, movement.ViewSize)
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error on mouse move\n{ex.ToString()}");
+ }
+
+ Thread.Sleep(MouseMoveInterval);
+ }
+
+ _mouseMoveThread = null;
+ });
+
+ _mouseMoveThread.IsBackground = true;
+ _mouseMoveThread.Start();
+ }
+
+ _mouseMovements.BlockEnqueue(new MouseMovement()
+ {
+ Location = location,
+ ViewSize = viewSize,
+ });
+ }
+
+ public async void MouseDoubleClick(MouseButton button, Point location, Size viewSize)
+ {
+ if (!InSession || _frameSize == null) return;
+
+ await _machineProvider.MachineOperator.SendGenericRequest<MouseStateRequest, MouseStateResponse>(new MouseStateRequest()
+ {
+ Button = button,
+ EventType = MouseEventType.DoubleClick,
+ Location = TranslateLocation(location, viewSize)
+ });
+ }
+
+ private Point TranslateLocation(Point location, Size viewSize)
+ {
+ return new Point(
+ (location.X / viewSize.Width) * _frameSize.Value.Width,
+ (location.Y / viewSize.Height) * _frameSize.Value.Height);
+ }
+
+ public async void KeyboardDown(Key key, bool ctrlDown, bool shitDown, bool altDown)
+ {
+ if (!InSession || _frameSize == null) return;
+
+ await _machineProvider.MachineOperator.SendGenericRequest<KeyboardStateRequest, KeyboardStateResponse>(new KeyboardStateRequest()
+ {
+ EventType = KeyboardEventType.Down,
+ Key = key,
+ IsCtrlDown = ctrlDown,
+ IsShiftDown = shitDown,
+ IsAltDown = altDown,
+ });
+ }
+
+ public async void KeyboardUp(Key key, bool ctrlDown, bool shitDown, bool altDown)
+ {
+ if (!InSession || _frameSize == null) return;
+
+ await _machineProvider.MachineOperator.SendGenericRequest<KeyboardStateRequest, KeyboardStateResponse>(new KeyboardStateRequest()
+ {
+ EventType = KeyboardEventType.Up,
+ Key = key,
+ IsCtrlDown = ctrlDown,
+ IsShiftDown = shitDown,
+ IsAltDown = altDown,
+ });
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj b/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj
index 7b3e64ff3..04d0e12d6 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Tango.FSE.UI.csproj
@@ -65,6 +65,24 @@
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Reactive.Core, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\System.Reactive.Core.3.1.1\lib\net46\System.Reactive.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Reactive.Interfaces, Version=3.0.1000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\System.Reactive.Interfaces.3.1.1\lib\net45\System.Reactive.Interfaces.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Reactive.Linq, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\System.Reactive.Linq.3.1.1\lib\net46\System.Reactive.Linq.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Reactive.PlatformServices, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\System.Reactive.PlatformServices.3.1.1\lib\net46\System.Reactive.PlatformServices.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Reactive.Windows.Threading, Version=3.0.1000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
+ <HintPath>..\..\packages\System.Reactive.Windows.Threading.3.1.1\lib\net45\System.Reactive.Windows.Threading.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Windows" />
+ <Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\ControlzEx.3.0.2.4\lib\net45\System.Windows.Interactivity.dll</HintPath>
</Reference>
@@ -128,6 +146,7 @@
<DependentUpon>MachineConnectionPane.xaml</DependentUpon>
</Compile>
<Compile Include="Panes\MachineConnectionPaneVM.cs" />
+ <Compile Include="RemoteDesktop\DefaultRemoteDesktopProvider.cs" />
<Compile Include="Threading\DefaultDispatcherProvider.cs" />
<Compile Include="ViewModelLocator.cs" />
<Compile Include="ViewModels\AccountViewVM.cs" />
@@ -260,7 +279,9 @@
</None>
</ItemGroup>
<ItemGroup>
- <None Include="App.config" />
+ <None Include="App.config">
+ <SubType>Designer</SubType>
+ </None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\SideChains\Tango.AutoComplete\Tango.AutoComplete.csproj">
@@ -295,6 +316,10 @@
<Project>{e4927038-348d-4295-aaf4-861c58cb3943}</Project>
<Name>Tango.PMR</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\Tango.RemoteDesktop\Tango.RemoteDesktop.csproj">
+ <Project>{A78068D4-2061-4376-8EDE-583D8D880DEC}</Project>
+ <Name>Tango.RemoteDesktop</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Tango.Settings\Tango.Settings.csproj">
<Project>{d8f1ad85-526a-4f50-b6dc-d437af63d8d8}</Project>
<Name>Tango.Settings</Name>
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs
index fac3d7018..536a42ae6 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/ViewModelLocator.cs
@@ -14,6 +14,7 @@ using Tango.FSE.Common.Gateway;
using Tango.FSE.Common.Modules;
using Tango.FSE.Common.Navigation;
using Tango.FSE.Common.Notifications;
+using Tango.FSE.Common.RemoteDesktop;
using Tango.FSE.Common.Threading;
using Tango.FSE.Common.Web;
using Tango.FSE.UI.Authentication;
@@ -24,6 +25,7 @@ using Tango.FSE.UI.Gateway;
using Tango.FSE.UI.Modules;
using Tango.FSE.UI.Navigation;
using Tango.FSE.UI.Notifications;
+using Tango.FSE.UI.RemoteDesktop;
using Tango.FSE.UI.Threading;
using Tango.FSE.UI.ViewModels;
@@ -43,6 +45,7 @@ namespace Tango.FSE.UI
TangoIOC.Default.Unregister<IMachineProvider>();
TangoIOC.Default.Unregister<IFSEApplicationManager>();
TangoIOC.Default.Unregister<IDiagnosticsProvider>();
+ TangoIOC.Default.Unregister<IRemoteDesktopProvider>();
//TangoIOC.Default.Unregister<ExternalBridgeScanner>();
//TangoIOC.Default.Unregister<IDiagnosticsFrameProvider>();
//TangoIOC.Default.Unregister<IEventLogger>();
@@ -60,8 +63,8 @@ namespace Tango.FSE.UI
TangoIOC.Default.Register<FSEServicesContainer, FSEServicesContainer>();
TangoIOC.Default.Register<IFSEModuleLoader, DefaultFSEModuleLoader>();
TangoIOC.Default.Register<INavigationManager, DefaultNavigationManager>();
- //TangoIOC.Default.Register<IMachineProvider, DefaultMachineProvider>();
TangoIOC.Default.Register<IFSEApplicationManager, DefaultFSEApplicationManager>();
+ TangoIOC.Default.Register<IRemoteDesktopProvider, DefaultRemoteDesktopProvider>();
TangoIOC.Default.Register<MainWindowVM>();
TangoIOC.Default.Register<MainViewVM>();
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml
index 36c1cba6f..dca8e2740 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/Views/MainView.xaml
@@ -104,8 +104,8 @@
</material:PackIcon>
<commonControls:IconButton Visibility="{Binding CanClose,Converter={StaticResource BooleanToVisibilityConverter}}" Margin="0 -5 -3 0" Icon="Close" DockPanel.Dock="Right" Padding="8" Command="{Binding CloseCommand}" HorizontalAlignment="Right" VerticalAlignment="Top" Width="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"/>
<StackPanel Margin="10 8 0 0">
- <TextBlock Text="{Binding Title}" FontSize="{StaticResource FSE_SmallFontSize}"></TextBlock>
- <TextBlock Margin="0 2 0 0" Text="{Binding Message}" FontSize="{StaticResource FSE_SmallerFontSize}" Foreground="{StaticResource FSE_GrayBrush}"></TextBlock>
+ <TextBlock TextWrapping="Wrap" Text="{Binding Title}" FontSize="{StaticResource FSE_SmallFontSize}"></TextBlock>
+ <TextBlock TextWrapping="Wrap" Margin="0 2 0 0" Text="{Binding Message}" FontSize="{StaticResource FSE_SmallerFontSize}" Foreground="{StaticResource FSE_GrayBrush}"></TextBlock>
</StackPanel>
</DockPanel>
</DockPanel>
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config b/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config
index dd8c723e4..d17a56d7d 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/packages.config
@@ -7,4 +7,10 @@
<package id="MaterialDesignColors" version="1.2.2" targetFramework="net461" />
<package id="MaterialDesignThemes" version="3.0.1" targetFramework="net461" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
+ <package id="System.Reactive" version="3.1.1" targetFramework="net461" />
+ <package id="System.Reactive.Core" version="3.1.1" targetFramework="net461" />
+ <package id="System.Reactive.Interfaces" version="3.1.1" targetFramework="net461" />
+ <package id="System.Reactive.Linq" version="3.1.1" targetFramework="net461" />
+ <package id="System.Reactive.PlatformServices" version="3.1.1" targetFramework="net461" />
+ <package id="System.Reactive.Windows.Threading" version="3.1.1" targetFramework="net461" />
</packages> \ No newline at end of file
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs
index 67d0b85de..0d5bd8559 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/DefaultRemoteDesktopService.cs
@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Input;
using Tango.Core;
using Tango.Core.DI;
using Tango.Integration.ExternalBridge;
@@ -14,9 +15,11 @@ using Tango.RemoteDesktop.CaptureMethods;
using Tango.RemoteDesktop.Encoders;
using Tango.RemoteDesktop.Engines;
using Tango.RemoteDesktop.Frames;
+using Tango.RemoteDesktop.Input;
using Tango.RemoteDesktop.Network;
using Tango.Settings;
using Tango.Transport;
+using static Tango.RemoteDesktop.Input.MouseController;
namespace Tango.PPC.Common.RemoteDesktop
{
@@ -45,6 +48,8 @@ namespace Tango.PPC.Common.RemoteDesktop
_clients = new List<RemoteDesktopClient>();
_engine = new RasterScreenCaptureEngine();
+ _engine.CaptureCursor = false;
+
_engine.FrameRate = Math.Min(Math.Max(_settings.RemoteDesktopFrameRate, 1), 20);
_engine.FrameReceived += _engine_FrameReceived;
}
@@ -56,6 +61,16 @@ namespace Tango.PPC.Common.RemoteDesktop
_engine.CaptureMethod.Dispose();
var mainWindow = System.Windows.Application.Current.MainWindow;
+ mainWindow.LocationChanged += (_, __) =>
+ {
+ _engine.CaptureRegion = new CaptureRegion()
+ {
+ Left = (int)mainWindow.Left,
+ Top = (int)mainWindow.Top,
+ Width = (int)mainWindow.Width,
+ Height = (int)mainWindow.Height
+ };
+ };
_engine.CaptureRegion = new CaptureRegion()
{
@@ -68,11 +83,6 @@ namespace Tango.PPC.Common.RemoteDesktop
#endif
_engine.Comparer.MaxDifferencesThrow = _engine.CaptureRegion.Width * _engine.CaptureRegion.Height / 2;
-
- if (_settings.EnableRemoteDesktop)
- {
- Start();
- }
}
private bool _isStarted;
@@ -82,24 +92,6 @@ namespace Tango.PPC.Common.RemoteDesktop
private set { _isStarted = value; RaisePropertyChangedAuto(); }
}
- public void Start()
- {
- if (!IsStarted)
- {
- _engine.Start();
- IsStarted = true;
- }
- }
-
- public void Stop()
- {
- if (IsStarted)
- {
- _engine.Stop();
- IsStarted = false;
- }
- }
-
[ExternalBridgeRequestHandlerMethod(typeof(StartRemoteDesktopSessionRequest))]
public async void OnStartRemoteDesktopSessionRequestReceived(StartRemoteDesktopSessionRequest request, String token, ExternalBridgeReceiver receiver)
{
@@ -119,10 +111,85 @@ namespace Tango.PPC.Common.RemoteDesktop
await receiver.SendGenericResponse(new StartRemoteDesktopSessionResponse()
{
FrameRate = _engine.FrameRate
- }, token, new TransportResponseConfig()
+ }, token);
+
+ if (_settings.EnableRemoteDesktop)
{
- Immediate = false,
- });
+ if (!_engine.IsStarted)
+ {
+ _engine.Start();
+ }
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(StopRemoteDesktopSessionRequest))]
+ public async void OnStopRemoteDesktopSessionRequestReceived(StopRemoteDesktopSessionRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ var client = _clients.SingleOrDefault(x => x.Receiver == receiver);
+
+ if (client != null)
+ {
+ _clients.Remove(client);
+ }
+
+ if (_clients.Count == 0)
+ {
+ _engine.Stop();
+ }
+
+ await receiver.SendGenericResponse(new StopRemoteDesktopSessionResponse(), token);
+
+ if (client != null)
+ {
+ await receiver.SendGenericResponse(new StartRemoteDesktopSessionResponse(), client.Token, new TransportResponseConfig() { Completed = true });
+ }
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(MouseStateRequest))]
+ public async void OnMouseStateRequestReceived(MouseStateRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ MouseController.SetCursorPosition((int)request.Location.X, (int)request.Location.Y);
+
+ if (request.EventType == MouseEventType.Up || request.EventType == MouseEventType.Down)
+ {
+ MouseEventFlags flag = MouseEventFlags.LeftUp;
+
+ switch (request.EventType)
+ {
+ case MouseEventType.Down:
+ flag = request.Button == MouseButton.Right ? MouseEventFlags.RightDown : MouseEventFlags.LeftDown;
+ break;
+ case MouseEventType.Up:
+ flag = request.Button == MouseButton.Right ? MouseEventFlags.RightUp : MouseEventFlags.LeftUp;
+ break;
+ }
+
+ MouseController.MouseEvent(flag);
+ }
+ else if (request.EventType == MouseEventType.DoubleClick)
+ {
+ MouseController.MouseEvent(MouseEventFlags.LeftDown);
+ MouseController.MouseEvent(MouseEventFlags.LeftUp);
+ MouseController.MouseEvent(MouseEventFlags.LeftDown);
+ MouseController.MouseEvent(MouseEventFlags.LeftUp);
+ }
+
+ await receiver.SendGenericResponse(new MouseStateResponse(), token);
+ }
+
+ [ExternalBridgeRequestHandlerMethod(typeof(KeyboardStateRequest))]
+ public async void OnKeyboardStateRequestReceived(KeyboardStateRequest request, String token, ExternalBridgeReceiver receiver)
+ {
+ if (request.EventType == KeyboardEventType.Down)
+ {
+ KeyboardController.KeyDown(request.Key, request.IsCtrlDown, request.IsShiftDown, request.IsAltDown);
+ }
+ else
+ {
+ KeyboardController.KeyUp(request.Key, request.IsCtrlDown, request.IsShiftDown, request.IsAltDown);
+ }
+
+ await receiver.SendGenericResponse(new KeyboardStateResponse(), token);
}
private async void _engine_FrameReceived(object sender, ScreenCaptureFrameReceivedEventArgs<RasterFrame> e)
@@ -211,6 +278,11 @@ namespace Tango.PPC.Common.RemoteDesktop
{
_clients.Remove(client);
}
+
+ if (_clients.Count == 0)
+ {
+ _engine.Stop();
+ }
}
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs
index bce1bf51e..3c23c4832 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/RemoteDesktop/IRemoteDesktopService.cs
@@ -9,7 +9,5 @@ namespace Tango.PPC.Common.RemoteDesktop
public interface IRemoteDesktopService
{
bool IsStarted { get; }
- void Start();
- void Stop();
}
}
diff --git a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj
index 8014ac3f2..605ba53dc 100644
--- a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj
+++ b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj
@@ -98,7 +98,7 @@
<Compile Include="IO\KnownFolders.cs" />
<Compile Include="Json\ProtobufContractResolver.cs" />
<Compile Include="Threading\ActionTimer.cs" />
- <Compile Include="Threading\SequencerThread.cs" />
+ <Compile Include="Threading\IntervalMessageDispatcher.cs" />
<Compile Include="Threading\TaskSequencer.cs" />
<Compile Include="Threading\ThreadFactory.cs" />
<Compile Include="Threading\TimeoutTask.cs" />
@@ -206,7 +206,7 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
- <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" />
+ <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" />
</VisualStudio>
</ProjectExtensions>
<Import Project="..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.108.0\build\net46\System.Data.SQLite.Core.targets')" />
diff --git a/Software/Visual_Studio/Tango.Core/Threading/IntervalMessageDispatcher.cs b/Software/Visual_Studio/Tango.Core/Threading/IntervalMessageDispatcher.cs
new file mode 100644
index 000000000..ed61bea62
--- /dev/null
+++ b/Software/Visual_Studio/Tango.Core/Threading/IntervalMessageDispatcher.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Tango.Core.Threading
+{
+ /// <summary>
+ /// Represents a message queue dispatcher for delivering messages at a constant interval.
+ /// </summary>
+ /// <typeparam name="TMessage"></typeparam>
+ /// <seealso cref="System.IDisposable" />
+ public class IntervalMessageDispatcher<TMessage> : IDisposable
+ {
+ private Thread _queueThread;
+ private ProducerConsumerQueue<TMessage> _queue;
+ private Action<TMessage> _onNext;
+
+ /// <summary>
+ /// Gets or sets the interval.
+ /// </summary>
+ public int Interval { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance has started.
+ /// </summary>
+ public bool IsStarted { get; private set; }
+
+ /// <summary>
+ /// Gets the number of queued messages.
+ /// </summary>
+ public int Count
+ {
+ get { return _queue.Count; }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IntervalMessageDispatcher{T}"/> class.
+ /// </summary>
+ /// <param name="onNext">The delivery callback.</param>
+ public IntervalMessageDispatcher(Action<TMessage> onNext)
+ {
+ _onNext = onNext;
+ _queue = new ProducerConsumerQueue<TMessage>();
+ }
+
+ /// <summary>
+ /// Starts the dispatching of messages.
+ /// </summary>
+ public void Start()
+ {
+ if (!IsStarted)
+ {
+ IsStarted = true;
+ _queueThread = new Thread(QueueThreadMethod);
+ _queueThread.Name = "Sequencer Thread";
+ _queueThread.IsBackground = true;
+ _queueThread.Start();
+ }
+ }
+
+ /// <summary>
+ /// Pushes the specified message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ public void Push(TMessage message)
+ {
+ _queue.BlockEnqueue(message);
+ }
+
+ private void QueueThreadMethod()
+ {
+ Stopwatch watch = new Stopwatch();
+ watch.Start();
+
+ while (IsStarted)
+ {
+ watch.Restart();
+ var item = _queue.BlockDequeue();
+
+ if (!IsStarted) break;
+
+ try
+ {
+ _onNext?.Invoke(item);
+ }
+ catch { }
+
+ Thread.Sleep(Math.Max(1, (int)(Interval - watch.ElapsedMilliseconds)));
+ }
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ public void Dispose()
+ {
+ IsStarted = false;
+ }
+
+ /// <summary>
+ /// Creates a new instance of <see cref="IntervalMessageDispatcher{T}"/> and starts it immediately.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="onNext">The on next.</param>
+ /// <param name="interval">The interval.</param>
+ /// <returns></returns>
+ public static IntervalMessageDispatcher<T> StartNew<T>(Action<T> onNext, int interval)
+ {
+ IntervalMessageDispatcher<T> dispatcher = new IntervalMessageDispatcher<T>(onNext);
+ dispatcher.Interval = interval;
+ dispatcher.Start();
+ return dispatcher;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.Core/Threading/SequencerThread.cs b/Software/Visual_Studio/Tango.Core/Threading/SequencerThread.cs
deleted file mode 100644
index 603790466..000000000
--- a/Software/Visual_Studio/Tango.Core/Threading/SequencerThread.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Tango.Core.Threading
-{
- public class SequencerThread<T> : IDisposable
- {
- private Thread _queueThread;
- private ProducerConsumerQueue<T> _queue;
- private Action<T> _onNext;
-
- public int FrameRate { get; set; }
-
- public bool IsStarted { get; private set; }
-
- public SequencerThread(Action<T> onNext)
- {
- _onNext = onNext;
- _queue = new ProducerConsumerQueue<T>();
- }
-
- public void Start()
- {
- if (!IsStarted)
- {
- IsStarted = true;
- _queueThread = new Thread(QueueThreadMethod);
- _queueThread.Name = "Sequencer Thread";
- _queueThread.IsBackground = true;
- _queueThread.Start();
- }
- }
-
- public void Push(T item)
- {
- _queue.BlockEnqueue(item);
- }
-
- private void QueueThreadMethod()
- {
- Stopwatch watch = new Stopwatch();
- watch.Start();
-
- while (IsStarted)
- {
- watch.Restart();
- var item = _queue.BlockDequeue();
-
- if (!IsStarted) break;
-
- try
- {
- _onNext?.Invoke(item);
- }
- catch { }
-
- Thread.Sleep(Math.Max(1, (int)(FrameRate - watch.ElapsedMilliseconds)));
- }
- }
-
- public void Dispose()
- {
- IsStarted = false;
- }
- }
-}
diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs
index 28ebc5341..5b59429d7 100644
--- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs
+++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeTcpClient.cs
@@ -223,14 +223,16 @@ namespace Tango.Integration.ExternalBridge
try
{
var response = await SendRequest<ExternalBridgeLogoutRequest, ExternalBridgeLogoutResponse>(request, new TransportRequestConfig() { ShouldLog = true });
-
- Status = MachineStatuses.Standby;
}
catch { }
+
+ Status = MachineStatuses.Standby;
}
State = TransportComponentState.Disconnected;
+ NotifyContinuousRequestMessagesDisconnection();
+
SessionLogger.EndSession();
if (Adapter != null)
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Input/KeyboardController.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Input/KeyboardController.cs
new file mode 100644
index 000000000..832018dac
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Input/KeyboardController.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using WindowsInput;
+using WindowsInput.Native;
+
+namespace Tango.RemoteDesktop.Input
+{
+ public static class KeyboardController
+ {
+ private static InputSimulator simulator;
+
+ static KeyboardController()
+ {
+ simulator = new InputSimulator();
+ }
+
+ public static void KeyDown(Key key, bool ctrlDown, bool shitDown, bool altDown)
+ {
+ VirtualKeyCode virtualKey = (VirtualKeyCode)KeyInterop.VirtualKeyFromKey(key);
+
+ if (ctrlDown || shitDown || altDown)
+ {
+ List<VirtualKeyCode> modifierKeys = new List<VirtualKeyCode>();
+
+ if (ctrlDown)
+ {
+ modifierKeys.Add(VirtualKeyCode.LCONTROL);
+ }
+ if (shitDown)
+ {
+ modifierKeys.Add(VirtualKeyCode.LSHIFT);
+ }
+ if (altDown)
+ {
+ modifierKeys.Add(VirtualKeyCode.MENU);
+ }
+
+ simulator.Keyboard.ModifiedKeyStroke(modifierKeys, virtualKey);
+ }
+ else
+ {
+ simulator.Keyboard.KeyDown(virtualKey);
+ }
+ }
+
+ public static void KeyUp(Key key, bool ctrlDown, bool shitDown, bool altDown)
+ {
+ VirtualKeyCode virtualKey = (VirtualKeyCode)KeyInterop.VirtualKeyFromKey(key);
+
+ if (ctrlDown || shitDown || altDown)
+ {
+ List<VirtualKeyCode> modifierKeys = new List<VirtualKeyCode>();
+
+ if (ctrlDown)
+ {
+ modifierKeys.Add(VirtualKeyCode.LCONTROL);
+ }
+ if (shitDown)
+ {
+ modifierKeys.Add(VirtualKeyCode.LSHIFT);
+ }
+ if (altDown)
+ {
+ modifierKeys.Add(VirtualKeyCode.MENU);
+ }
+
+ simulator.Keyboard.ModifiedKeyStroke(modifierKeys, virtualKey);
+ }
+ else
+ {
+ simulator.Keyboard.KeyUp(virtualKey);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Input/MouseController.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Input/MouseController.cs
new file mode 100644
index 000000000..ec556f88c
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Input/MouseController.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Input
+{
+ public static class MouseController
+ {
+ [Flags]
+ public enum MouseEventFlags
+ {
+ LeftDown = 0x00000002,
+ LeftUp = 0x00000004,
+ MiddleDown = 0x00000020,
+ MiddleUp = 0x00000040,
+ Move = 0x00000001,
+ Absolute = 0x00008000,
+ RightDown = 0x00000008,
+ RightUp = 0x00000010
+ }
+
+ [DllImport("user32.dll", EntryPoint = "SetCursorPos")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool SetCursorPos(int x, int y);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool GetCursorPos(out MousePoint lpMousePoint);
+
+ [DllImport("user32.dll")]
+ private static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
+
+ public static void SetCursorPosition(int x, int y)
+ {
+ SetCursorPos(x, y);
+ }
+
+ public static void SetCursorPosition(MousePoint point)
+ {
+ SetCursorPos(point.X, point.Y);
+ }
+
+ public static MousePoint GetCursorPosition()
+ {
+ MousePoint currentMousePoint;
+ var gotPoint = GetCursorPos(out currentMousePoint);
+ if (!gotPoint) { currentMousePoint = new MousePoint(0, 0); }
+ return currentMousePoint;
+ }
+
+ public static void MouseEvent(MouseEventFlags value)
+ {
+ MousePoint position = GetCursorPosition();
+
+ mouse_event
+ ((int)value,
+ position.X,
+ position.Y,
+ 0,
+ 0)
+ ;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MousePoint
+ {
+ public int X;
+ public int Y;
+
+ public MousePoint(int x, int y)
+ {
+ X = x;
+ Y = y;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardEventType.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardEventType.cs
new file mode 100644
index 000000000..b1b5b0353
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardEventType.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public enum KeyboardEventType
+ {
+ Up,
+ Down
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardStateRequest.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardStateRequest.cs
new file mode 100644
index 000000000..27ae49f64
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardStateRequest.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public class KeyboardStateRequest
+ {
+ public KeyboardEventType EventType { get; set; }
+ public Key Key { get; set; }
+ public bool IsCtrlDown { get; set; }
+ public bool IsShiftDown { get; set; }
+ public bool IsAltDown { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardStateResponse.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardStateResponse.cs
new file mode 100644
index 000000000..6544078f0
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/KeyboardStateResponse.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public class KeyboardStateResponse
+ {
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseEventType.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseEventType.cs
new file mode 100644
index 000000000..2c9f84e03
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseEventType.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public enum MouseEventType
+ {
+ Down,
+ Up,
+ Move,
+ DoubleClick
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseStateRequest.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseStateRequest.cs
new file mode 100644
index 000000000..36e98351f
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseStateRequest.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public class MouseStateRequest
+ {
+ public MouseButton Button { get; set; }
+ public MouseEventType EventType { get; set; }
+ public Point Location { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseStateResponse.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseStateResponse.cs
new file mode 100644
index 000000000..f67715d99
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/MouseStateResponse.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public class MouseStateResponse
+ {
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/StopRemoteDesktopSessionRequest.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/StopRemoteDesktopSessionRequest.cs
new file mode 100644
index 000000000..058f5bbe1
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/StopRemoteDesktopSessionRequest.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public class StopRemoteDesktopSessionRequest
+ {
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Network/StopRemoteDesktopSessionResponse.cs b/Software/Visual_Studio/Tango.RemoteDesktop/Network/StopRemoteDesktopSessionResponse.cs
new file mode 100644
index 000000000..92a239d11
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Network/StopRemoteDesktopSessionResponse.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.RemoteDesktop.Network
+{
+ public class StopRemoteDesktopSessionResponse
+ {
+ }
+}
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs b/Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs
index 81efb6a30..8920b983b 100644
--- a/Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs
@@ -111,7 +111,6 @@ namespace Tango.RemoteDesktop
if (IsStarted)
{
IsStarted = false;
- _previousBitmap?.Dispose();
}
}
@@ -180,15 +179,20 @@ namespace Tango.RemoteDesktop
catch (Exception ex)
{
Debug.WriteLine($"Error in screen capture engine: {ex.Message}");
+ _previousBitmap = null;
}
int delay = Math.Max(5, (1000 / FrameRate) - (int)watch.ElapsedMilliseconds);
Thread.Sleep(delay);
}
+
+ _previousBitmap = null;
}
private void OnFrameReceived(Bitmap currentBitmap, TFrame diffFrame, uint differenceCount)
{
+ if (!IsStarted) return;
+
FrameReceived?.Invoke(this, new ScreenCaptureFrameReceivedEventArgs<TFrame>()
{
Frame = new ScreenCaptureFrame<TFrame>(currentBitmap, diffFrame)
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/Tango.RemoteDesktop.csproj b/Software/Visual_Studio/Tango.RemoteDesktop/Tango.RemoteDesktop.csproj
index b0feb438d..dfffe90ec 100644
--- a/Software/Visual_Studio/Tango.RemoteDesktop/Tango.RemoteDesktop.csproj
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/Tango.RemoteDesktop.csproj
@@ -66,6 +66,9 @@
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
+ <Reference Include="WindowsInput, Version=1.0.4.0, Culture=neutral, PublicKeyToken=9b287f7dc5073cad, processorArchitecture=MSIL">
+ <HintPath>..\packages\InputSimulator.1.0.4.0\lib\net20\WindowsInput.dll</HintPath>
+ </Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="BitmapComparerResult.cs" />
@@ -82,11 +85,21 @@
<Compile Include="Engines\RasterScreenCaptureEngine.cs" />
<Compile Include="Engines\VectorScreenCaptureEngine.cs" />
<Compile Include="Frames\VectorFrameColor.cs" />
+ <Compile Include="Input\KeyboardController.cs" />
+ <Compile Include="Input\MouseController.cs" />
<Compile Include="IScreenCaptureEngine.cs" />
<Compile Include="MaxDifferencesReachedException.cs" />
+ <Compile Include="Network\KeyboardEventType.cs" />
+ <Compile Include="Network\KeyboardStateRequest.cs" />
+ <Compile Include="Network\KeyboardStateResponse.cs" />
+ <Compile Include="Network\MouseEventType.cs" />
+ <Compile Include="Network\MouseStateRequest.cs" />
+ <Compile Include="Network\MouseStateResponse.cs" />
<Compile Include="Network\RemoteDesktopPacket.cs" />
<Compile Include="Network\StartRemoteDesktopSessionRequest.cs" />
<Compile Include="Network\StartRemoteDesktopSessionResponse.cs" />
+ <Compile Include="Network\StopRemoteDesktopSessionRequest.cs" />
+ <Compile Include="Network\StopRemoteDesktopSessionResponse.cs" />
<Compile Include="Quantization\ColorBgra.cs" />
<Compile Include="Quantization\OctreeQuantizer.cs" />
<Compile Include="Quantization\PaletteTable.cs" />
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/packages.config b/Software/Visual_Studio/Tango.RemoteDesktop/packages.config
index 2cbb3038a..67634c01e 100644
--- a/Software/Visual_Studio/Tango.RemoteDesktop/packages.config
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/packages.config
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
+ <package id="InputSimulator" version="1.0.4.0" targetFramework="net461" />
<package id="Quamotion.TurboJpegWrapper" version="1.5.69" targetFramework="net461" />
</packages> \ No newline at end of file
diff --git a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs
index f61e29795..45b89f9ff 100644
--- a/Software/Visual_Studio/Tango.Transport/TransporterBase.cs
+++ b/Software/Visual_Studio/Tango.Transport/TransporterBase.cs
@@ -21,6 +21,8 @@ using System.IO;
using Tango.Core.ExtensionMethods;
using Tango.PMR.Integration;
using Newtonsoft.Json;
+using System.Diagnostics;
+using System.Reactive.Concurrency;
namespace Tango.Transport
{
@@ -333,6 +335,7 @@ namespace Tango.Transport
{
try
{
+ _pendingRequests.Remove(request);
LogManager.Log($"Notifying continuous request '{(request.Message as ITangoMessage).Type}'...");
OnRequestFailed(request, new TransporterDisconnectedException("Transporter disconnected."));
request.SetException(new TransporterDisconnectedException("Transporter disconnected."));
@@ -908,8 +911,10 @@ namespace Tango.Transport
var responseObject = JsonConvert.DeserializeObject<Response>(response.Message.Data.ToStringUtf8());
subject.OnNext(responseObject);
}
- catch
+ catch (Exception ex)
{
+ Debugger.Break();
+ System.Diagnostics.Debug.WriteLine($"Exception thrown by the generic request continuous handler.\n{ex.ToString()}");
//Ignore exception at the client side.
}
@@ -920,8 +925,10 @@ namespace Tango.Transport
{
subject.OnError(ex);
}
- catch
+ catch (Exception xx)
{
+ Debugger.Break();
+ System.Diagnostics.Debug.WriteLine($"Exception thrown by the generic request error handler.\n{xx.ToString()}");
//Ignore exception at the client side.
}
@@ -931,8 +938,10 @@ namespace Tango.Transport
{
subject.OnCompleted();
}
- catch
+ catch (Exception ex)
{
+ Debugger.Break();
+ System.Diagnostics.Debug.WriteLine($"Exception thrown by the generic request completed handler.\n{ex.ToString()}");
//Ignore exception at the client side.
}
});
@@ -1052,7 +1061,7 @@ namespace Tango.Transport
{
config = config ?? new TransportResponseConfig();
- if (_pushThread == null || _pushThread.ThreadState == ThreadState.Aborted)
+ if (_pushThread == null || _pushThread.ThreadState == System.Threading.ThreadState.Aborted)
{
throw new InvalidOperationException("Transporter push thread is not in a running state.");
}