125 lines
5.2 KiB
C#
125 lines
5.2 KiB
C#
using RemoteFrameBuffer.Client;
|
|
using RemoteFrameBuffer.Common;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace RemoteFrameBuffer
|
|
{
|
|
public partial class RemoteFramebufferClient
|
|
{
|
|
private static Dictionary<RfbProtoVersion, IRemoteFramebufferClientProtocolFactory> _protocolHandlers;
|
|
|
|
private readonly IRemoteFrameBufferClientStreamProvider _socketProvider;
|
|
private Task? backgroundWorker;
|
|
private CancellationTokenSource cancellationTokenSource = new();
|
|
|
|
static RemoteFramebufferClient()
|
|
{
|
|
_protocolHandlers = new() {
|
|
{ new RfbProtoVersion(3, 3), new RemoteFrameBufferClientProtocolFactory<RemoteFrameBufferClientProtocol_3_3>() },
|
|
};
|
|
}
|
|
|
|
public Byte[] BitMap { get; protected set; } = [];
|
|
|
|
public RemoteFramebufferClient(IRemoteFrameBufferClientStreamProvider socketProvider)
|
|
{
|
|
this._socketProvider = socketProvider;
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
if (this.backgroundWorker == null)
|
|
{
|
|
Console.Error.WriteLine("VNC client starting");
|
|
this.backgroundWorker = new Task(this.Worker, this.cancellationTokenSource.Token);
|
|
this.backgroundWorker.ContinueWith(this.WorkerStopped);
|
|
this.backgroundWorker.Start();
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (this.backgroundWorker != null)
|
|
{
|
|
this.cancellationTokenSource.Cancel();
|
|
this.backgroundWorker.Wait();
|
|
}
|
|
}
|
|
|
|
private void Worker()
|
|
{
|
|
Int32 failcount = 0;
|
|
while (!this.cancellationTokenSource.Token.IsCancellationRequested)
|
|
{
|
|
Console.Error.WriteLine("Attempting to connect");
|
|
Stream s = this._socketProvider.GetRemoteFrameBufferClientStream();
|
|
using (s)
|
|
{
|
|
Byte[] buf = new Byte[12];
|
|
CancellationTokenSource readTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
|
CancellationTokenSource readTokenSource = CancellationTokenSource.CreateLinkedTokenSource(this.cancellationTokenSource.Token, readTimeout.Token);
|
|
try
|
|
{
|
|
s.ReadExactlyAsync(buf, 0, buf.Length, readTokenSource.Token).AsTask().Wait();
|
|
} catch (AggregateException e)// TODO: go through InnerExceptions and only throw if we are left with unhandled ones
|
|
{
|
|
if (e.InnerException is TaskCanceledException)
|
|
{
|
|
if (failcount++ >= 5)
|
|
{
|
|
break;
|
|
}
|
|
Task.Delay(1000).Wait();
|
|
continue;
|
|
} else
|
|
{
|
|
throw new Exception("Something broke. Yeah, I know, very useful message.", e);
|
|
}
|
|
}
|
|
failcount = 0;
|
|
String protover = Encoding.ASCII.GetString(buf);
|
|
Console.Error.Write($"Server protocol: {protover}");
|
|
Match m = _rfbVersionRegex().Match(protover);
|
|
if (!m.Success)
|
|
{
|
|
throw new Exception("Cannot parse protocol version");
|
|
}
|
|
RfbProtoVersion serverVersion = new(Int16.Parse(m.Groups["major"].Value), Int16.Parse(m.Groups["minor"].Value));
|
|
RfbProtoVersion[] supportedProto = _protocolHandlers.Keys.Where((ph) => ph <= serverVersion).ToArray();
|
|
if (supportedProto.Length == 0)
|
|
{
|
|
throw new NotSupportedException("No common protocol between client and server");
|
|
}
|
|
RfbProtoVersion maxProto = supportedProto.Max();
|
|
RemoteFrameBufferClientProtocol proto = _protocolHandlers[maxProto].Construct(s);
|
|
Console.Error.WriteLine($"Using client protocol {proto.Version.Major}.{proto.Version.Minor}");
|
|
// TODO: register auth callback and the like
|
|
proto.Handshake(this.cancellationTokenSource.Token);
|
|
proto.Initialization(this.cancellationTokenSource.Token);
|
|
proto.FrameUpdate += (bitmap) => {
|
|
Console.Error.WriteLine("####################################### CB hit");
|
|
this.BitMap = bitmap;
|
|
};
|
|
proto.StartListener(this.cancellationTokenSource.Token).Wait();
|
|
}
|
|
#warning REMOVE THIS
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void WorkerStopped(Task t)
|
|
{
|
|
Console.Error.WriteLine("VNC client exited");
|
|
this.backgroundWorker = null;
|
|
if (!this.cancellationTokenSource.TryReset())
|
|
{
|
|
this.cancellationTokenSource = new();
|
|
}
|
|
}
|
|
|
|
[GeneratedRegex(@"^RFB (?'major'\d{3}).(?'minor'\d{3})$")]
|
|
private static partial Regex _rfbVersionRegex();
|
|
}
|
|
}
|