Refactoring, improvements, and more

Rafactored a reasonably large portion of the Remote Desktop. Added a few
interfaces, cleaned up some code, and multiple improvements to the
design.
Also fully implemented the new custom PictureBox control. Performance
gain is more noticable.
This commit is contained in:
yankejustin 2015-07-28 14:36:04 -04:00
parent d697e32c1e
commit 2d2a66b3d1
4 changed files with 234 additions and 89 deletions

View File

@ -16,7 +16,11 @@
if (disposing && (components != null))
{
components.Dispose();
// Stop running.
this.Stop();
}
base.Dispose(disposing);
}

View File

@ -1,21 +1,129 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using xServer.Core.Utilities;
namespace xServer.Controls
{
public partial class PictureBoxEx : PictureBox
public delegate void PictureSizeChangedEventHandler(int width, int height);
public class PictureSizeChangedEventArgs : EventArgs
{
public int NewWidth;
public int NewHeight;
public PictureSizeChangedEventArgs(int width, int height)
{
NewWidth = width;
NewHeight = height;
}
}
public interface IRapidPictureBox
{
bool Running { get; set; }
void Start();
void Stop();
void UpdateImage(Bitmap bmp, bool cloneBitmap = false);
Image _Image { get; set; }
}
/// <summary>
/// Custom PictureBox Control designed for rapidly-changing images.
/// </summary>
public partial class PictureBoxEx : PictureBox, IRapidPictureBox
{
#region IRapidPictureBox Implementation
public bool Running { get; set; }
public void Start()
{
_frameCounter = new FrameCounter();
_sWatch = Stopwatch.StartNew();
Running = true;
}
public void Stop()
{
if (_sWatch != null)
_sWatch.Stop();
Running = false;
}
public void UpdateImage(Bitmap bmp, bool cloneBitmap = false)
{
try
{
CountFps();
if ((bmpWidth != bmp.Width) && (bmpHeight != bmp.Height))
OnPictureSizeChanged(new PictureSizeChangedEventArgs(bmp.Width, bmp.Height));
lock (ImgLocker)
{
if (this._Image != null)
{
this._Image.Dispose();
this._Image = null;
}
this._Image = cloneBitmap ? new Bitmap(bmp, picDesktop.Width, picDesktop.Height) /*resize bitmap*/ : bmp;
}
}
catch (InvalidOperationException)
{
}
catch (Exception ex)
{
MessageBox.Show(
string.Format(
"An unexpected error occurred: {0}\n\nPlease report this as fast as possible here:\\https://github.com/MaxXor/xRAT/issues",
ex.Message), "", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
// Fields used to keep track of the remote desktop's size.
public int bmpWidth { get; private set; }
public int bmpHeight { get; private set; }
// Fields for the FrameCounter.
public FrameCounter _frameCounter;
private Stopwatch _sWatch;
public PictureBoxEx()
{
InitializeComponent();
//this.DoubleBuffered = true;
this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
_frameCounter = new FrameCounter();
}
#region Events
public event PictureSizeChangedEventHandler PictureSizeChanged;
protected virtual void OnPictureSizeChanged(PictureSizeChangedEventArgs e)
{
PictureSizeChangedEventHandler handler = PictureSizeChanged;
if (handler != null)
handler(e.NewWidth, e.NewHeight);
}
#endregion
#region Overrides
protected override CreateParams CreateParams
{
get
@ -28,7 +136,42 @@ namespace xServer.Controls
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
lock (ImgLocker)
{
if (this._Image != null)
{
pe.Graphics.DrawImage(this._Image, this.Location);
}
}
}
#endregion
private void CountFps()
{
var deltaTime = (float)_sWatch.Elapsed.TotalSeconds;
_sWatch = Stopwatch.StartNew();
_frameCounter.Update(deltaTime);
}
private readonly object ImgLocker = new object();
/// <summary>
/// Provides thread-safe access to the PictureBox's image.
/// </summary>
public Image _Image
{
get
{
return picDesktop.Image;
}
set
{
lock (ImgLocker)
{
picDesktop.Image = value;
}
}
}
}
}

View File

@ -1,22 +1,36 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
namespace xServer.Core.Utilities
{
public class FrameUpdatedEventArgs : EventArgs
{
public float CurrentFramesPerSecond { get; private set; }
public FrameUpdatedEventArgs(float _CurrentFramesPerSecond)
{
CurrentFramesPerSecond = _CurrentFramesPerSecond;
}
}
public delegate void FrameUpdatedEventHandler(FrameUpdatedEventArgs e);
public class FrameCounter
{
public long TotalFrames { get; private set; }
public float TotalSeconds { get; private set; }
public float AverageFramesPerSecond { get; private set; }
public float CurrentFramesPerSecond { get; private set; }
public const int MAXIMUM_SAMPLES = 100;
private Queue<float> _sampleBuffer = new Queue<float>();
public event FrameUpdatedEventHandler FrameUpdated;
public void Update(float deltaTime)
{
CurrentFramesPerSecond = 1.0f / deltaTime;
float CurrentFramesPerSecond = 1.0f / deltaTime;
_sampleBuffer.Enqueue(CurrentFramesPerSecond);
@ -30,8 +44,17 @@ namespace xServer.Core.Utilities
AverageFramesPerSecond = CurrentFramesPerSecond;
}
OnFrameUpdated(new FrameUpdatedEventArgs(AverageFramesPerSecond));
TotalFrames++;
TotalSeconds += deltaTime;
}
protected virtual void OnFrameUpdated(FrameUpdatedEventArgs e)
{
FrameUpdatedEventHandler handler = FrameUpdated;
if (handler != null)
handler(e);
}
}
}
}

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
@ -17,8 +16,7 @@ namespace xServer.Forms
private readonly Client _connectClient;
private bool _enableMouseInput;
private bool _started;
private FrameCounter _frameCounter;
private Stopwatch _sWatch;
private int _screenWidth;
private int _screenHeight;
@ -31,18 +29,20 @@ namespace xServer.Forms
_connectClient = c;
_connectClient.Value.FrmRdp = this;
InitializeComponent();
picDesktop.PictureSizeChanged += picDesktop_PictureSizeChanged;
}
private void FrmRemoteDesktop_Load(object sender, EventArgs e)
{
this.Text = WindowHelper.GetWindowTitle("Remote Desktop", _connectClient);
panelTop.Left = (this.Width/2) - (panelTop.Width/2);
panelTop.Left = (this.Width / 2) - (panelTop.Width / 2);
btnHide.Left = (panelTop.Width/2) - (btnHide.Width/2);
btnHide.Left = (panelTop.Width / 2) - (btnHide.Width / 2);
btnShow.Location = new Point(377, 0);
btnShow.Left = (this.Width/2) - (btnShow.Width/2);
btnShow.Left = (this.Width / 2) - (btnShow.Width / 2);
if (_connectClient.Value != null)
new Core.Packets.ServerPackets.GetMonitors().Execute(_connectClient);
@ -50,7 +50,7 @@ namespace xServer.Forms
public void ProcessScreens(object state)
{
while (true)
while (true && picDesktop != null && !picDesktop.IsDisposed && !picDesktop.Disposing)
{
GetDesktopResponse packet;
lock (ProcessingScreensQueue)
@ -68,21 +68,36 @@ namespace xServer.Forms
}
if (_connectClient.Value.StreamCodec == null)
_connectClient.Value.StreamCodec = new UnsafeStreamCodec(packet.Quality, packet.Monitor, packet.Resolution);
if (_connectClient.Value.StreamCodec.ImageQuality != packet.Quality || _connectClient.Value.StreamCodec.Monitor != packet.Monitor
|| _connectClient.Value.StreamCodec.Resolution != packet.Resolution)
{
if (_connectClient.Value.StreamCodec != null)
_connectClient.Value.StreamCodec = new UnsafeStreamCodec(packet.Quality, packet.Monitor, packet.Resolution);
}
else if (_connectClient.Value.StreamCodec.ImageQuality != packet.Quality || _connectClient.Value.StreamCodec.Monitor != packet.Monitor)
{
if (string.Compare(_connectClient.Value.StreamCodec.Resolution, packet.Resolution, StringComparison.InvariantCultureIgnoreCase) != 0)
{
_connectClient.Value.StreamCodec.Dispose();
}
_connectClient.Value.StreamCodec = new UnsafeStreamCodec(packet.Quality, packet.Monitor, packet.Resolution);
}
using (MemoryStream ms = new MemoryStream(packet.Image))
{
if (_connectClient.Value.FrmRdp != null)
_connectClient.Value.FrmRdp.UpdateImage(_connectClient.Value.StreamCodec.DecodeData(ms), true);
try
{
// Update the new image from the packet data.
picDesktop.UpdateImage(_connectClient.Value.StreamCodec.DecodeData(ms), true);
this.Invoke((MethodInvoker)delegate
{
if (picDesktop != null && !picDesktop.IsDisposed && picDesktop._Image != null)
{
picDesktop.Invalidate();
}
});
}
catch
{ }
}
packet.Image = null;
@ -93,7 +108,7 @@ namespace xServer.Forms
{
try
{
cbMonitors.Invoke((MethodInvoker) delegate
cbMonitors.Invoke((MethodInvoker)delegate
{
for (int i = 0; i < montiors; i++)
cbMonitors.Items.Add(string.Format("Monitor {0}", i + 1));
@ -112,73 +127,27 @@ namespace xServer.Forms
}
}
private void CountFps()
// Update on frame change.
private void _frameCounter_FrameUpdated(FrameUpdatedEventArgs e)
{
var deltaTime = (float)_sWatch.Elapsed.TotalSeconds;
_sWatch = Stopwatch.StartNew();
_frameCounter.Update(deltaTime);
UpdateFps(_frameCounter.AverageFramesPerSecond);
this.Invoke((MethodInvoker)delegate
{
this.Text = string.Format("{0} - FPS: {1}", WindowHelper.GetWindowTitle("Remote Desktop", _connectClient), e.CurrentFramesPerSecond.ToString("0.00"));
});
}
private void UpdateFps(float fps)
{
try
{
this.Invoke((MethodInvoker)delegate
{
this.Text = string.Format("{0} - FPS: {1}", WindowHelper.GetWindowTitle("Remote Desktop", _connectClient), fps.ToString("0.00"));
});
}
catch (InvalidOperationException)
{
}
}
private void UpdateScreenResolution(int width, int height)
private void picDesktop_PictureSizeChanged(int width, int height)
{
_screenWidth = width;
_screenHeight = height;
}
public void UpdateImage(Bitmap bmp, bool cloneBitmap = false)
{
try
{
CountFps();
UpdateScreenResolution(bmp.Width, bmp.Height);
picDesktop.Invoke((MethodInvoker) delegate
{
// get old image to dispose it correctly
var oldImage = picDesktop.Image;
picDesktop.SuspendLayout();
picDesktop.Image = cloneBitmap ? new Bitmap(bmp, picDesktop.Width, picDesktop.Height) /*resize bitmap*/ : bmp;
picDesktop.ResumeLayout();
if (oldImage != null)
oldImage.Dispose();
});
}
catch (InvalidOperationException)
{
}
catch (Exception ex)
{
MessageBox.Show(
string.Format(
"An unexpected error occurred: {0}\n\nPlease report this as fast as possible here:\\https://github.com/MaxXor/xRAT/issues",
ex.Message), "", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void ToggleControls(bool t)
{
_started = !t;
try
{
this.Invoke((MethodInvoker) delegate
this.Invoke((MethodInvoker)delegate
{
btnStart.Enabled = t;
btnStop.Enabled = !t;
@ -194,16 +163,16 @@ namespace xServer.Forms
{
if (_started)
new Core.Packets.ServerPackets.GetDesktop(0, 0, RemoteDesktopAction.Stop).Execute(_connectClient);
if (_sWatch != null)
_sWatch.Stop();
if (!picDesktop.IsDisposed && !picDesktop.Disposing)
picDesktop.Dispose();
if (_connectClient.Value != null)
_connectClient.Value.FrmRdp = null;
}
private void FrmRemoteDesktop_Resize(object sender, EventArgs e)
{
panelTop.Left = (this.Width/2) - (panelTop.Width/2);
btnShow.Left = (this.Width/2) - (btnShow.Width/2);
panelTop.Left = (this.Width / 2) - (panelTop.Width / 2);
btnShow.Left = (this.Width / 2) - (btnShow.Width / 2);
}
private void btnStart_Click(object sender, EventArgs e)
@ -215,11 +184,13 @@ namespace xServer.Forms
return;
}
_frameCounter = new FrameCounter();
_sWatch = Stopwatch.StartNew();
ToggleControls(false);
picDesktop.Start();
// Subscribe to the new frame counter.
picDesktop._frameCounter.FrameUpdated += _frameCounter_FrameUpdated;
new Core.Packets.ServerPackets.GetDesktop(barQuality.Value, cbMonitors.SelectedIndex, RemoteDesktopAction.Start).Execute(_connectClient);
}
@ -227,7 +198,11 @@ namespace xServer.Forms
{
new Core.Packets.ServerPackets.GetDesktop(0, 0, RemoteDesktopAction.Stop).Execute(_connectClient);
ToggleControls(true);
_sWatch.Stop();
picDesktop.Stop();
// Unsubscribe from the frame counter. It will be re-created when starting again.
picDesktop._frameCounter.FrameUpdated -= _frameCounter_FrameUpdated;
}
private void barQuality_Scroll(object sender, EventArgs e)