view VNCServer/VNCServer.cs @ 111:df53bdd49507 default tip

Merge
author Ivo Smits <Ivo@UCIS.nl>
date Fri, 07 Nov 2014 18:37:39 +0100
parents 2ecb82eea559
children
line wrap: on
line source

???using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using UCIS.Net.HTTP;

namespace UCIS.VNCServer {
	public class VNCServerManager : IFramebuffer {
		private VNCFramebuffer fb;
		private List<VNCServerConnection> clients = new List<VNCServerConnection>();
		private VNCServer server;

		public event MouseEventHandler MouseDown;
		public event MouseEventHandler MouseMove;
		public event MouseEventHandler MouseUp;
		public event KeyEventHandler KeyDown;
		public event KeyEventHandler KeyUp;

		public VNCServerManager(int w, int h) : this(w, h, 5901) { }
		public VNCServerManager(int w, int h, int p) {
			fb = new VNCFramebuffer(w, h);
			server = new VNCServer(p);
			server.ClientConnected += delegate(object sender, VNCClientConnectedEventArgs e) {
				e.Client.Framebuffer = fb;
				e.Client.MouseDown += MouseDown;
				e.Client.MouseMove += MouseMove;
				e.Client.MouseUp += MouseUp;
				e.Client.KeyDown += KeyDown;
				e.Client.KeyUp += KeyUp;
				e.Client.Disconnected += ClientDisconnected;
				lock (clients) clients.Add(e.Client);
			};
			server.Listen();
		}

		private void ClientDisconnected(Object sender, EventArgs e) {
			lock (clients) clients.Remove((VNCServerConnection)sender);
		}

		public void Close() {
			server.Close();
			foreach (VNCServerConnection c in clients.ToArray()) c.Close();
		}

		public int Width {
			get { return fb.Width; }
		}
		public int Height {
			get { return fb.Height; }
		}
		public void Clear() {
			fb.Clear();
		}
		public void DrawImage(Image image, Rectangle srcrect, Point dest) {
			fb.DrawImage(image, srcrect, dest);
		}
		public void DrawPixels(int[] bitmap, int bmwidth, Rectangle srcrect, Point dest) {
			fb.DrawPixels(bitmap, bmwidth, srcrect, dest);
		}
		public void DrawPixels(IntPtr bitmap, int bmwidth, Rectangle srcrect, Point dest) {
			fb.DrawPixels(bitmap, bmwidth, srcrect, dest);
		}
		public void CopyRectangle(Rectangle srcrect, Point dest) {
			fb.CopyRectangle(srcrect, dest);
		}
		public void CopyRectangleTo(Rectangle srcrect, IFramebuffer destbuffer, Point destposition) {
			fb.CopyRectangleTo(srcrect, destbuffer, destposition);
		}

		public void Resize(int w, int h) {
			fb = new VNCFramebuffer(w, h);
			foreach (VNCServerConnection c in clients) c.Framebuffer = fb;
		}
	}
	public class VNCFramebufferUpdateEventArgs : EventArgs {
		public VNCFramebuffer Framebuffer { get; private set; }
		public Rectangle Area { get; private set; }
		internal VNCFramebufferUpdateEventArgs(VNCFramebuffer fb, Rectangle a) {
			this.Framebuffer = fb;
			this.Area = a;
		}
	}
	public class VNCFramebuffer : IFramebuffer {
		public event EventHandler<VNCFramebufferUpdateEventArgs> Update;
		internal Int32[] Framebuffer { get; private set; }
		public int Width { get; private set; }
		public int Height { get; private set; }
		public VNCFramebuffer(int width, int height) {
			if (width <= 0) throw new ArgumentOutOfRangeException("width");
			if (height <= 0) throw new ArgumentOutOfRangeException("height");
			this.Width = width;
			this.Height = height;
			this.Framebuffer = new Int32[width * height];
		}
		public void Clear() {
			for (int i = 0; i < Width * Height; i++) Framebuffer[i] = 0;
			EventHandler<VNCFramebufferUpdateEventArgs> eh = Update;
			if (eh != null) eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(0, 0, Width, Height)));
		}
		public unsafe void DrawImage(Image image, Rectangle srcrect, Point dest) {
			if (srcrect.Width == 0 || srcrect.Height == 0) return;
			fixed (int* fbptr = Framebuffer) {
				using (Bitmap b = new Bitmap(Width, Height, Width * 4, PixelFormat.Format32bppRgb, (IntPtr)fbptr)) {
					using (Graphics g = Graphics.FromImage(b)) {
						g.CompositingMode = CompositingMode.SourceCopy;
						g.DrawImage(image, new Rectangle(dest, srcrect.Size), srcrect, GraphicsUnit.Pixel);
					}
				}
			}
			EventHandler<VNCFramebufferUpdateEventArgs> eh = Update;
			if (eh != null) eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(dest, srcrect.Size)));
		}
		public void DrawBitmap(Bitmap bitmap, Rectangle srcrect, Point dest) {
			DrawImage(bitmap, srcrect, dest);
		}
		public unsafe void DrawPixels(int[] bitmap, int bmwidth, Rectangle srcrect, Point dest) {
			fixed (int* bmp = bitmap) DrawPixels(bmp, bmwidth, srcrect, dest);
		}
		public unsafe void DrawPixels(IntPtr bitmap, int bmwidth, Rectangle srcrect, Point dest) {
			DrawPixels((int*)bitmap, bmwidth, srcrect, dest);
		}
		public unsafe void DrawPixels(int* bitmap, int bmwidth, Rectangle srcrect, Point dest) {
			if (srcrect.X < 0 || srcrect.Y < 0 || srcrect.Width < 0 || srcrect.Height < 0) throw new ArgumentOutOfRangeException("srcrect");
			if (dest.X < 0 || dest.Y < 0 || dest.X + srcrect.Width > Width || dest.Y + srcrect.Height > Height) throw new ArgumentOutOfRangeException("dest");
			if (srcrect.Width == 0 || srcrect.Height == 0) return;
			int* bmin = bitmap + srcrect.Y * bmwidth + srcrect.X;
			//Optionally detect regions that have actually changed. This produces many small regions which may slow down the Tight JPEG encoder (and ZLib based codecs)
			//DrawChangedPixels(Framebuffer, dest.Y * Width + dest.X, bmin, srcrect.Width, srcrect.Height, bmwidth, dest.X, dest.Y);
			//return;
			fixed (int* fbptr = Framebuffer) {
				int* bmout = fbptr + dest.Y * Width + dest.X;
				for (int y = 0; y < srcrect.Height; y++) {
					int* bminl = bmin + y * bmwidth;
					int* bmoutl = bmout + y * Width;
					for (int x = 0; x < srcrect.Width; x++) {
						bmoutl[x] = bminl[x];
					}
				}
			}
			EventHandler<VNCFramebufferUpdateEventArgs> eh = Update;
			if (eh != null) eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(dest, srcrect.Size)));
		}
		private unsafe void DrawChangedPixels(int[] shadow, int shadowoffset, int* pixels, int width, int height, int bmwidth, int xoffset, int yoffset) {
			EventHandler<VNCFramebufferUpdateEventArgs> eh = Update;
			if (eh == null) return;
			int firstx = -1, lastx = -1, firsty = -1, lasty = -1;
			for (int y = 0; y < height; y++) {
				int firstxline = -1, lastxline = -1;
				for (int x = 0; x < width; x++) {
					if (shadow[shadowoffset] != *pixels) {
						if (firstxline == -1) firstxline = x;
						lastxline = x;
						shadow[shadowoffset] = *pixels;
					}
					shadowoffset++;
					pixels++;
				}
				shadowoffset += Width - width;
				pixels += bmwidth - width;
				if (firsty != -1 && firstxline == -1) {
					eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(firstx + xoffset, firsty + yoffset, lastx - firstx + 1, lasty - firsty + 1)));
					firsty = lasty = -1;
				} else if (firstxline != -1) {
					if (firsty == -1) {
						firsty = y;
						firstx = firstxline;
						lastx = lastxline;
					} else {
						if (firstxline < firstx) firstx = firstxline;
						if (lastxline > lastx) lastx = lastxline;
					}
					lasty = y;
				}
			}
			if (firsty != -1) {
				eh(this, new VNCFramebufferUpdateEventArgs(this, new Rectangle(firstx + xoffset, firsty + yoffset, lastx - firstx + 1, lasty - firsty + 1)));
			}
		}
		public void CopyRectangle(Rectangle srcrect, Point dest) {
			DrawPixels(Framebuffer, Width, srcrect, dest);
		}
		public void CopyRectangleTo(Rectangle srcrect, IFramebuffer destbuffer, Point destposition) {
			destbuffer.DrawPixels(Framebuffer, Width, srcrect, destposition);
		}
	}
	struct RFBPixelFormat {
		public Byte BitsPerPixel { get; set; }
		public Byte ColorDepth { get; set; }
		public Boolean BigEndian { get; set; }
		public Boolean TrueColor { get; set; }
		public UInt16 RedMax { get; set; }
		public UInt16 GreenMax { get; set; }
		public UInt16 BlueMax { get; set; }
		public Byte RedShift { get; set; }
		public Byte GreenShift { get; set; }
		public Byte BlueShift { get; set; }
	}
	public class VNCClientConnectedEventArgs : EventArgs {
		public VNCServer Server { get; private set; }
		public EndPoint RemoteEndPoint { get; private set; }
		public VNCServerConnection Client { get; private set; }
		public Boolean Drop { get; set; }
		public Boolean AllowNoAuthentication { get; set; }
		public Boolean AllowPasswordAuthentication { get; set; }
		public VNCClientConnectedEventArgs(VNCServer serv, VNCServerConnection c, EndPoint ep) {
			this.Server = serv;
			this.RemoteEndPoint = ep;
			this.Client = c;
			this.Drop = false;
			this.AllowNoAuthentication = true;
			this.AllowPasswordAuthentication = false;
		}
	}
	public class VNCClientAuthenticationEventArgs : EventArgs {
		public VNCServerConnection Client { get; private set; }
		public Boolean Drop { get; set; }
		public Boolean UsedPasswordAuthentication { get; internal set; }
		public String DesktopName { get; set; }
		internal Byte[] VNCAuthChallenge { private get; set; }
		internal Byte[] VNCAuthResponse { private get; set; }
		internal VNCClientAuthenticationEventArgs(VNCServerConnection c) {
			this.Client = c;
			this.Drop = false;
		}
		public Boolean CheckPassword(String password) {
			return CheckPassword(Encoding.ASCII.GetBytes(password));
		}
		public Boolean CheckPassword(Byte[] password) {
			Byte[] passwordtransform = new Byte[8];
			for (int i = 0; i < 8 && i < password.Length; i++) {
				Byte a = password[i], b = 0;
				for (int j = 0; j < 8; j++) b |= (Byte)(((a >> (7 - j)) & 1) << j);
				passwordtransform[i] = b;
			}
			byte[] check;
			using (DES des = new DESCryptoServiceProvider()) {
				des.Mode = CipherMode.ECB;
				des.Padding = PaddingMode.None;
				using (ICryptoTransform transform = des.CreateEncryptor(passwordtransform, null)) {
					check = transform.TransformFinalBlock(VNCAuthChallenge, 0, 16);
				}
			}
			for (int i = 0; i < 16; i++) if (VNCAuthResponse[i] != check[i]) return false;
			return true;
		}
	}
	public class VNCServer : IHTTPContentProvider {
		public event EventHandler<VNCClientConnectedEventArgs> ClientConnected;
		public EndPoint LocalEndPoint { get; protected set; }
		public Boolean Listening { get; protected set; }
		private Socket socket = null;
		public VNCServer() : this(null) { }
		public VNCServer(int port) : this(new IPEndPoint(IPAddress.Any, port)) { }
		public VNCServer(EndPoint ep) {
			LocalEndPoint = ep;
		}
		public void Listen() {
			if (LocalEndPoint == null) throw new ArgumentNullException("LocalEndPoint");
			Listen(LocalEndPoint);
		}
		public void Listen(int port) {
			Listen(new IPEndPoint(IPAddress.Any, port));
		}
		public virtual void Listen(EndPoint ep) {
			if (Listening) throw new InvalidOperationException("The server is already listening");
			socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Unspecified);
			socket.Bind(ep);
			socket.Listen(5);
			LocalEndPoint = ep;
			Listening = true;
			socket.BeginAccept(AcceptCallback, socket);
		}
		public virtual void Close() {
			if (!Listening) throw new InvalidOperationException("The server is not listening");
			Listening = false;
			socket.Close();
		}
		private void AcceptCallback(IAsyncResult ar) {
			Socket socket = ar.AsyncState as Socket;
			try {
				Socket clientsock = socket.EndAccept(ar);
				if (clientsock.ProtocolType == ProtocolType.Tcp) clientsock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
				ClientAccepted(new VNCServerConnection(clientsock));
			} catch (SocketException ex) {
				Debug.WriteLine(ex);
			} catch (ObjectDisposedException ex) {
				Debug.WriteLine(ex);
			}
			if (Listening) socket.BeginAccept(AcceptCallback, socket);
		}
		void IHTTPContentProvider.ServeRequest(HTTPContext context) {
			WebSocketPacketStream stream = new WebSocketPacketStream(context);
			ClientAccepted(new VNCServerConnection(stream, context.Socket));
		}
		protected void ClientAccepted(VNCServerConnection client) {
			VNCClientConnectedEventArgs clientargs = new VNCClientConnectedEventArgs(this, client, client.RemoteEndPoint);
			if (ClientConnected != null) ClientConnected(this, clientargs);
			if (clientargs.Drop) {
				client.Close();
			} else {
				client.RunAsync(clientargs);
			}
		}
	}
	public class WebVNCServer : VNCServer {
		private HTTPServer httpserver;
		public WebVNCServer() : this(null) { }
		public WebVNCServer(int port) : this(new IPEndPoint(IPAddress.Any, port)) { }
		public WebVNCServer(EndPoint ep) : base(ep) {
			httpserver = new HTTPServer();
			httpserver.ContentProvider = this;
			httpserver.ServeFlashPolicyFile = true;
		}
		public override void Listen(EndPoint ep) {
			httpserver.Listen(ep);
		}
		public override void Close() {
			httpserver.Dispose();
		}
	}
	public interface IZLibCompressor {
		byte[] Compress(Byte[] data, int offset, int count);
	}
	public delegate IZLibCompressor ZLibCompressorFactory();
	public class VNCServerConnection {
		VNCFramebuffer framebuffer = null;
		List<Rectangle> dirtyrects = new List<Rectangle>();
		int waitingforupdate = 0;
		Rectangle waitingforupdaterect = Rectangle.Empty;
		Int32[] SupportedEncodings = new Int32[] { };
		MouseButtons mousebuttons = MouseButtons.None;
		Boolean resized = false;
		Thread worker = null;
		Keys modifierKeys = Keys.None;
		MemoryStream SendBuffer = null;
		Stream socket;
		Socket realsocket;
		Int32 protover;
		RFBPixelFormat pixelformat;
		int jpegCounter = 0;
		Rectangle blurryrect = Rectangle.Empty;
		int blurryrecoveryline = 0;
		Point mousePosition = new Point(-1, -1);
		ZLibCompressorFactory ZLibFactory = null;

		public int Width { get; private set; }
		public int Height { get; private set; }
		public Object Tag { get; set; }

		public EndPoint RemoteEndPoint { get { return realsocket == null ? null : realsocket.RemoteEndPoint; } }
		public VNCFramebuffer Framebuffer {
			get { return framebuffer; }
			set {
				if (framebuffer != null) {
					framebuffer.Update -= FBUpdate;
				}
				framebuffer = value;
				if (value == null) return;
				lock (dirtyrects) {
					resized = true;
					dirtyrects.Clear();
					framebuffer.Update += FBUpdate;
				}
				FBUpdate(this, new VNCFramebufferUpdateEventArgs(value, new Rectangle(0, 0, framebuffer.Width, framebuffer.Height)));
			}
		}

		public event MouseEventHandler MouseDown;
		public event MouseEventHandler MouseMove;
		public event MouseEventHandler MouseUp;
		public event KeyEventHandler KeyDown;
		public event KeyEventHandler KeyUp;

		public event EventHandler Disconnected;

		public event EventHandler UpdateRequested;
		public event EventHandler WaitingForUpdate;

		public event EventHandler<VNCClientAuthenticationEventArgs> ClientAuthentication;
		public event EventHandler ConnectionComplete;

		public VNCServerConnection(Socket client) : this(new NetworkStream(client, true), client) {
			realsocket.SendBufferSize = 1024 * 1024;
		}
		public VNCServerConnection(Stream client) : this(client, null) { }
		public VNCServerConnection(Stream client, Socket socket) {
			this.socket = client;
			this.realsocket = socket;
			pixelformat = new RFBPixelFormat() {
				BigEndian = false, BitsPerPixel = 32, ColorDepth = 24, TrueColor = true,
				BlueMax = 255, BlueShift = 0, GreenMax = 255, GreenShift = 8, RedMax = 255, RedShift = 16
			};
		}
		
		public void Close() {
			socket.Close();
		}
		
		public void RunAsync(VNCClientConnectedEventArgs args) {
			worker = new Thread(RunSafe);
			worker.Start(args);
		}
		private void RunSafe(Object state) {
			try {
				RunProc((VNCClientConnectedEventArgs)state);
			} catch (Exception ex) {
				Console.Error.WriteLine(ex);
			} finally {
				try { socket.Close(); } catch (Exception ex) { Console.Error.WriteLine(ex); }
				if (Disconnected != null) Disconnected(this, new EventArgs());
			}
		}
		private void RunProc(VNCClientConnectedEventArgs connargs) {
			Initialisation(connargs);
			ReceiveLoop();
		}

		private void Initialisation(VNCClientConnectedEventArgs connargs) {
			{
				Byte[] protovbuf = Encoding.ASCII.GetBytes("RFB 003.008\n");
				SendAll(protovbuf);
				FlushSendBuffer();
				protovbuf = ReceiveAll(12);
				String protovs = Encoding.ASCII.GetString(protovbuf);
				protover = int.Parse(protovs.Substring(4, 3)) * 1000 + int.Parse(protovs.Substring(8, 3));
			}
			//Console.WriteLine("Client protocol is {0} = {1}", protovs.TrimEnd('\n'), protover);
			if (protover < 3003) throw new InvalidOperationException("Unsupported protocol version");
			VNCClientAuthenticationEventArgs authargs = new VNCClientAuthenticationEventArgs(this);
			if (protover >= 3007) {
				if (connargs.AllowNoAuthentication && connargs.AllowPasswordAuthentication) {
					SendAll(new Byte[] { 2, 1, 2 }); //2 security types, no security, VNC security
				} else if (connargs.AllowNoAuthentication) {
					SendAll(new Byte[] { 1, 1 }); //1 security type, none security
				} else if (connargs.AllowPasswordAuthentication) {
					SendAll(new Byte[] { 1, 2 }); //1 security type, VNC security
				} else {
					SendAll(new Byte[] { 0 }); //no security types, drop connection
					throw new InvalidOperationException("No security types allowed");
				}
				FlushSendBuffer();
				Byte[] sectype = ReceiveAll(1);
				if (sectype[0] == 1 && connargs.AllowNoAuthentication) {
					authargs.UsedPasswordAuthentication = false;
				} else if (sectype[0] == 2 && connargs.AllowPasswordAuthentication) {
					authargs.UsedPasswordAuthentication = true;
				} else throw new InvalidOperationException("Unsupported security type");
			} else if (protover == 3003) {
				if (connargs.AllowNoAuthentication) {
					SendUInt32(1); //Security type is none
					authargs.UsedPasswordAuthentication = false;
				} else if (connargs.AllowPasswordAuthentication) {
					SendUInt32(2); //Security type is VNC security
					authargs.UsedPasswordAuthentication = true;
				} else {
					SendUInt32(0); //no security types, drop connection
					throw new InvalidOperationException("No security types allowed");
				}
				FlushSendBuffer();
			} else {
				throw new InvalidOperationException("Unsupported protocol version");
			}
			if (authargs.UsedPasswordAuthentication) {
				Byte[] challenge = new Byte[16];
				(new RNGCryptoServiceProvider()).GetBytes(challenge);
				SendAll(challenge);
				FlushSendBuffer();
				authargs.VNCAuthChallenge = challenge;
				authargs.VNCAuthResponse = ReceiveAll(16);
			}
			if (ClientAuthentication != null) ClientAuthentication(this, authargs);
			if (authargs.Drop) {
				SendUInt32(1); //Security not OK
				FlushSendBuffer();
				throw new Exception("Authentication rejected");
			}
			if (authargs.UsedPasswordAuthentication || protover >= 3008) SendUInt32(0); //Security OK
			FlushSendBuffer();
			Byte[] clientinit = ReceiveAll(1);
			//Console.WriteLine("Shared = {0}", clientinit[0]);
			{
				VNCFramebuffer fb = framebuffer;
				if (fb != null) {
					Width = fb.Width;
					Height = fb.Height;
				} else {
					Width = 1024;
					Height = 768;
				}
			}
			resized = false;
			SendUInt16((UInt16)Width);
			SendUInt16((UInt16)Height);
			SendPixelFormat(pixelformat);
			{
				Byte[] desknamestr;
				if (authargs.DesktopName == null) desknamestr = new Byte[0];
				else desknamestr = Encoding.ASCII.GetBytes(authargs.DesktopName);
				SendUInt32((UInt32)desknamestr.Length);
				SendAll(desknamestr);
			}
			FlushSendBuffer();
			if (ConnectionComplete != null) ConnectionComplete(this, new EventArgs());
		}

		private void ReceiveLoop() {
			while (true) {
				Byte mtype = ReceiveByte();
				switch (mtype) {
					case 0: //SetPixelFormat
						ReceiveMessageSetPixelFormat();
						break;
					case 2: //SetEncodings
						Byte[] b = ReceiveAll(3);
						b = ReceiveAll(4 * ((b[1] << 8) | b[2]));
						SupportedEncodings = new Int32[b.Length / 4];
						for (int i = 0; i < b.Length; i += 4) SupportedEncodings[i / 4] = (b[i] << 24) | (b[i + 1] << 16) | (b[i + 2] << 8) | b[i + 3];
						break;
					case 3: //FramebufferUpdateRequest
						b = ReceiveAll(9);
						Rectangle r = new Rectangle((b[1] << 8) | b[2], (b[3] << 8) | b[4], (b[5] << 8) | b[6], (b[7] << 8) | b[8]);
						SendQueuedRectangles(r, b[0] != 0);
						break;
					case 4: //KeyEvent
						ReceiveMessageKeyboard();
						break;
					case 5: //PointerEvent
						ReceiveMessageMouse();
						break;
					case 6: //ClientCutText
						b = ReceiveAll(7);
						ReceiveAll((b[3] << 24) | (b[4] << 16) | (b[5] << 8) | b[6]);
						break;
					default:
						throw new InvalidOperationException("Received unknown message type, synchronization is lost");
				}
			}
		}
		private void ReceiveMessageSetPixelFormat() {
			Byte[] b = ReceiveAll(3);
			pixelformat = ReceivePixelFormat();
			if (!pixelformat.TrueColor) {
				//I don't want to use a pallette, so I cheat by sending a 8bpp "true color" pallette
				pixelformat.TrueColor = true;
				pixelformat.BigEndian = false;
				pixelformat.RedShift = 0;
				pixelformat.RedMax = 3;
				pixelformat.GreenShift = 2;
				pixelformat.GreenMax = 7;
				pixelformat.BlueShift = 5;
				pixelformat.BlueMax = 7;
				lock (socket) {
					SendUInt8(1); //SetColourMapEntries
					SendUInt8(0); //Padding
					SendUInt16(0); //First colour
					SendUInt16(256); //Number of colours
					for (UInt16 blue = 0; blue < 256; blue += 32) {
						for (UInt16 green = 0; green < 256; green += 32) {
							for (UInt16 red = 0; red < 256; red += 64) {
								SendUInt16((UInt16)(red << 8));
								SendUInt16((UInt16)(green << 8));
								SendUInt16((UInt16)(blue << 8));
							}
						}
					}
					FlushSendBuffer();
				}
			}
		}
		private void ReceiveMessageKeyboard() {
			Byte[] b = ReceiveAll(7);
			Boolean down = b[0] != 0;
			uint key = (uint)((b[3] << 24) | (b[4] << 16) | (b[5] << 8) | b[6]);
			//Console.WriteLine("KeyEvent code=0x{0:x} down={1}", key, down);
			Keys keyval = Keys.None;
			//see: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h
			if (key >= 'A' && key <= 'Z') keyval = (Keys)(key + (int)Keys.A - 'A') | Keys.Shift;
			else if (key >= 'a' && key <= 'z') keyval = (Keys)(key + (int)Keys.A - 'a');
			else if (key >= '0' && key <= '9') keyval = (Keys)(key + (int)Keys.D0 - '0');
			else if (key >= 0xffb0 && key <= 0xffb9) keyval = (Keys)(key + (int)Keys.NumPad0 - 0xffb0);
			else if (key >= 0xffbe && key <= 0xffd5) keyval = (Keys)(key + (int)Keys.F1 - 0xffbe); //all the way to F35...
			else switch (key) {
					case 0xff08: keyval = Keys.Back; break;
					case 0xff09: keyval = Keys.Tab; break;
					case 0xff0a: keyval = Keys.LineFeed; break;
					case 0xff0b: keyval = Keys.Clear; break;
					case 0xff0d: keyval = Keys.Return; break;
					case 0xff13: keyval = Keys.Pause; break;
					case 0xff14: keyval = Keys.Scroll; break;
					case 0xff15: keyval = Keys.None; break; //Sys req
					case 0xff1b: keyval = Keys.Escape; break;
					case 0xffff: keyval = Keys.Delete; break;
					case 0xff50: keyval = Keys.Home; break;
					case 0xff51: keyval = Keys.Left; break;
					case 0xff52: keyval = Keys.Up; break;
					case 0xff53: keyval = Keys.Right; break;
					case 0xff54: keyval = Keys.Down; break;
					case 0xff55: keyval = Keys.PageUp; break;
					case 0xff56: keyval = Keys.PageDown; break;
					case 0xff57: keyval = Keys.End; break;
					case 0xff58: keyval = Keys.Home; break;
					case 0xff60: keyval = Keys.Select; break;
					case 0xff61: keyval = Keys.Print; break;
					case 0xff62: keyval = Keys.Execute; break;
					case 0xff63: keyval = Keys.Insert; break;
					case 0xff65: keyval = Keys.None; break; //Undo
					case 0xff66: keyval = Keys.None; break; //Redo
					case 0xff67: keyval = Keys.Apps; break;
					case 0xff68: keyval = Keys.BrowserSearch; break;
					case 0xff69: keyval = Keys.Cancel; break;
					case 0xff6a: keyval = Keys.Help; break;
					case 0xff6b: keyval = Keys.Pause; break;
					case 0xff7e: keyval = Keys.None; break; //Character set switch
					case 0xff7f: keyval = Keys.NumLock; break;
					case 0xff80: keyval = Keys.Space; break;
					case 0xff89: keyval = Keys.Tab; break;
					case 0xff8d: keyval = Keys.Enter; break;
					case 0xff91: keyval = Keys.F1; break;
					case 0xff92: keyval = Keys.F2; break;
					case 0xff93: keyval = Keys.F3; break;
					case 0xff94: keyval = Keys.F4; break;
					case 0xff95: keyval = Keys.Home; break;
					case 0xff96: keyval = Keys.Left; break;
					case 0xff97: keyval = Keys.Up; break;
					case 0xff98: keyval = Keys.Right; break;
					case 0xff99: keyval = Keys.Down; break;
					case 0xff9a: keyval = Keys.PageUp; break;
					case 0xff9b: keyval = Keys.PageDown; break;
					case 0xff9c: keyval = Keys.End; break;
					case 0xff9d: keyval = Keys.Home; break;
					case 0xff9e: keyval = Keys.Insert; break;
					case 0xff9f: keyval = Keys.Delete; break;
					case 0xffbd: keyval = Keys.None; break; //keypad equals
					case 0xffaa: keyval = Keys.Multiply; break;
					case 0xffab: keyval = Keys.Add; break;
					case 0xffac: keyval = Keys.Separator; break;
					case 0xffad: keyval = Keys.Subtract; break;
					case 0xffae: keyval = Keys.Decimal; break;
					case 0xffaf: keyval = Keys.Divide; break;
					case 0xffe1: keyval = Keys.LShiftKey; break;
					case 0xffe2: keyval = Keys.RShiftKey; break;
					case 0xffe3: keyval = Keys.LControlKey; break;
					case 0xffe4: keyval = Keys.RControlKey; break;
					case 0xffe5: keyval = Keys.CapsLock; break;
					case 0xffe6: keyval = Keys.CapsLock; break; //shift lock!?
					case 0xffe7: keyval = Keys.None; break; //Left meta!?
					case 0xffe8: keyval = Keys.None; break; //Right meta!?
					case 0xffe9: keyval = Keys.LMenu; break;
					case 0xffea: keyval = Keys.Menu; break; //right alt
					case 0xffeb: keyval = Keys.LWin; break;
					case 0xffec: keyval = Keys.RWin; break;
					case 0xffed: keyval = Keys.None; break; //Left hyper
					case 0xffee: keyval = Keys.None; break; //Right hyper
					//Some X11 specific stuff
					case 0x20: keyval = Keys.Space; break;
					case 0x21: keyval = Keys.D1 | Keys.Shift; break; //!
					case 0x22: keyval = Keys.OemQuotes | Keys.Shift; break; //double quotes
					case 0x23: keyval = Keys.D3 | Keys.Shift; break; //number sign? #
					case 0x24: keyval = Keys.D4 | Keys.Shift; break; //dollar
					case 0x25: keyval = Keys.D5 | Keys.Shift; break; //percent
					case 0x26: keyval = Keys.D7 | Keys.Shift; break; //ampersand
					case 0x27: keyval = Keys.OemQuotes; break; //apostrophe
					case 0x28: keyval = Keys.D9 | Keys.Shift; break; //parenleft
					case 0x29: keyval = Keys.D0 | Keys.Shift; break; //parenright
					case 0x2a: keyval = Keys.D8 | Keys.Shift; break; //askerisk
					case 0x2b: keyval = Keys.Oemplus | Keys.Shift; break; //plus
					case 0x2c: keyval = Keys.Oemcomma; break; //comma
					case 0x2d: keyval = Keys.OemMinus; break; //minus
					case 0x2e: keyval = Keys.OemPeriod; break; //period
					case 0x2f: keyval = Keys.OemQuestion; break; //slash
					//digits handled above
					case 0x3a: keyval = Keys.OemSemicolon | Keys.Shift; break; //colon
					case 0x3b: keyval = Keys.OemSemicolon; break; //semicolon
					case 0x3c: keyval = Keys.Oemcomma | Keys.Shift; break; //less than
					case 0x3d: keyval = Keys.Oemplus; break; //equals
					case 0x3e: keyval = Keys.OemPeriod | Keys.Shift; break; //greater than
					case 0x3f: keyval = Keys.OemQuestion | Keys.Shift; break; //question mark
					case 0x40: keyval = Keys.D2 | Keys.Shift; break; //commercial at
					//capital letters handled above
					case 0x5b: keyval = Keys.OemOpenBrackets; break; //left square bracker
					case 0x5c: keyval = Keys.OemBackslash; break; //backslash
					case 0x5d: keyval = Keys.OemCloseBrackets; break; //right square bracker
					case 0x5e: keyval = Keys.D6 | Keys.Shift; break; //CIRCUMFLEX ACCENT
					case 0x5f: keyval = Keys.OemMinus | Keys.Shift; break; //underscore
					case 0x60: keyval = Keys.Oemtilde; break; //grave accent
					//small letters handled above
					case 0x7b: keyval = Keys.OemOpenBrackets | Keys.Shift; break; //left curly bracket
					case 0x7c: keyval = Keys.OemBackslash | Keys.Shift; break; //vertical line
					case 0x7d: keyval = Keys.OemCloseBrackets | Keys.Shift; break; //right curly bracket
					case 0x7e: keyval = Keys.Oemtilde | Keys.Shift; break; //tilde
					//blah blah
					//experimental:
					case 0xfe03: keyval = Keys.RMenu; break; //Alt gr or XK_ISO_Level3_Shift
				}
			switch (keyval) {
				case Keys.LShiftKey:
				case Keys.RShiftKey:
				case Keys.ShiftKey:
					if (down) modifierKeys |= Keys.Shift; else modifierKeys &= ~Keys.Shift;
					break;
				case Keys.LControlKey:
				case Keys.RControlKey:
				case Keys.ControlKey:
					if (down) modifierKeys |= Keys.Control; else modifierKeys &= ~Keys.Control;
					break;
				case Keys.LMenu:
				case Keys.RMenu:
				case Keys.Menu:
					if (down) modifierKeys |= Keys.Alt; else modifierKeys &= ~Keys.Alt;
					break;
			}
			keyval |= modifierKeys;
			if (down && KeyDown != null) KeyDown(this, new KeyEventArgs(keyval));
			else if (!down && KeyUp != null) KeyUp(this, new KeyEventArgs(keyval));
		}
		private void ReceiveMessageMouse() {
			Byte[] b = ReceiveAll(5);
			Point p = new Point((b[1] << 8) | b[2], (b[3] << 8) | b[4]);
			MouseButtons mb = MouseButtons.None;
			if ((b[0] & 1) != 0) mb |= MouseButtons.Left;
			if ((b[0] & 2) != 0) mb |= MouseButtons.Middle;
			if ((b[0] & 4) != 0) mb |= MouseButtons.Right;
			if ((b[0] & 32) != 0) mb |= MouseButtons.XButton1;
			if ((b[0] & 64) != 0) mb |= MouseButtons.XButton2;
			int dd = 0;
			if ((b[0] & 8) != 0) dd = 120;
			if ((b[0] & 16) != 0) dd = -120;
			//Console.WriteLine("PointerEvent x={0} y={1} buttons={2} delta={3}", p.X, p.Y, mb, dd);
			foreach (MouseButtons mbi in Enum.GetValues(typeof(MouseButtons))) if ((mousebuttons & mbi) != 0 && (mb & mbi) == 0) if (MouseUp != null) MouseUp(this, new MouseEventArgs(mbi, 0, p.X, p.Y, 0));
			if ((dd != 0 || mousePosition != p) && MouseMove != null) MouseMove(this, new MouseEventArgs(mb & mousebuttons, 0, p.X, p.Y, dd));
			foreach (MouseButtons mbi in Enum.GetValues(typeof(MouseButtons))) if ((mousebuttons & mbi) == 0 && (mb & mbi) != 0) if (MouseDown != null) MouseDown(this, new MouseEventArgs(mbi, 0, p.X, p.Y, 0));
			mousePosition = p;
			mousebuttons = mb;
		}

		private void FBUpdate(Object sender, VNCFramebufferUpdateEventArgs e) {
			if (e.Framebuffer != framebuffer) return;
			Rectangle r = e.Area;
			r.Intersect(new Rectangle(0, 0, e.Framebuffer.Width, e.Framebuffer.Height));
			if (r.Width == 0 || r.Height == 0) return;
			Boolean send;
			lock (dirtyrects) {
				for (int i = 0; i < dirtyrects.Count; i++) {
					Rectangle r2 = dirtyrects[i];
					if (r.IntersectsWith(r2)) {
						dirtyrects.RemoveAt(i);
						i = -1;
						r = Rectangle.Union(r, r2);
					}
				}
				dirtyrects.Add(r);
				send = waitingforupdate > 0;
			}
			if (send) {
				Interlocked.Decrement(ref waitingforupdate);
				SendQueuedRectangles(waitingforupdaterect, false);
			}
		}

		private void ClearIntersectingQueuedRectangles(Rectangle r) {
			lock (dirtyrects) {
				for (int i = 0; i < dirtyrects.Count; i++) {
					Rectangle r2 = dirtyrects[i];
					if (!r2.IntersectsWith(r)) continue;
					dirtyrects.RemoveAt(i);
					i--;
				}
				dirtyrects.TrimExcess();
			}
		}
		private void SendQueuedRectangles(Rectangle r, Boolean incremental) {
			lock (socket) {
				if (resized) {
					resized = false;
					jpegCounter = 0;
					VNCFramebuffer fb = framebuffer;
					SendUInt8(0); //FramebufferUpdate
					SendUInt8(0); //Padding
					if (Array.IndexOf(SupportedEncodings, -223) == -1) {
						Console.Error.WriteLine("VNC: Desktop size update not supported");
						SendUInt16(fb == null ? (UInt16)1 : (UInt16)2); //Number of rectangles
						int[] empty = new int[r.Width * r.Height];
						SendFBRectangle(empty, new Rectangle(0, 0, r.Width, r.Height), r.Width);
						ClearIntersectingQueuedRectangles(r);
						blurryrect = Rectangle.Empty;
						if (fb != null) SendFBRectangle(fb.Framebuffer, r, fb.Width);
					} else {
						if (fb != null) {
							Width = fb.Width;
							Height = fb.Height;
						}
						Console.Error.WriteLine("VNC: Sending desktop size update {0}x{1}", Width, Height);
						SendUInt16(1); //Number of rectangles
						SendUInt16(0);
						SendUInt16(0);
						SendUInt16((UInt16)Width);
						SendUInt16((UInt16)Height);
						SendUInt32(unchecked((UInt32)(-223))); //Encoding type
					}
					FlushSendBuffer();
				} else {
					if (UpdateRequested != null) UpdateRequested(this, new EventArgs());
					VNCFramebuffer fb = framebuffer;
					if (!incremental) {
						jpegCounter = 0;
						ClearIntersectingQueuedRectangles(r);
						blurryrect = Rectangle.Empty;
						SendUInt8(0); //FramebufferUpdate
						SendUInt8(0); //Padding
						SendUInt16(1); //Number of rectangles
						SendFBRectangle(fb.Framebuffer, r, fb.Width);
					} else { //incremental
						List<Rectangle> sending = new List<Rectangle>();
						lock (dirtyrects) {
							if (dirtyrects.Count == 0) {
								if (jpegCounter > 0) jpegCounter--;
							} else {
								if (jpegCounter < 10) jpegCounter++;
							}
							for (int i = 0; i < dirtyrects.Count; i++) {
								Rectangle r2 = dirtyrects[i];
								if (!r2.IntersectsWith(r)) continue;
								dirtyrects.RemoveAt(i);
								i--;
								r2.Intersect(r);
								sending.Add(r2);
							}
							dirtyrects.TrimExcess();
							if (sending.Count == 0 && blurryrect.IsEmpty) {
								Interlocked.Increment(ref waitingforupdate);
								waitingforupdaterect = r;
							}
						}
						if (sending.Count > 0 || !blurryrect.IsEmpty) {
							SendUInt8(0); //FramebufferUpdate
							SendUInt8(0); //Padding
							SendUInt16((UInt16)(sending.Count + (blurryrect.IsEmpty ? 0 : 1))); //Number of rectangles
							if (!blurryrect.IsEmpty) {
								//The idea here is to use a lossless compression for a small area to "recover" textual/static content from the JPEG artifacts
								//Only a small area is updated here each time because compressing a full frame takes too much CPU time
								Rectangle fixrect = blurryrect;
								if (blurryrecoveryline < fixrect.Top) blurryrecoveryline = fixrect.Top;
								else if (blurryrecoveryline >= fixrect.Bottom) blurryrecoveryline = fixrect.Top;
								fixrect.Intersect(new Rectangle(0, blurryrecoveryline, Int16.MaxValue, 10));
								if (fixrect.IsEmpty) fixrect = blurryrect;
								int oldjpeg = jpegCounter;
								jpegCounter = 0;
								SendFBRectangle(fb.Framebuffer, Rectangle.Intersect(fixrect, r), fb.Width);
								jpegCounter = oldjpeg;
								blurryrecoveryline = fixrect.Bottom;
								if (fixrect.Top <= blurryrect.Top && fixrect.Bottom >= blurryrect.Top) {
									blurryrect.Intersect(new Rectangle(0, fixrect.Bottom, Int16.MaxValue, Int16.MaxValue));
								} else if (fixrect.Top <= blurryrect.Bottom && fixrect.Bottom >= blurryrect.Bottom) {
									blurryrect.Intersect(new Rectangle(0, 0, fixrect.Top, Int16.MaxValue));
								}
								if (blurryrect.Height == 0) blurryrect = Rectangle.Empty;
							}
							foreach (Rectangle r2 in sending) {
								SendFBRectangle(fb.Framebuffer, r2, fb.Width);
							}
						} else {
							if (WaitingForUpdate != null) WaitingForUpdate(this, new EventArgs());
						}
					}
				}
				FlushSendBuffer();
			}
		}

		private void SendFBRectangle(Int32[] fb, Rectangle r, Int32 fbwidth) {
			r.Intersect(new Rectangle(0, 0, fbwidth, fb.Length / fbwidth));
			r.Intersect(new Rectangle(0, 0, Width, Height));
			Boolean sent = false;
			foreach (Int32 enc in SupportedEncodings) {
				switch (enc) {
					case 0:
						SendFBRectangleRaw(fb, r, fbwidth); sent = true; break;
					case 2:
						if (r.Height < 8) break;
						SendFBRectangleRRE(fb, r, fbwidth); sent = true; break;
					case 5:
						if (r.Width < 16 || r.Height < 16) break;
						SendFBRectangleHextile(fb, r, fbwidth); sent = true; break;
					case 6:
						sent = SendFBRectangleZLib(fb, r, fbwidth);
						break;
					case 7:
						sent = SendFBRectangleTight(fb, r, fbwidth);
						break;
				}
				if (sent) break;
			}
			if (!sent) SendFBRectangleRaw(fb, r, fbwidth);
		}
		private void SendFBRectangleHeader(Rectangle r, UInt32 encoding) {
			SendUInt16((UInt16)r.X);
			SendUInt16((UInt16)r.Y);
			SendUInt16((UInt16)r.Width);
			SendUInt16((UInt16)r.Height);
			SendUInt32(encoding);
		}
		IZLibCompressor TightZLib = null;
		private unsafe Boolean SendFBRectangleTight(Int32[] framebuffer, Rectangle r, Int32 fbwidth) {
			if (jpegCounter > 3 && r.Width * r.Height > 100 * 100) {
				SendFBRectangleHeader(r, 7);
				SendUInt8(0x90); //Jpeg encoding
				MemoryStream jpeg = new MemoryStream();
				if (r.Width > 0 && r.Height > 0) {
					fixed (int* fbptr = framebuffer) {
						using (Bitmap bmp = new Bitmap(r.Width, r.Height, fbwidth * 4, PixelFormat.Format32bppRgb, (IntPtr)(fbptr + (r.Top * fbwidth + r.Left)))) {
							ImageCodecInfo ici = Array.Find(ImageCodecInfo.GetImageEncoders(), delegate(ImageCodecInfo item) { return item.FormatID == ImageFormat.Jpeg.Guid; });
							EncoderParameters ep = new EncoderParameters(1);
							ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 50L);
							bmp.Save(jpeg, ici, ep);
							//bmp.Save(jpeg, ImageFormat.Jpeg);
						}
					}
				}
				jpeg.Seek(0, SeekOrigin.Begin);
				int length = (int)jpeg.Length;
				Byte b1 = (Byte)(length & 0x7F);
				Byte b2 = (Byte)((length >> 7) & 0x7F);
				Byte b3 = (Byte)((length >> 14) & 0x7F);
				if (b3 != 0) b2 |= 0x80;
				if (b2 != 0) b1 |= 0x80;
				SendUInt8(b1);
				if (b2 != 0) SendUInt8(b2);
				if (b3 != 0) SendUInt8(b3);
				SendAll(jpeg.ToArray());
				blurryrect = blurryrect.IsEmpty ? r : Rectangle.Union(blurryrect, r);
			} else {
				if (TightZLib == null && ZLibFactory == null) return false;
				SendFBRectangleHeader(r, 7);
				SendUInt8(0x00); //Basic encoding, copy filter (raw), zlib stream 0
				Byte[] row;
				int i = 0;
				if (pixelformat.BitsPerPixel == 32 && pixelformat.ColorDepth == 24) {
					row = new Byte[r.Width * r.Height * 3];
					for (int y = r.Top; y < r.Bottom; y++) {
						for (int x = r.Left; x < r.Right; x++) {
							UInt32 pixel = (UInt32)framebuffer[y * fbwidth + x];
							row[i++] = (Byte)(pixel >> 16);
							row[i++] = (Byte)(pixel >> 8);
							row[i++] = (Byte)(pixel >> 0);
						}
					}
				} else {
					row = new Byte[r.Width * r.Height * pixelformat.BitsPerPixel / 8];
					for (int y = r.Top; y < r.Bottom; y++) {
						for (int x = r.Left; x < r.Right; x++) {
							UInt32 pixel = (UInt32)framebuffer[y * fbwidth + x];
							UInt32 encoded = 0;
							encoded |= ((((pixel >> 16) & 0xff) * (pixelformat.RedMax + 1u) / 256) & pixelformat.RedMax) << pixelformat.RedShift;
							encoded |= ((((pixel >> 8) & 0xff) * (pixelformat.GreenMax + 1u) / 256) & pixelformat.GreenMax) << pixelformat.GreenShift;
							encoded |= ((((pixel >> 0) & 0xff) * (pixelformat.BlueMax + 1u) / 256) & pixelformat.BlueMax) << pixelformat.BlueShift;
							if (pixelformat.BigEndian) {
								for (int b = pixelformat.BitsPerPixel - 8; b >= 0; b -= 8) row[i++] = (Byte)(encoded >> b);
							} else {
								for (int b = 0; b < pixelformat.BitsPerPixel; b += 8) row[i++] = (Byte)(encoded >> b);
							}
						}
					}
				}
				if (i >= 12) {
					if (TightZLib == null) TightZLib = ZLibFactory();
					Byte[] compressed = TightZLib.Compress(row, 0, i);
					int length = compressed.Length;
					Byte b1 = (Byte)(length & 0x7F);
					Byte b2 = (Byte)((length >> 7) & 0x7F);
					Byte b3 = (Byte)((length >> 14) & 0x7F);
					if (b3 != 0) b2 |= 0x80;
					if (b2 != 0) b1 |= 0x80;
					SendUInt8(b1);
					if (b2 != 0) SendUInt8(b2);
					if (b3 != 0) SendUInt8(b3);
					SendAll(compressed);
				} else {
					SendAll(row, 0, i);
				}
			}
			return true;
		}
		private void SendFBRectangleRaw(Int32[] framebuffer, Rectangle r, int fbwidth) {
			SendFBRectangleHeader(r, 0);
			for (int y = r.Top; y < r.Bottom; y++) {
				Byte[] row = new Byte[r.Width * pixelformat.BitsPerPixel / 8];
				int i = 0;
				for (int x = r.Left; x < r.Right; x++) {
					UInt32 pixel = (UInt32)framebuffer[y * fbwidth + x];
					UInt32 encoded = 0;
					encoded |= ((((pixel >> 16) & 0xff) * (pixelformat.RedMax + 1u) / 256) & pixelformat.RedMax) << pixelformat.RedShift;
					encoded |= ((((pixel >> 8) & 0xff) * (pixelformat.GreenMax + 1u) / 256) & pixelformat.GreenMax) << pixelformat.GreenShift;
					encoded |= ((((pixel >> 0) & 0xff) * (pixelformat.BlueMax + 1u) / 256) & pixelformat.BlueMax) << pixelformat.BlueShift;
					if (pixelformat.BigEndian) {
						for (int b = pixelformat.BitsPerPixel - 8; b >= 0; b -= 8) row[i++] = (Byte)(encoded >> b);
					} else {
						for (int b = 0; b < pixelformat.BitsPerPixel; b += 8) row[i++] = (Byte)(encoded >> b);
					}
				}
				SendAll(row);
			}
		}

		IZLibCompressor ZLibStream = null;
		private Boolean SendFBRectangleZLib(Int32[] framebuffer, Rectangle r, int fbwidth) {
			if (ZLibStream == null && ZLibFactory == null) return false;
			SendFBRectangleHeader(r, 6);
			Byte[] row = new Byte[r.Width * r.Height * pixelformat.BitsPerPixel / 8];
			int i = 0;
			for (int y = r.Top; y < r.Bottom; y++) {
				for (int x = r.Left; x < r.Right; x++) {
					UInt32 pixel = (UInt32)framebuffer[y * fbwidth + x];
					UInt32 encoded = 0;
					encoded |= ((((pixel >> 16) & 0xff) * (pixelformat.RedMax + 1u) / 256) & pixelformat.RedMax) << pixelformat.RedShift;
					encoded |= ((((pixel >> 8) & 0xff) * (pixelformat.GreenMax + 1u) / 256) & pixelformat.GreenMax) << pixelformat.GreenShift;
					encoded |= ((((pixel >> 0) & 0xff) * (pixelformat.BlueMax + 1u) / 256) & pixelformat.BlueMax) << pixelformat.BlueShift;
					if (pixelformat.BigEndian) {
						for (int b = pixelformat.BitsPerPixel - 8; b >= 0; b -= 8) row[i++] = (Byte)(encoded >> b);
					} else {
						for (int b = 0; b < pixelformat.BitsPerPixel; b += 8) row[i++] = (Byte)(encoded >> b);
					}
				}
			}
			if (ZLibStream == null) ZLibStream = ZLibFactory();
			Byte[] compressed = ZLibStream.Compress(row, 0, i);
			SendUInt32((UInt32)compressed.Length);
			SendAll(compressed);
			return true;
		}


		private void SendFBRectangleRRE(Int32[] framebuffer, Rectangle r, int fbwidth) {
			SendFBRectangleHeader(r, 2);
			int basecolor = framebuffer[r.Y * fbwidth + r.X];
			List<Rectangle> subrects = new List<Rectangle>();
			if (false) {
				for (int y = r.Top; y < r.Bottom; y++) {
					int color = basecolor;
					int runstart = r.Left;
					int runlength = 0;
					for (int x = r.Left; x < r.Right; x++) {
						int newcolor = framebuffer[y * fbwidth + x];
						if (color != newcolor) {
							if (color != basecolor && runlength > 0) {
								Rectangle r2 = new Rectangle(runstart, y, runlength, 1);
								if (r2.Y > 0 && framebuffer[(r2.Y - 1) * fbwidth + r2.X] == color) {
									Boolean hasadjacent = false;
									Rectangle adjacent = r2;
									foreach (Rectangle r3 in subrects) {
										if (r3.Left == r2.Left && r3.Width == r2.Width && r3.Top + r3.Height == r2.Top) {
											adjacent = r3;
											hasadjacent = true;
											break;
										}
									}
									if (hasadjacent) {
										subrects.Remove(adjacent);
										r2 = Rectangle.Union(r2, adjacent);
									}
								}
								subrects.Add(r2);
							}
							runstart = x;
							runlength = 0;
							color = newcolor;
						}
						runlength++;
					}
					if (color != basecolor && runlength > 0) subrects.Add(new Rectangle(runstart, y, runlength, 1));
				}
			} else {
				Queue<Rectangle> remaining = new Queue<Rectangle>();
				remaining.Enqueue(r);
				while (remaining.Count > 0) {
					Rectangle r2 = remaining.Dequeue();
					int color = framebuffer[r2.Y * fbwidth + r2.X];
					int rw = -1, rh = -1;
					for (int x = r2.Left; x < r2.Right && rw == -1; x++) {
						if (color != framebuffer[r2.Y * fbwidth + x]) rw = x - r2.Left;
					}
					if (rw == -1) rw = r2.Width;
					for (int y = r2.Top + 1; y < r2.Bottom && rh == -1; y++) {
						Boolean success = true;
						for (int x = r2.Left; x < r2.Left + rw && success; x++) {
							if (color != framebuffer[y * fbwidth + x]) success = false;
						}
						if (!success) rh = y - r2.Top;
					}
					if (rh == -1) rh = r2.Height;
					if (rw != 0 && rh != 0) subrects.Add(new Rectangle(r2.X, r2.Y, rw, rh));
					//if (r2.Width - rw > 0 && rh != 0) remaining.Enqueue(new Rectangle(r2.X + rw, r2.Y, r2.Width - rw, rh));
					if (r2.Height - rh > 0 && rw != 0) remaining.Enqueue(new Rectangle(r2.X, r2.Y + rh, rw, r2.Height - rh));
					//if (r2.Width - rw > 0 && r2.Height - rh > 0) remaining.Enqueue(new Rectangle(r2.X + rw, r2.Y + rh, r2.Width - rw, r2.Height - rh));
					//if (r2.Height - rh > 0) remaining.Enqueue(new Rectangle(r2.X, r2.Y + rh, r2.Width, r2.Height - rh));
					if (r2.Width - rw > 0) remaining.Enqueue(new Rectangle(r2.X + rw, r2.Y, r2.Width - rw, r2.Height));
				}
			}
			SendUInt32((UInt32)subrects.Count);
			SendPixel(basecolor);
			foreach (Rectangle r2 in subrects) {
				SendPixel(framebuffer[r2.Y * fbwidth + r2.X]);
				SendUInt16((UInt16)(r2.Left - r.Left));
				SendUInt16((UInt16)(r2.Top - r.Top));
				SendUInt16((UInt16)r2.Width);
				SendUInt16((UInt16)r2.Height);
			}
		}
		private void SendFBRectangleHextile(Int32[] framebuffer, Rectangle r, int fbwidth) {
			SendFBRectangleHeader(r, 5);
			const int hextileRaw = 1;
			const int hextileBgSpecified = 2;
			const int hextileFgSpecified = 4;
			const int hextileAnySubrects = 8;
			const int hextileSubrectsColoured = 16;
			int oldBg = 0, oldFg = 0;
			bool oldBgValid = false;
			bool oldFgValid = false;
			Rectangle t = new Rectangle();
			for (t.Y = r.Top; t.Top < r.Bottom; t.Y += 16) {
				t.Height = Math.Min(r.Bottom, t.Top + 16) - t.Top;
				for (t.X = r.Left; t.Left < r.Right; t.X += 16) {
					t.Width = Math.Min(r.Right, t.Left + 16) - t.Left;
					int tileType = 0;
					int bg = framebuffer[t.Y * fbwidth + t.X];
					int fg = 0;
					if (!oldBgValid || oldBg != bg) {
						tileType |= hextileBgSpecified;
						oldBg = bg;
						oldBgValid = true;
					}
					Boolean foundfg = false;
					Boolean foundcol = false;
					int subrects = 0;
					for (int y = t.Top; y < t.Bottom; y++) {
						int color = bg;
						int length = 0;
						for (int x = t.Left; x < t.Right; x++) {
							int pixel = framebuffer[y * fbwidth + x];
							if (pixel == bg) {
							} else if (!foundfg) {
								fg = pixel;
								foundfg = true;
							} else if (pixel == fg) {
							} else {
								foundcol = true;
							}
							if (color != pixel && length > 0) {
								if (color != bg) subrects++;
								length = 0;
							}
							length++;
							color = pixel;
						}
						if (length > 0 && color != bg) subrects++;
					}
					if (foundcol) {
						tileType |= hextileSubrectsColoured | hextileAnySubrects;
						oldFgValid = false;
					} else if (foundfg) {
						tileType |= hextileAnySubrects;
						if (!oldFgValid || oldFg != fg) {
							tileType |= hextileFgSpecified;
							oldFg = fg;
							oldFgValid = true;
						}
					}
					int encbytes = 0;
					if ((tileType & hextileBgSpecified) != 0) encbytes += pixelformat.BitsPerPixel / 8;
					if ((tileType & hextileFgSpecified) != 0) encbytes += pixelformat.BitsPerPixel / 8;
					if ((tileType & hextileAnySubrects) != 0) {
						int pertile = 2;
						if ((tileType & hextileSubrectsColoured) != 0) pertile += pixelformat.BitsPerPixel / 8;
						encbytes += pertile * subrects;
					}
					if (t.Width * t.Height * pixelformat.BitsPerPixel / 8 <= encbytes) {
						SendUInt8(hextileRaw);
						for (int y = t.Top; y < t.Bottom; y++) for (int x = t.Left; x < t.Right; x++) SendPixel(framebuffer[y * fbwidth + x]);
						oldBgValid = oldFgValid = false;
						continue;
					}
					SendUInt8((Byte)tileType);
					if ((tileType & hextileBgSpecified) != 0) SendPixel(bg);
					if ((tileType & hextileFgSpecified) != 0) SendPixel(fg);
					if ((tileType & hextileAnySubrects) != 0) {
						SendUInt8((Byte)subrects);
						int subrectsa = 0;
						for (int y = t.Top; y < t.Bottom; y++) {
							int color = bg;
							int length = 0;
							int start = 0;
							for (int x = t.Left; x < t.Right; x++) {
								int newcolor = framebuffer[y * fbwidth + x];
								if (color != newcolor && length > 0) {
									if (color != bg && subrectsa < subrects) {
										if ((tileType & hextileSubrectsColoured) != 0) SendPixel(color);
										SendUInt8((Byte)(((start & 0xF) << 4) | ((y - t.Top) & 0xF)));
										SendUInt8((Byte)((((length - 1) & 0xF) << 4) | (0 & 0xF)));
										subrectsa++;
									}
									length = 0;
									start = x - t.Left;
								}
								length++;
								color = newcolor;
							}
							if (length > 0 && color != bg && subrectsa < subrects) {
								if ((tileType & hextileSubrectsColoured) != 0) SendPixel(color);
								SendUInt8((Byte)(((start & 0xF) << 4) | ((y - t.Top) & 0xF)));
								SendUInt8((Byte)((((length - 1) & 0xF) << 4) | (0 & 0xF)));
								subrectsa++;
							}
						}
						for (int i = subrectsa; i < subrects; i++) {
							if ((tileType & hextileSubrectsColoured) != 0) SendPixel(0);
							SendUInt16(0);
							subrectsa++;
						}
						if (subrects != subrectsa) throw new Exception("subrects != subrectsa");
					}
				}
				//Flush();
			}
		}

		private void SendPixel(int pixeli) {
			UInt32 encoded = 0;
			UInt32 pixel = (UInt32)pixeli;
			encoded |= ((((pixel >> 16) & 0xff) * (pixelformat.RedMax + 1u) / 256) & pixelformat.RedMax) << pixelformat.RedShift;
			encoded |= ((((pixel >> 8) & 0xff) * (pixelformat.GreenMax + 1u) / 256) & pixelformat.GreenMax) << pixelformat.GreenShift;
			encoded |= ((((pixel >> 0) & 0xff) * (pixelformat.BlueMax + 1u) / 256) & pixelformat.BlueMax) << pixelformat.BlueShift;
			byte[] row = new Byte[pixelformat.BitsPerPixel / 8];
			int i = 0;
			if (pixelformat.BigEndian) {
				for (int b = pixelformat.BitsPerPixel - 8; b >= 0; b -= 8) row[i++] = (Byte)(encoded >> b);
			} else {
				for (int b = 0; b < pixelformat.BitsPerPixel; b += 8) row[i++] = (Byte)(encoded >> b);
			}
			SendAll(row);
		}

		private void SendPixelFormat(RFBPixelFormat pf) {
			SendUInt8(pf.BitsPerPixel); //bits per pixel
			SendUInt8(pf.ColorDepth); //depth
			SendUInt8(pf.BigEndian ? (Byte)1 : (Byte)0); //big endian
			SendUInt8(pf.TrueColor ? (Byte)1 : (Byte)0); //true color
			SendUInt16(pf.RedMax); //red max
			SendUInt16(pf.GreenMax); //green max
			SendUInt16(pf.BlueMax); //blue max
			SendUInt8(pf.RedShift); //red shift
			SendUInt8(pf.GreenShift); //green shift
			SendUInt8(pf.BlueShift); //blue shift
			SendUInt8(0); //padding
			SendUInt8(0); //padding
			SendUInt8(0); //padding
		}
		private RFBPixelFormat ReceivePixelFormat() {
			Byte[] b = ReceiveAll(16);
			RFBPixelFormat pf = new RFBPixelFormat();
			pf.BitsPerPixel = b[0];
			pf.ColorDepth = b[1];
			pf.BigEndian = b[2] != 0;
			pf.TrueColor = b[3] != 0;
			pf.RedMax = (UInt16)((b[4] << 8) | b[5]);
			pf.GreenMax = (UInt16)((b[6] << 8) | b[7]);
			pf.BlueMax = (UInt16)((b[8] << 8) | b[9]);
			pf.RedShift = b[10];
			pf.GreenShift = b[11];
			pf.BlueShift = b[12];
			return pf;
		}

		private void SendUInt32(UInt32 value) {
			SendAll(new Byte[] { (Byte)(value >> 24), (Byte)(value >> 16), (Byte)(value >> 8), (Byte)value });
		}
		private void SendUInt16(UInt16 value) {
			SendAll(new Byte[] { (Byte)(value >> 8), (Byte)value });
		}
		private void SendUInt8(Byte value) {
			SendAll(new Byte[] { value });
		}
		private void SendAll(Byte[] buffer) {
			SendAll(buffer, 0, buffer.Length);
		}

		private void SendAll(Byte[] buffer, int off, int len) {
			if (SendBuffer == null) SendBuffer = new MemoryStream();
			SendBuffer.Write(buffer, off, len);
			if (SendBuffer.Length > 102400) FlushSendBuffer();
		}
		private void FlushSendBuffer() {
			if (SendBuffer == null) return;
			SendBuffer.Seek(0, SeekOrigin.Begin);
			SendBuffer.WriteTo(socket);
			SendBuffer.SetLength(0);
		}

		private Byte ReceiveByte() {
			Byte[] buffer = new Byte[1];
			ReceiveAll(buffer, 0, 1);
			return buffer[0];
		}
		private Byte[] ReceiveAll(int len) {
			Byte[] buffer = new Byte[len];
			ReceiveAll(buffer, 0, len);
			return buffer;
		}
		private void ReceiveAll(Byte[] buffer, int off, int len) {
			while (len > 0) {
				int sent = socket.Read(buffer, off, len);
				if (sent == 0) throw new EndOfStreamException();
				len -= sent;
				off += sent;
			}
		}
	}
}