changeset 23:644a923bca98

Added FBGUI and VNCServer
author Ivo Smits <Ivo@UCIS.nl>
date Mon, 15 Apr 2013 02:04:36 +0200
parents 5b14fed54a89
children 87f5e19c4885
files FBGUI.cs UCIS.csproj VNCServer/IFramebuffer.cs VNCServer/VNCServer.cs
diffstat 4 files changed, 3191 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FBGUI.cs	Mon Apr 15 02:04:36 2013 +0200
@@ -0,0 +1,1797 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Threading;
+using System.Windows.Forms;
+using UCIS.VNCServer;
+using ThreadingTimer = System.Threading.Timer;
+
+namespace UCIS.FBGUI {
+	public interface IFBGControl {
+		Rectangle Bounds { get; set; }
+		Boolean Visible { get; set; }
+		void Paint(Graphics g);
+		void MouseMove(Point position, MouseButtons buttons);
+		void MouseDown(Point position, MouseButtons buttons);
+		void MouseUp(Point position, MouseButtons buttons);
+		void KeyDown(Keys key);
+		void KeyPress(Char keyChar);
+		void KeyUp(Keys key);
+		void LostKeyboardCapture();
+		void Orphaned();
+	}
+	public interface IFBGContainerControl {
+		Size Size { get; } //Todo: really necessary? Probably not.
+		void Invalidate(IFBGControl control, Rectangle rect);
+		void AddControl(IFBGControl control);
+		void RemoveControl(IFBGControl control);
+		Boolean CaptureMouse(IFBGControl control, Boolean capture);
+		Boolean CaptureKeyboard(IFBGControl control, Boolean capture);
+	}
+	public class FBGControl : IFBGControl {
+		private Rectangle bounds = new Rectangle(0, 0, 100, 100);
+		private Color backColor = Color.Transparent;
+		private Boolean visible = true;
+		public virtual IFBGContainerControl Parent { get; private set; }
+		public event MouseEventHandler OnMouseDown;
+		public event MouseEventHandler OnMouseMove;
+		public event MouseEventHandler OnMouseUp;
+		public event PaintEventHandler OnPaint;
+		public event EventHandler OnResize;
+		public event EventHandler OnMove;
+		public FBGControl(IFBGContainerControl parent) {
+			this.Parent = parent;
+			if (Parent != null) Parent.AddControl(this);
+		}
+		public virtual Rectangle Bounds {
+			get { return bounds; }
+			set {
+				if (bounds == value) return;
+				Rectangle old = bounds;
+				bounds = value;
+				Parent.Invalidate(this, Rectangle.Union(new Rectangle(Point.Empty, value.Size), new Rectangle(old.X - value.X, old.Y - value.Y, old.Width, old.Height)));
+				if (value.Location != old.Location) RaiseEvent(OnMove);
+				if (value.Size != old.Size) RaiseEvent(OnResize);
+			}
+		}
+		public virtual Boolean Visible {
+			get { return visible; }
+			set {
+				visible = value;
+				Invalidate();
+			}
+		}
+		public Size Size { get { return Bounds.Size; } set { Rectangle r = Bounds; r.Size = value; Bounds = r; } }
+		public Point Location { get { return Bounds.Location; } set { Rectangle r = Bounds; r.Location = value; Bounds = r; } }
+		public int Left { get { return Bounds.Left; } set { Rectangle r = Bounds; r.X = value; Bounds = r; } }
+		public int Top { get { return Bounds.Top; } set { Rectangle r = Bounds; r.Y = value; Bounds = r; } }
+		public int Width { get { return Bounds.Width; } set { Rectangle r = Bounds; r.Width = value; Bounds = r; } }
+		public int Height { get { return Bounds.Height; } set { Rectangle r = Bounds; r.Height = value; Bounds = r; } }
+		public virtual Color BackColor { get { return backColor; } set { if (backColor == value) return; backColor = value; Invalidate(); } }
+		public virtual void Invalidate() {
+			Invalidate(new Rectangle(Point.Empty, Bounds.Size));
+		}
+		public virtual void Invalidate(Rectangle rect) {
+			Parent.Invalidate(this, rect);
+		}
+		void IFBGControl.Paint(Graphics g) { Paint(g); }
+		void IFBGControl.MouseMove(Point position, MouseButtons buttons) { MouseMove(position, buttons); }
+		void IFBGControl.MouseDown(Point position, MouseButtons buttons) { MouseDown(position, buttons); }
+		void IFBGControl.MouseUp(Point position, MouseButtons buttons) { MouseUp(position, buttons); }
+		void IFBGControl.KeyDown(Keys g) { KeyDown(g); }
+		void IFBGControl.KeyPress(Char g) { KeyPress(g); }
+		void IFBGControl.KeyUp(Keys g) { KeyUp(g); }
+		void IFBGControl.LostKeyboardCapture() { LostKeyboardCapture(); }
+		void IFBGControl.Orphaned() { Orphaned(); }
+		protected virtual void Paint(Graphics g) {
+			if (!visible) return;
+			if (backColor.A != 0) g.Clear(backColor);
+			RaiseEvent(OnPaint, new PaintEventArgs(g, Rectangle.Round(g.ClipBounds)));
+		}
+		protected virtual void MouseMove(Point position, MouseButtons buttons) { RaiseEvent(OnMouseMove, new MouseEventArgs(buttons, 0, position.X, position.Y, 0)); }
+		protected virtual void MouseDown(Point position, MouseButtons buttons) { RaiseEvent(OnMouseDown, new MouseEventArgs(buttons, 1, position.X, position.Y, 0)); }
+		protected virtual void MouseUp(Point position, MouseButtons buttons) { RaiseEvent(OnMouseUp, new MouseEventArgs(buttons, 1, position.X, position.Y, 0)); }
+		protected virtual Boolean CaptureMouse(Boolean capture) {
+			return Parent.CaptureMouse(this, capture);
+		}
+		protected virtual void KeyDown(Keys key) { }
+		protected virtual void KeyPress(Char keyChar) { }
+		protected virtual void KeyUp(Keys key) { }
+		protected virtual Boolean CaptureKeyboard(Boolean capture) {
+			return Parent.CaptureKeyboard(this, capture);
+		}
+		protected virtual void LostKeyboardCapture() { }
+		protected virtual void Orphaned() {
+			//IDisposable disp = this as IDisposable;
+			//if (!ReferenceEquals(disp, null)) disp.Dispose();
+		}
+		protected void RaiseEvent(KeyEventHandler eh, KeyEventArgs ea) { if (eh != null) eh(this, ea); }
+		protected void RaiseEvent(KeyPressEventHandler eh, KeyPressEventArgs ea) { if (eh != null) eh(this, ea); }
+		protected void RaiseEvent(MouseEventHandler eh, MouseEventArgs ea) { if (eh != null) eh(this, ea); }
+		protected void RaiseEvent(PaintEventHandler eh, PaintEventArgs ea) { if (eh != null) eh(this, ea); }
+		protected void RaiseEvent<T>(EventHandler<T> eh, T ea) where T : EventArgs { if (eh != null) eh(this, ea); }
+		protected void RaiseEvent(EventHandler eh, EventArgs ea) { if (eh != null) eh(this, ea); }
+		protected void RaiseEvent(EventHandler eh) { if (eh != null) eh(this, new EventArgs()); }
+	}
+	public class FBGContainerControl : FBGControl, IFBGContainerControl {
+		protected List<IFBGControl> controls = new List<IFBGControl>();
+		private IFBGControl mouseCaptureControl = null;
+		private IFBGControl keyboardCaptureControl = null;
+		private Rectangle childarea = Rectangle.Empty;
+		public Rectangle ClientRectangle { get { return childarea; } protected set { childarea = value; Invalidate(); } }
+		public FBGContainerControl(IFBGContainerControl parent) : base(parent) { }
+		Size IFBGContainerControl.Size { get { return childarea.IsEmpty ? Bounds.Size : childarea.Size; } }
+		void IFBGContainerControl.AddControl(IFBGControl control) { AddControl(control); }
+		protected virtual void AddControl(IFBGControl control) {
+			controls.Add(control);
+			if (control.Visible) Invalidate(control);
+		}
+		public virtual void RemoveControl(IFBGControl control) {
+			if (controls.Remove(control)) {
+				if (control.Visible) Invalidate(control);
+				CaptureMouse(control, false);
+				CaptureKeyboard(control, false);
+				control.Orphaned();
+			}
+		}
+		public virtual Point PointToChild(IFBGControl child, Point point) {
+			return point - (Size)child.Bounds.Location - (Size)ClientRectangle.Location;
+		}
+		public virtual Point PointFromChild(IFBGControl child, Point point) {
+			return point + (Size)child.Bounds.Location + (Size)ClientRectangle.Location;
+		}
+		public virtual void BringControlToFront(IFBGControl control) {
+			if (controls.Count == 0) return;
+			if (ReferenceEquals(controls[controls.Count - 1], control)) return;
+			if (!controls.Remove(control)) return;
+			controls.Add(control);
+			if (control.Visible) Invalidate(control);
+		}
+		public virtual void Invalidate(IFBGControl control) {
+			Invalidate(new Rectangle(PointFromChild(control, Point.Empty), control.Bounds.Size));
+		}
+		public virtual void Invalidate(IFBGControl control, Rectangle rect) {
+			Invalidate(new Rectangle(PointFromChild(control, rect.Location), rect.Size));
+		}
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			if (controls == null) return;
+			GraphicsState state2 = null;
+			if (!childarea.IsEmpty) {
+				state2 = g.Save();
+				g.TranslateTransform(childarea.X, childarea.Y, MatrixOrder.Append);
+				g.IntersectClip(new Rectangle(Point.Empty, childarea.Size));
+			}
+			foreach (IFBGControl control in controls) {
+				if (!control.Visible) continue;
+				if (control.Bounds.Width <= 0 || control.Bounds.Height <= 0) continue;
+				if (!g.ClipBounds.IntersectsWith((RectangleF)control.Bounds)) continue;
+				GraphicsState state = g.Save();
+				g.TranslateTransform(control.Bounds.X, control.Bounds.Y, MatrixOrder.Append);
+				g.IntersectClip(new Rectangle(Point.Empty, control.Bounds.Size));
+				control.Paint(g);
+				g.Restore(state);
+			}
+			if (state2 != null) g.Restore(state2);
+		}
+		public IFBGControl FindControlAtPosition(Point p) {
+			if (!childarea.IsEmpty && !childarea.Contains(p)) return null;
+			p.Offset(-childarea.X, -childarea.Y);
+			return ((List<IFBGControl>)controls).FindLast(delegate(IFBGControl control) { return control.Visible && control.Bounds.Contains(p); });
+		}
+		protected override void MouseMove(Point position, MouseButtons buttons) {
+			IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : FindControlAtPosition(position);
+			if (control == null) {
+				base.MouseMove(position, buttons);
+			} else {
+				control.MouseMove(PointToChild(control, position), buttons);
+			}
+		}
+		protected override void MouseDown(Point position, MouseButtons buttons) {
+			IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : FindControlAtPosition(position);
+			if (control == null) {
+				base.MouseDown(position, buttons);
+			} else {
+				control.MouseDown(PointToChild(control, position), buttons);
+			}
+		}
+		protected override void MouseUp(Point position, MouseButtons buttons) {
+			IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : FindControlAtPosition(position);
+			if (control == null) {
+				base.MouseUp(position, buttons);
+			} else {
+				control.MouseUp(PointToChild(control, position), buttons);
+			}
+		}
+		Boolean IFBGContainerControl.CaptureMouse(IFBGControl control, Boolean capture) { return CaptureMouse(control, capture); }
+		protected Boolean CaptureMouse(IFBGControl control, Boolean capture) {
+			if (capture && !ReferenceEquals(mouseCaptureControl, null)) return false;
+			if (!capture && !ReferenceEquals(mouseCaptureControl, control)) return false;
+			if (!CaptureMouse(capture)) return false;
+			mouseCaptureControl = capture ? control : null;
+			return true;
+		}
+		protected override void KeyDown(Keys key) {
+			if (ReferenceEquals(keyboardCaptureControl, null)) base.KeyDown(key);
+			else keyboardCaptureControl.KeyDown(key);
+		}
+		protected override void KeyPress(Char keyChar) {
+			if (ReferenceEquals(keyboardCaptureControl, null)) base.KeyPress(keyChar);
+			else keyboardCaptureControl.KeyPress(keyChar);
+		}
+		protected override void KeyUp(Keys key) {
+			if (ReferenceEquals(keyboardCaptureControl, null)) base.KeyUp(key);
+			else keyboardCaptureControl.KeyUp(key);
+		}
+		Boolean IFBGContainerControl.CaptureKeyboard(IFBGControl control, Boolean capture) { return CaptureKeyboard(control, capture); }
+		protected Boolean CaptureKeyboard(IFBGControl control, Boolean capture) {
+			if (!capture && !ReferenceEquals(keyboardCaptureControl, control)) return false;
+			if (!CaptureKeyboard(capture)) return false;
+			IFBGControl prev = keyboardCaptureControl;
+			keyboardCaptureControl = capture ? control : null;
+			if (prev != null) LostKeyboardCapture();
+			return true;
+		}
+		protected override void LostKeyboardCapture() {
+			base.LostKeyboardCapture();
+			if (keyboardCaptureControl != null) keyboardCaptureControl.LostKeyboardCapture();
+		}
+		protected override void Orphaned() {
+			base.Orphaned();
+			IFBGControl[] c = controls.ToArray();
+			controls.Clear();
+			foreach (IFBGControl control in c) control.Orphaned();
+			mouseCaptureControl = null;
+			keyboardCaptureControl = null;
+		}
+	}
+	public class FBGDockContainer : FBGContainerControl {
+		private Dictionary<IFBGControl, DockStyle> dockStyles = new Dictionary<IFBGControl, DockStyle>();
+		private Dictionary<IFBGControl, AnchorStyles> anchorStyles = new Dictionary<IFBGControl, AnchorStyles>();
+		private Rectangle oldBounds;
+		public FBGDockContainer(IFBGContainerControl parent)
+			: base(parent) {
+			oldBounds = ClientRectangle.IsEmpty ? Bounds : new Rectangle(Bounds.Location + (Size)ClientRectangle.Location, ClientRectangle.Size);
+		}
+		protected override void AddControl(IFBGControl control) {
+			base.AddControl(control);
+		}
+		public override void BringControlToFront(IFBGControl control) {
+			base.BringControlToFront(control);
+			if (dockStyles.ContainsKey(control)) DoLayout();
+		}
+		public override void RemoveControl(IFBGControl control) {
+			base.RemoveControl(control);
+			if (dockStyles.Remove(control)) DoLayout();
+		}
+		public override Rectangle Bounds {
+			get { return base.Bounds; }
+			set {
+				base.Bounds = value;
+				DoLayout();
+				Rectangle newBounds = ClientRectangle.IsEmpty ? Bounds : new Rectangle(Bounds.Location + (Size)ClientRectangle.Location, ClientRectangle.Size);
+				foreach (KeyValuePair<IFBGControl, AnchorStyles> c in anchorStyles) {
+					Rectangle b = c.Key.Bounds;
+					if ((c.Value & AnchorStyles.Right) != 0) {
+						if ((c.Value & AnchorStyles.Left) == 0) b.X += newBounds.Width - oldBounds.Width;
+						else b.Width += newBounds.Width - oldBounds.Width;
+					} else if ((c.Value & AnchorStyles.Left) == 0) b.X += newBounds.X - oldBounds.X;
+					if ((c.Value & AnchorStyles.Bottom) != 0) {
+						if ((c.Value & AnchorStyles.Top) == 0) b.Y += newBounds.Height - oldBounds.Height;
+						else b.Height += newBounds.Height - oldBounds.Height;
+					} else if ((c.Value & AnchorStyles.Top) == 0) b.Y += newBounds.Y - oldBounds.Y;
+					c.Key.Bounds = b;
+				}
+				oldBounds = newBounds;
+			}
+		}
+		public DockStyle GetDockStyle(IFBGControl control) {
+			DockStyle ds;
+			if (!dockStyles.TryGetValue(control, out ds)) ds = DockStyle.None;
+			return ds;
+		}
+		public void SetDockStyle(IFBGControl control, DockStyle style) {
+			if (style == DockStyle.None) {
+				if (dockStyles.Remove(control)) DoLayout();
+			} else if (controls.Contains(control)) {
+				anchorStyles.Remove(control);
+				dockStyles[control] = style;
+				DoLayout();
+			}
+		}
+		public AnchorStyles GetAnchorStyle(IFBGControl control) {
+			AnchorStyles ds;
+			if (!anchorStyles.TryGetValue(control, out ds)) ds = AnchorStyles.Left | AnchorStyles.Top;
+			return ds;
+		}
+		public void SetAnchorStyle(IFBGControl control, AnchorStyles style) {
+			if (style == (AnchorStyles.Left | AnchorStyles.Top)) {
+				anchorStyles.Remove(control);
+			} else if (controls.Contains(control)) {
+				dockStyles.Remove(control);
+				anchorStyles[control] = style;
+			}
+		}
+		public void SetAnchor(IFBGControl control, AnchorStyles style, int value) {
+			if (controls.Contains(control)) {
+				AnchorStyles oldstyle;
+				if (!anchorStyles.TryGetValue(control, out oldstyle)) oldstyle = AnchorStyles.Left | AnchorStyles.Top;
+				Rectangle b = control.Bounds;
+				switch (style) {
+					case AnchorStyles.None: throw new ArgumentException("style", "Anchor style can not be None");
+					case AnchorStyles.Left: b.X = value; break;
+					case AnchorStyles.Top: b.Y = value; break;
+					case AnchorStyles.Right:
+						if ((oldstyle & AnchorStyles.Left) == 0) b.X = ClientRectangle.Width - b.Width - value;
+						else b.Width = ClientRectangle.Width - b.X - value;
+						break;
+					case AnchorStyles.Bottom:
+						if ((oldstyle & AnchorStyles.Top) == 0) b.Y = ClientRectangle.Height - b.Height - value;
+						else b.Height = ClientRectangle.Height - b.Y - value; 
+						break;
+					default: throw new ArgumentOutOfRangeException("style", "The value vor the style argument is invalid");
+				}
+				control.Bounds = b;
+				dockStyles.Remove(control);
+				anchorStyles[control] = oldstyle | style;
+			}
+		}
+		private void DoLayout() {
+			Rectangle a = new Rectangle(Point.Empty, ClientRectangle.IsEmpty ? Bounds.Size : ClientRectangle.Size);
+			foreach (KeyValuePair<IFBGControl, DockStyle> c in dockStyles) {
+				Rectangle b = c.Key.Bounds;
+				if (c.Value == DockStyle.Left) {
+					b.Location = a.Location;
+					b.Height = a.Height;
+					a.X += b.Width;
+					a.Width -= b.Width;
+				} else if (c.Value == DockStyle.Top) {
+					b.Location = a.Location;
+					b.Width = a.Width;
+					a.Y += b.Height;
+					a.Height -= b.Height;
+				} else if (c.Value == DockStyle.Right) {
+					b.X = a.X + a.Width - b.Width;
+					b.Y = a.Y;
+					b.Height = a.Height;
+					a.Width -= b.Width;
+				} else if (c.Value == DockStyle.Bottom) {
+					b.X = a.X;
+					b.Y = a.Y + a.Height - b.Height;
+					b.Width = a.Width;
+					a.Height -= b.Height;
+				} else if (c.Value == DockStyle.Fill) {
+					b = a;
+				}
+				c.Key.Bounds = b;
+				if (a.Width < 0) a.Width = 0;
+				if (a.Height < 0) a.Height = 0;
+			}
+		}
+	}
+	public class FBGGroupBox : FBGDockContainer {
+		private String text = String.Empty;
+		public FBGGroupBox(IFBGContainerControl parent)
+			: base(parent) {
+			ClientRectangle = new Rectangle(1, 15, Bounds.Width - 2, Bounds.Height - 17);
+		}
+		public override Rectangle Bounds {
+			get { return base.Bounds; }
+			set {
+				ClientRectangle = new Rectangle(1, 15, value.Width - 2, value.Height - 17);
+				base.Bounds = value;
+			}
+		}
+		public String Text { get { return text; } set { if (text == value) return; text = value; Invalidate(new Rectangle(0, 0, Bounds.Width, 15)); } }
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			g.DrawRectangle(Pens.Gray, 0, 6, Bounds.Width - 1, Bounds.Height - 7);
+			SizeF ss = g.MeasureString(Text, SystemFonts.DefaultFont, new Size(Bounds.Width - 10 - 9, 15));
+			g.FillRectangle(SystemBrushes.Control, 9, 0, ss.Width, ss.Height);
+			g.DrawString(Text, SystemFonts.DefaultFont, Brushes.DarkBlue, new Rectangle(9, 0, Bounds.Width - 10 - 9, 15));
+		}
+	}
+	public class WinFormsFBGHost : Control, IFBGContainerControl {
+		private IFBGControl childControl = null;
+		private IFBGControl mouseCaptureControl = null;
+		private IFBGControl keyboardCaptureControl = null;
+		public IFBGControl ChildControl { get { return childControl; } }
+
+		public WinFormsFBGHost() {
+			DoubleBuffered = true;
+		}
+		
+		public virtual Point PointToChild(IFBGControl child, Point point) {
+			return point - (Size)child.Bounds.Location;
+		}
+		public virtual Point PointFromChild(IFBGControl child, Point point) {
+			return point + (Size)child.Bounds.Location;
+		}
+
+		Size IFBGContainerControl.Size { get { return ClientSize; } }
+		void IFBGContainerControl.Invalidate(IFBGControl control, Rectangle rect) {
+			Invalidate(new Rectangle(PointFromChild(control, rect.Location), rect.Size));
+		}
+		void IFBGContainerControl.AddControl(IFBGControl control) {
+			if (!ReferenceEquals(childControl, null)) throw new InvalidOperationException("This container can have only one child control");
+			childControl = control;
+			control.Bounds = new Rectangle(Point.Empty, ClientSize);
+			Invalidate(control.Bounds);
+		}
+		void IFBGContainerControl.RemoveControl(IFBGControl control) {
+			if (!ReferenceEquals(childControl, control)) return;
+			childControl = null;
+			Invalidate(control.Bounds);
+			if (mouseCaptureControl == control) mouseCaptureControl = null;
+			if (keyboardCaptureControl == control) control = null;
+			control.Orphaned();
+		}
+		Boolean IFBGContainerControl.CaptureMouse(IFBGControl control, Boolean capture) {
+			if (capture && !ReferenceEquals(mouseCaptureControl, null)) return false;
+			if (!capture && !ReferenceEquals(mouseCaptureControl, control)) return false;
+			mouseCaptureControl = capture ? control : null;
+			return true;
+		}
+		Boolean IFBGContainerControl.CaptureKeyboard(IFBGControl control, Boolean capture) {
+			if (!capture && !ReferenceEquals(keyboardCaptureControl, control)) return false;
+			keyboardCaptureControl = capture ? control : null;
+			return true;
+		}
+
+		protected override void OnPaint(PaintEventArgs e) {
+			base.OnPaint(e);
+			Graphics g = e.Graphics;
+			GraphicsState state = g.Save();
+			g.SetClip(e.ClipRectangle);
+			if (ReferenceEquals(childControl, null)) return;
+			if (childControl.Bounds.Width <= 0 || childControl.Bounds.Height <= 0) return;
+			if (!g.ClipBounds.IntersectsWith((RectangleF)childControl.Bounds)) return;
+			g.TranslateTransform(childControl.Bounds.X, childControl.Bounds.Y, MatrixOrder.Append);
+			g.IntersectClip(new Rectangle(Point.Empty, childControl.Bounds.Size));
+			childControl.Paint(g);
+			g.Restore(state);
+		}
+		protected override void OnResize(EventArgs e) {
+			if (!ReferenceEquals(childControl, null)) childControl.Bounds = new Rectangle(Point.Empty, ClientSize);
+			base.OnResize(e);
+		}
+		protected override void OnMouseDown(MouseEventArgs e) {
+			base.OnMouseDown(e);
+			IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : childControl;
+			if (control != null) control.MouseDown(PointToChild(control, e.Location), e.Button);
+		}
+		protected override void OnMouseUp(MouseEventArgs e) {
+			base.OnMouseUp(e);
+			IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : childControl;
+			if (control != null) control.MouseUp(PointToChild(control, e.Location), e.Button);
+		}
+		protected override void OnMouseMove(MouseEventArgs e) {
+			IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : childControl;
+			if (control != null) control.MouseMove(PointToChild(control, e.Location), e.Button);
+		}
+		protected override bool IsInputChar(char charCode) {
+			return true;
+		}
+		protected override bool IsInputKey(Keys keyData) {
+			return true;
+		}
+		protected override void OnKeyDown(KeyEventArgs e) {
+			//base.OnKeyDown(e);
+			if (!ReferenceEquals(keyboardCaptureControl, null)) keyboardCaptureControl.KeyDown(e.KeyData);
+		}
+		protected override void OnKeyPress(KeyPressEventArgs e) {
+			//base.OnKeyPress(e);
+			if (!ReferenceEquals(keyboardCaptureControl, null)) keyboardCaptureControl.KeyPress(e.KeyChar);
+		}
+		protected override void OnKeyUp(KeyEventArgs e) {
+			//base.OnKeyUp(e);
+			if (!ReferenceEquals(keyboardCaptureControl, null)) keyboardCaptureControl.KeyUp(e.KeyData);
+		}
+		protected override void OnHandleDestroyed(EventArgs e) {
+			if (!ReferenceEquals(childControl, null)) childControl.Orphaned();
+			base.OnHandleDestroyed(e);
+		}
+	}
+	public class FBGCursor {
+		public Image Image { get; private set; }
+		public Point Hotspot { get; private set; }
+		public Size Size { get; private set; }
+		public FBGCursor(Image image, Point hotspot) {
+			this.Image = image;
+			this.Hotspot = hotspot;
+			this.Size = image.Size;
+		}
+
+		private const String ArrowCursorImageData = "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAVCAYAAAByrA+0AAAAkUlEQVR42pWTORbEMAhDJb3cYNqUuf+JUk6bM2iKLM9DvGAqG/MFxpgAfKwbkTQBwOe7ewqwnYZ0L7KQyk0GUnSMINWcPUgtpRakXr01SKOuREiZ3pcQz329KeR7YpZaUCkQ50wjxWYGko8aSduGbZD8m2bF4NQsxeBj3XiX92rrzOfpvkMrizBpS+/wyuLynj9U+GDtLEEVuQAAAABJRU5ErkJggg==";
+		public static readonly FBGCursor ArrowCursor = new FBGCursor(Image.FromStream(new MemoryStream(Convert.FromBase64String(ArrowCursorImageData))), Point.Empty);
+	}
+	public class FBGRenderer : FBGContainerControl, IDisposable {
+		private FBGCursor cursor = null;
+		private Point cursorposition = Point.Empty;
+		public IFramebuffer Framebuffer { get; private set; }
+		private Bitmap Frontbuffer;
+		private Object RenderLock = new object();
+		public event EventHandler<InvalidateEventArgs> Painted;
+		private Size size;
+		private ThreadingTimer PaintTimer = null;
+		private Boolean PaintScheduled = false;
+		private int PaintDelay = 0;
+		public Boolean SuspendDrawing {
+			get { return suspenddrawing; }
+			set {
+				lock (RenderLock) {
+					suspenddrawing = value;
+					if (!value) {
+						Refresh(DirtyRectangle);
+						DirtyRectangle = Rectangle.Empty;
+					}
+				}
+			}
+		}
+		public int RedrawDelay {
+			get { return PaintDelay; }
+			set {
+				lock (RenderLock) {
+					if (value < 0) throw new ArgumentOutOfRangeException("value");
+					PaintDelay = value;
+					if (PaintDelay == 0) {
+						if (PaintTimer != null) PaintTimer.Dispose();
+						PaintTimer = null;
+						if (PaintScheduled) PaintTimerCallback(null);
+					} else {
+						PaintTimer = new ThreadingTimer(PaintTimerCallback);
+					}
+				}
+			}
+		}
+		private Boolean suspenddrawing = false;
+		private Rectangle DirtyRectangle;
+
+		public FBGCursor Cursor {
+			get { return cursor; }
+			set {
+				cursor = value;
+				Invalidate();
+			}
+		}
+		public Point CursorPosition {
+			get { return cursorposition; }
+			set {
+				if (cursorposition == value) return;
+				Point oldposition = cursorposition;
+				cursorposition = value;
+				if (cursor == null) return;
+				Size s = cursor.Size;
+				if (s.Width == 0 && s.Height == 0) s = new Size(32, 32);
+				Rectangle r = Rectangle.Union(new Rectangle(oldposition, s), new Rectangle(cursorposition, s));
+				r.Offset(-cursor.Hotspot.X, -cursor.Hotspot.Y);
+				if (Environment.OSVersion.Platform == PlatformID.Unix) {
+					r = Rectangle.Union(r, Rectangle.Union(new Rectangle(oldposition.X - 2, oldposition.Y - 2, 4, 4), new Rectangle(cursorposition.X - 2, cursorposition.Y - 2, 4, 4)));
+				}
+				Invalidate(r);
+			}
+		}
+		public override Rectangle Bounds {
+			get { return new Rectangle(Point.Empty, size); }
+			set { throw new NotSupportedException("Can not change the top control bounds"); }
+		}
+		public FBGRenderer(IFramebuffer fb) : this(fb.Width, fb.Height) {
+			Framebuffer = fb;
+		}
+		public FBGRenderer(Size fbsize) : this(fbsize.Width, fbsize.Height) { }
+		public FBGRenderer(int width, int height) : this(new Bitmap(width, height, PixelFormat.Format32bppRgb)) { }
+		public FBGRenderer(Bitmap bmp) : base(null) {
+			Frontbuffer = bmp;
+			BackColor = SystemColors.Control;
+			size = Frontbuffer.Size;
+		}
+		public override void Invalidate(Rectangle rect) {
+			if (rect.Width == 0 || rect.Height == 0) return;
+			lock (RenderLock) {
+				if (SuspendDrawing || PaintTimer != null) {
+					DirtyRectangle = DirtyRectangle.IsEmpty ? rect : Rectangle.Union(DirtyRectangle, rect);
+					if (!SuspendDrawing && !PaintScheduled) {
+						PaintScheduled = true;
+						PaintTimer.Change(PaintDelay, Timeout.Infinite);
+					}
+				} else {
+					Refresh(rect);
+				}
+			}
+		}
+		private void PaintTimerCallback(Object state) {
+			try {
+				lock (RenderLock) {
+					PaintScheduled = false;
+					Refresh(DirtyRectangle);
+					DirtyRectangle = Rectangle.Empty;
+				}
+			} catch { }
+		}
+		private void Refresh(Rectangle rect) {
+			lock (RenderLock) {
+				rect.Intersect(Bounds);
+				if (rect.Width == 0 || rect.Height == 0) return;
+				using (Graphics g = Graphics.FromImage(Frontbuffer)) {
+					g.SetClip(rect);
+					Paint(g);
+				}
+				if (Framebuffer != null) Framebuffer.DrawImage(Frontbuffer, rect, rect.Location);
+				RaiseEvent(Painted, new InvalidateEventArgs(rect));
+			}
+		}
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			if (Cursor != null) {
+				Point r = CursorPosition;
+				r.Offset(-cursor.Hotspot.X, -cursor.Hotspot.Y);
+				g.DrawImageUnscaled(Cursor.Image, r);
+			}
+		}
+		protected override Boolean CaptureMouse(Boolean capture) {
+			return true;
+		}
+		protected override Boolean CaptureKeyboard(bool capture) {
+			return true;
+		}
+		public void Dispose() {
+			lock (RenderLock) {
+				if (PaintTimer != null) PaintTimer.Dispose();
+				PaintTimer = null;
+			}
+			Orphaned();
+			if (Frontbuffer != null) Frontbuffer.Dispose();
+		}
+		public Bitmap LockBitmapBuffer() {
+			Monitor.Enter(RenderLock);
+			return Frontbuffer;
+		}
+		public void UnlockBitmapBuffer() {
+			Monitor.Exit(RenderLock);
+		}
+		public new void MouseMove(Point position, MouseButtons buttons) { base.MouseMove(position, buttons); }
+		public new void MouseDown(Point position, MouseButtons buttons) { base.MouseDown(position, buttons); }
+		public new void MouseUp(Point position, MouseButtons buttons) { base.MouseUp(position, buttons); }
+		public new void KeyDown(Keys key) { base.KeyDown(key); }
+		public new void KeyPress(Char key) { base.KeyPress(key); }
+		public new void KeyUp(Keys key) { base.KeyUp(key); }
+	}
+	public class FBGForm : FBGDockContainer {
+		private Point prevPosition = Point.Empty;
+		private NonClientOps moveresize = 0;
+		private String text = String.Empty;
+		public event EventHandler TextChanged;
+		public Boolean Sizable { get; set; }
+		public Boolean Movable { get; set; }
+		public Boolean Closable { get; set; }
+		[Flags]
+		private enum NonClientOps : int {
+			None = 0,
+			Move = 1,
+			ResizeLeft = 2,
+			ResizeRight = 4,
+			ResizeTop = 8,
+			ResizeBottom = 16,
+			ButtonClose = 32,
+			MoveResize = ResizeLeft | ResizeRight | ResizeBottom | ResizeTop | Move,
+		}
+		public FBGForm(IFBGContainerControl parent) : base(parent) {
+			BackColor = SystemColors.Control;
+			ClientRectangle = new Rectangle(4, 22, Bounds.Width - 8, Bounds.Height - 26);
+			Sizable = true;
+			Movable = true;
+			Closable = false;
+		}
+		public override Rectangle Bounds {
+			get { return base.Bounds; }
+			set {
+				ClientRectangle = new Rectangle(4, 22, value.Width - 8, value.Height - 26);
+				base.Bounds = value;
+			}
+		}
+		public String Text { get { return text; } set { if (text == value) return; text = value; Invalidate(new Rectangle(0, 0, Bounds.Width, 20)); RaiseEvent(TextChanged); } }
+		protected override void MouseDown(Point p, MouseButtons buttons) {
+			NonClientOps mr = 0;
+			if ((buttons & MouseButtons.Left) != 0) {
+				if ((new Rectangle(Bounds.Width - 5 - 14, 4, 14, 14)).Contains(p)) {
+					mr = NonClientOps.ButtonClose;
+				} else {
+					if (Sizable) {
+						if (Movable) {
+							if (p.X < 4) mr |= NonClientOps.ResizeLeft;
+							if (p.Y < 4) mr |= NonClientOps.ResizeTop;
+						}
+						if (p.X >= Bounds.Width - 4) mr |= NonClientOps.ResizeRight;
+						if (p.Y >= Bounds.Height - 4) mr |= NonClientOps.ResizeBottom;
+					}
+					if (mr == 0 && Movable && p.Y < 20) mr = NonClientOps.Move;
+				}
+			}
+			if (mr != 0) {
+				moveresize = mr;
+				prevPosition = p;
+				CaptureMouse(true);
+			} else {
+				base.MouseDown(p, buttons);
+			}
+		}
+		protected override void MouseMove(Point position, MouseButtons buttons) {
+			if (moveresize == 0) {
+				base.MouseMove(position, buttons);
+			} else if ((moveresize & NonClientOps.MoveResize) != 0) {
+				Rectangle b = Bounds;
+				int dx = position.X - prevPosition.X;
+				int dy = position.Y - prevPosition.Y;
+				if (moveresize == NonClientOps.Move) b.Offset(dx, dy);
+				if ((moveresize & NonClientOps.ResizeLeft) != 0) {
+					b.X += dx;
+					b.Width -= dx;
+				} else if ((moveresize & NonClientOps.ResizeRight) != 0) {
+					b.Width += dx;
+					prevPosition.X = position.X;
+				}
+				if ((moveresize & NonClientOps.ResizeTop) != 0) {
+					b.Y += dy;
+					b.Height -= dy;
+				} else if ((moveresize & NonClientOps.ResizeBottom) != 0) {
+					b.Height += dy;
+					prevPosition.Y = position.Y;
+				}
+				if (b.Width < 55) b.Width = 55;
+				if (b.Height < 25) b.Height = 25;
+				Bounds = b;
+			}
+		}
+		protected override void MouseUp(Point position, MouseButtons buttons) {
+			if (moveresize == 0) {
+				base.MouseUp(position, buttons);
+			} else if ((buttons & MouseButtons.Left) != 0) {
+				MouseMove(position, buttons);
+				CaptureMouse(false);
+				if (moveresize == NonClientOps.ButtonClose && (new Rectangle(Bounds.Width - 5 - 14, 4, 14, 14)).Contains(position) && Closable) Close();
+				moveresize = 0;
+			}
+		}
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			g.DrawRectangle(Pens.Gray, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
+			g.DrawRectangle(Pens.LightGray, 1, 1, Bounds.Width - 3, Bounds.Height - 3);
+			g.DrawRectangle(Pens.DarkGray, 2, 20, Bounds.Width - 5, Bounds.Height - 23);
+			g.DrawRectangle(Pens.Gray, 3, 21, Bounds.Width - 7, Bounds.Height - 25);
+			using (Brush b = new LinearGradientBrush(new Rectangle(0, 1, 1, 18), Color.Gray, Color.LightGray, LinearGradientMode.Vertical))
+				g.FillRectangle(b, 2, 2, Bounds.Width - 4, 18);
+
+			g.DrawString(Text, SystemFonts.CaptionFont, Brushes.Black, 4, 1);
+
+			g.DrawRectangle(Pens.Gray, Bounds.Width - 5 - 14, 4, 14, 14);
+			g.DrawRectangle(Pens.Gray, Bounds.Width - 5 - 14 - 16, 4, 14, 14);
+			g.DrawRectangle(Pens.Gray, Bounds.Width - 5 - 14 - 32, 4, 14, 14);
+			using (Brush b = new LinearGradientBrush(new Rectangle(0, 5, 1, 13), Color.LightGray, Color.DarkGray, LinearGradientMode.Vertical)) {
+				g.FillRectangle(b, Bounds.Width - 5 - 14 + 1, 5, 13, 13);
+				g.FillRectangle(b, Bounds.Width - 5 - 14 + 1 - 16, 5, 13, 13);
+				g.FillRectangle(b, Bounds.Width - 5 - 14 + 1 - 32, 5, 13, 13);
+			}
+			if (Closable) {
+				g.DrawLine(Pens.Black, Bounds.Width - 5 - 14 + 3, 4 + 3, Bounds.Width - 5 - 14 + 14 - 3, 4 + 14 - 3);
+				g.DrawLine(Pens.Black, Bounds.Width - 5 - 14 + 3, 4 + 14 - 3, Bounds.Width - 5 - 14 + 14 - 3, 4 + 3);
+			}
+		}
+		public void Close() {
+			Parent.RemoveControl(this);
+		}
+	}
+	public class FBGDesktop : FBGContainerControl {
+		FBGContainerControl windowcontainer;
+		FBGContainerControl menucontainer;
+		Boolean startup = true;
+		class FBGWindowState {
+			public IFBGControl Control;
+			public FBGButton MenuButton;
+			public Boolean Visible;
+			public Rectangle OldBounds;
+			public FBGWindowState(IFBGControl cntrl, FBGButton btn) {
+				Control = cntrl;
+				MenuButton = btn;
+				Visible = true;
+			}
+		}
+		Dictionary<IFBGControl, FBGWindowState> windowstates = new Dictionary<IFBGControl, FBGWindowState>();
+		public FBGDesktop(IFBGContainerControl parent) : base(parent) {
+			menucontainer = new FBGContainerControl(this);
+			menucontainer.Bounds = new Rectangle(0, Bounds.Height - 25, Bounds.Width, 25);
+			windowcontainer = new FBGContainerControl(this);
+			windowcontainer.Bounds = new Rectangle(0, 0, Bounds.Width, Bounds.Height - 25);
+			startup = false;
+		}
+		public override Rectangle Bounds {
+			get { return base.Bounds; }
+			set {
+				if (Bounds == value) return;
+				base.Bounds = value;
+				if (startup) return;
+				menucontainer.Bounds = new Rectangle(0, value.Height - 25, value.Width, 25);
+				windowcontainer.Bounds = new Rectangle(0, 0, value.Width, value.Height - 25);
+				ScaleMenuButtons();
+			}
+		}
+		protected override void AddControl(IFBGControl control) {
+			if (startup) {
+				base.AddControl(control);
+				return;
+			}
+			((IFBGContainerControl)windowcontainer).AddControl(control);
+			FBGButton btn = new FBGButton(menucontainer);
+			FBGForm formcontrol = control as FBGForm;
+			if (formcontrol == null) {
+				btn.Text = "Untitled";
+			} else {
+				formcontrol.TextChanged += delegate(Object sender, EventArgs e) {
+					btn.Text = formcontrol.Text;
+				};
+				btn.Text = formcontrol.Text;
+			}
+			FBGWindowState ws = new FBGWindowState(control, btn);
+			windowstates.Add(control, ws);
+			ScaleMenuButtons();
+			btn.Click += delegate(Object sender, EventArgs e) {
+				if (ws.Visible) {
+					if (ws.MenuButton.BackColor == Color.DarkGray) {
+						ws.OldBounds = ws.Control.Bounds;
+						ws.Visible = false;
+						ws.Control.Bounds = Rectangle.Empty;
+					} else {
+						windowcontainer.BringControlToFront(ws.Control);
+						foreach (FBGWindowState wsa in windowstates.Values) if (!ReferenceEquals(ws, wsa)) wsa.MenuButton.BackColor = SystemColors.ButtonFace;
+						ws.MenuButton.BackColor = Color.DarkGray;
+					}
+				} else {
+					ws.Control.Bounds = ws.OldBounds;
+					ws.Visible = true;
+					windowcontainer.BringControlToFront(ws.Control);
+					foreach (FBGWindowState wsa in windowstates.Values) if (!ReferenceEquals(ws, wsa)) wsa.MenuButton.BackColor = SystemColors.ButtonFace;
+					ws.MenuButton.BackColor = Color.DarkGray;
+				}
+			};
+		}
+		public override void RemoveControl(IFBGControl control) {
+			windowcontainer.RemoveControl(control);
+			windowstates.Remove(control);
+			ScaleMenuButtons();
+		}
+		private void ScaleMenuButtons() {
+			int bcount = windowstates.Count;
+			int bwidth = 200;
+			int twidth = bwidth * bcount;
+			if (twidth > Bounds.Width) bwidth = menucontainer.Bounds.Width / bcount;
+			int i = 0;
+			foreach (FBGWindowState ws in windowstates.Values) {
+				ws.MenuButton.Bounds = new Rectangle(i * bwidth, 0, bwidth, 25);
+				i++;
+			}
+		}
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			g.DrawLine(Pens.Black, 0, Bounds.Height - 25, Bounds.Width, Bounds.Height - 25);
+		}
+		protected override void MouseDown(Point position, MouseButtons buttons) {
+			IFBGControl control = FindControlAtPosition(position);
+			if (ReferenceEquals(control, windowcontainer)) {
+				control = windowcontainer.FindControlAtPosition(PointToChild(windowcontainer, position));
+				if (!ReferenceEquals(control, null)) {
+					windowcontainer.BringControlToFront(control);
+					foreach (FBGWindowState ws in windowstates.Values) 
+						ws.MenuButton.BackColor = ReferenceEquals(ws.Control, control) ? Color.DarkGray : SystemColors.ButtonFace;
+				}
+			}
+			base.MouseDown(position, buttons);
+		}
+	}
+
+	public class FBGLabel : FBGControl, IDisposable {
+		public FBGLabel(IFBGContainerControl parent) : base(parent) {
+			Size = new Size(200, 16);
+		}
+		private String text = String.Empty;
+		private Font font = SystemFonts.DefaultFont;
+		private Brush brush = SystemBrushes.ControlText;
+		private StringFormat stringformat = new StringFormat();
+		public String Text { get { return text; } set { text = value; Invalidate(); } }
+		public Font Font { get { return font; } set { font = value; Invalidate(); } }
+		public Brush Brush { get { return brush; } set { brush = value; Invalidate(); } }
+		public Color Color { set { Brush = new SolidBrush(value); } }
+		public StringAlignment Alignment { get { return stringformat.Alignment; } set { stringformat.Alignment = value; Invalidate(); } }
+		public StringFormatFlags FormatFlags { get { return stringformat.FormatFlags; } set { stringformat.FormatFlags = value; Invalidate(); } }
+		public StringAlignment LineAlignment { get { return stringformat.LineAlignment; } set { stringformat.LineAlignment = value; Invalidate(); } }
+		public StringTrimming Trimming { get { return stringformat.Trimming; } set { stringformat.Trimming = value; Invalidate(); } }
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			g.DrawString(text, font, brush, new Rectangle(Point.Empty, Bounds.Size), stringformat);
+		}
+		public void Dispose() {
+			stringformat.Dispose();
+		}
+		protected override void Orphaned() {
+			base.Orphaned();
+			Dispose();
+		}
+	}
+	public class FBGTextBox : FBGControl {
+		public FBGTextBox(IFBGContainerControl parent) : base(parent) { 
+			BackColor = Color.White;
+			Size = new Size(200, 20);
+		}
+		private String text = String.Empty;
+		private Char passwordChar = (Char)0;
+		private Font font = new Font(FontFamily.GenericMonospace, 10);
+		private Brush brush = SystemBrushes.ControlText;
+		private Boolean hasKeyboardFocus = false;
+		public String Text { get { return text; } set { text = value; if (CaretPosition > text.Length) CaretPosition = text.Length; Invalidate(); RaiseEvent(TextChanged); } }
+		public Font Font { get { return font; } set { font = value; Invalidate(); } }
+		public Brush Brush { get { return brush; } set { brush = value; Invalidate(); } }
+		public Color Color { set { Brush = new SolidBrush(value); } }
+		public Int32 CaretPosition { get; private set; }
+		public Char PasswordChar { get { return passwordChar; } set { passwordChar = value; Invalidate(); } }
+		public event EventHandler TextChanged;
+		public event KeyPressEventHandler OnKeyPress;
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			g.DrawRectangle(Pens.Gray, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
+			using (StringFormat sf_nonprinting = new StringFormat(StringFormat.GenericTypographic)) {
+				sf_nonprinting.Trimming = StringTrimming.None;
+				sf_nonprinting.FormatFlags = StringFormatFlags.DisplayFormatControl | StringFormatFlags.MeasureTrailingSpaces;
+				sf_nonprinting.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
+
+				float x = 1;
+				float y = 1;
+				float w = Width - 2;
+				if (hasKeyboardFocus && CaretPosition == 0) {
+					g.DrawLine(Pens.Black, x + 2, 2, x + 2, Height - 4);
+				}
+				String c = passwordChar == 0 ? null : new String(passwordChar, 1);
+				for (int i = 0; i < text.Length; i++) {
+					if (passwordChar == 0) c = text.Substring(i, 1);
+					SizeF s = g.MeasureString(c, font, (int)Math.Ceiling(w), sf_nonprinting);
+					g.DrawString(c, font, brush, x, y);
+					x += (float)Math.Ceiling(s.Width);
+					w -= (float)Math.Ceiling(s.Width);
+
+					if (hasKeyboardFocus && i == CaretPosition - 1) {
+						g.DrawLine(Pens.Black, x + 2, 2, x + 2, Height - 4);
+					}
+				}
+			}
+		}
+		protected override void MouseDown(Point position, MouseButtons buttons) {
+			hasKeyboardFocus = CaptureKeyboard(true);
+			CaretPosition = text.Length;
+			float x = 1;
+			String c = passwordChar == 0 ? null : new String(passwordChar, 1);
+			for (int i = 0; i < text.Length; i++) {
+				if (passwordChar == 0) c = text.Substring(i, 1);
+				Size s;
+				try {
+					s = TextRenderer.MeasureText(c, font, Size.Empty, TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
+				} catch (Exception) {
+					break;
+				}
+				x += s.Width;
+				if (position.X < x) {
+					CaretPosition = i;
+					break;
+				}
+			}
+			Invalidate();
+		}
+		protected override void KeyDown(Keys key) {
+			if ((key & Keys.KeyCode) == Keys.Left) {
+				CaretPosition--;
+				if (CaretPosition < 0) CaretPosition = 0;
+				Invalidate();
+			} else if ((key & Keys.KeyCode) == Keys.Right) {
+				CaretPosition++;
+				if (CaretPosition > text.Length) CaretPosition = text.Length;
+				Invalidate();
+			} else if ((key & Keys.KeyCode) == Keys.Home) {
+				CaretPosition = 0;
+				Invalidate();
+			} else if ((key & Keys.KeyCode) == Keys.End) {
+				CaretPosition = text.Length;
+				Invalidate();
+			} else if ((key & Keys.Control) != 0 && (key & Keys.KeyCode) == Keys.V) {
+				String cbtext = Clipboard.GetText(TextDataFormat.UnicodeText);
+				CaretPosition += cbtext.Length;
+				Text = Text.Insert(CaretPosition - cbtext.Length, cbtext);
+			}
+		}
+		protected override void KeyPress(char keyChar) {
+			KeyPressEventArgs e = new KeyPressEventArgs(keyChar);
+			RaiseEvent(OnKeyPress, e);
+			if (e.Handled) return;
+			hasKeyboardFocus = true;
+			if (keyChar == 8) {
+				if (CaretPosition > 0) {
+					CaretPosition--;
+					Text = Text.Remove(CaretPosition, 1);
+				}
+			} else if (keyChar == 127) {
+				if (CaretPosition < Text.Length) {
+					Text = Text.Remove(CaretPosition, 1);
+				}
+			} else if (keyChar < 32) {
+			} else {
+				CaretPosition++;
+				Text = Text.Insert(CaretPosition - 1, new String(keyChar, 1));
+			}
+		}
+		public void Focus() {
+			hasKeyboardFocus = CaptureKeyboard(true);
+		}
+		protected override void LostKeyboardCapture() {
+			base.LostKeyboardCapture();
+			hasKeyboardFocus = false;
+			Invalidate();
+		}
+	}
+	public class FBGButton : FBGControl {
+		public FBGButton(IFBGContainerControl parent) : base(parent) {
+			BackColor = SystemColors.ButtonFace;
+		}
+		private String text = String.Empty;
+		private Font font = SystemFonts.DefaultFont;
+		private Color color = SystemColors.ControlText;
+		private Boolean pressed = false;
+		private Boolean enabled = true;
+		public String Text { get { return text; } set { text = value; Invalidate(); } }
+		public Font Font { get { return font; } set { font = value; Invalidate(); } }
+		public Color Color { get { return color; } set { color = value; Invalidate(); } }
+		public Boolean Enabled { get { return enabled; } set { enabled = value; Invalidate(); } }
+		public event EventHandler Click;
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			if (Bounds.Width == 0 || Bounds.Height == 0) return;
+			if (BackColor == SystemColors.ButtonFace) {
+				ControlPaint.DrawButton(g, new Rectangle(0, 0, Bounds.Width, Bounds.Height), enabled ? (pressed ? ButtonState.Pushed : ButtonState.Normal) : ButtonState.Inactive);
+			} else {
+				//Hackish and not completely right...
+				//Todo: borrowed from mono... possible licencing issues!?
+				g.DrawLine(new Pen(ControlPaint.LightLight(BackColor)), 0, 0, Bounds.Width, 0);
+				g.DrawLine(new Pen(ControlPaint.LightLight(BackColor)), 0, 0, 0, Bounds.Height);
+				g.DrawLine(new Pen(ControlPaint.Dark(BackColor)), 1, Bounds.Height - 2, Bounds.Width - 1, Bounds.Height - 2);
+				g.DrawLine(new Pen(ControlPaint.Dark(BackColor)), Bounds.Width - 2, 1, Bounds.Width - 2, Bounds.Height - 2);
+				g.DrawLine(new Pen(ControlPaint.DarkDark(BackColor)), 0, Bounds.Height - 1, Bounds.Width, Bounds.Height - 1);
+				g.DrawLine(new Pen(ControlPaint.DarkDark(BackColor)), Bounds.Width - 1, 1, Bounds.Width - 1, Bounds.Height - 1);
+				Graphics dc = g;
+				Rectangle rectangle = new Rectangle(0, 0, Bounds.Width - 1, Bounds.Height - 1);
+				Color ColorControl = BackColor;
+				Color ColorControlLight = ControlPaint.Light(ColorControl);
+				ButtonState state = pressed ? ButtonState.Pushed : ButtonState.Normal;
+				using (Pen NormalPen = new Pen(BackColor), LightPen = new Pen(ControlPaint.Light(BackColor)), DarkPen = new Pen(ControlPaint.Dark(BackColor))) {
+					// sadly enough, the rectangle gets always filled with a hatchbrush
+					using (HatchBrush hb = new HatchBrush(HatchStyle.Percent50, Color.FromArgb(Math.Min(255, ColorControl.R + 3), ColorControl.G, ColorControl.B), ColorControl)) {
+						dc.FillRectangle(hb, rectangle.X + 1, rectangle.Y + 1, rectangle.Width - 2, rectangle.Height - 2);
+					}
+					if ((state & ButtonState.All) == ButtonState.All || ((state & ButtonState.Checked) == ButtonState.Checked && (state & ButtonState.Flat) == ButtonState.Flat)) {
+						using (HatchBrush hb = new HatchBrush(HatchStyle.Percent50, ColorControlLight, ColorControl)) {
+							dc.FillRectangle(hb, rectangle.X + 2, rectangle.Y + 2, rectangle.Width - 4, rectangle.Height - 4);
+						}
+						dc.DrawRectangle(SystemPens.ControlDark, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1);
+					} else if ((state & ButtonState.Flat) == ButtonState.Flat) {
+						dc.DrawRectangle(SystemPens.ControlDark, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1);
+					} else if ((state & ButtonState.Checked) == ButtonState.Checked) {
+						using (HatchBrush hb = new HatchBrush(HatchStyle.Percent50, ColorControlLight, ColorControl)) {
+							dc.FillRectangle(hb, rectangle.X + 2, rectangle.Y + 2, rectangle.Width - 4, rectangle.Height - 4);
+						}
+						Pen pen = DarkPen;
+						dc.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.X, rectangle.Bottom - 2);
+						dc.DrawLine(pen, rectangle.X + 1, rectangle.Y, rectangle.Right - 2, rectangle.Y);
+
+						pen = NormalPen;
+						dc.DrawLine(pen, rectangle.X + 1, rectangle.Y + 1, rectangle.X + 1, rectangle.Bottom - 3);
+						dc.DrawLine(pen, rectangle.X + 2, rectangle.Y + 1, rectangle.Right - 3, rectangle.Y + 1);
+
+						pen = LightPen;
+						dc.DrawLine(pen, rectangle.X, rectangle.Bottom - 1, rectangle.Right - 2, rectangle.Bottom - 1);
+						dc.DrawLine(pen, rectangle.Right - 1, rectangle.Y, rectangle.Right - 1, rectangle.Bottom - 1);
+					} else if (((state & ButtonState.Pushed) == ButtonState.Pushed) && ((state & ButtonState.Normal) == ButtonState.Normal)) {
+						Pen pen = DarkPen;
+						dc.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.X, rectangle.Bottom - 2);
+						dc.DrawLine(pen, rectangle.X + 1, rectangle.Y, rectangle.Right - 2, rectangle.Y);
+
+						pen = NormalPen;
+						dc.DrawLine(pen, rectangle.X + 1, rectangle.Y + 1, rectangle.X + 1, rectangle.Bottom - 3);
+						dc.DrawLine(pen, rectangle.X + 2, rectangle.Y + 1, rectangle.Right - 3, rectangle.Y + 1);
+
+						pen = LightPen;
+						dc.DrawLine(pen, rectangle.X, rectangle.Bottom - 1, rectangle.Right - 2, rectangle.Bottom - 1);
+						dc.DrawLine(pen, rectangle.Right - 1, rectangle.Y, rectangle.Right - 1, rectangle.Bottom - 1);
+					} else if (((state & ButtonState.Inactive) == ButtonState.Inactive) || ((state & ButtonState.Normal) == ButtonState.Normal)) {
+						Pen pen = LightPen;
+						dc.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.Right - 2, rectangle.Y);
+						dc.DrawLine(pen, rectangle.X, rectangle.Y, rectangle.X, rectangle.Bottom - 2);
+
+						pen = NormalPen;
+						dc.DrawLine(pen, rectangle.X + 1, rectangle.Bottom - 2, rectangle.Right - 2, rectangle.Bottom - 2);
+						dc.DrawLine(pen, rectangle.Right - 2, rectangle.Y + 1, rectangle.Right - 2, rectangle.Bottom - 3);
+
+						pen = DarkPen;
+						dc.DrawLine(pen, rectangle.X, rectangle.Bottom - 1, rectangle.Right - 1, rectangle.Bottom - 1);
+						dc.DrawLine(pen, rectangle.Right - 1, rectangle.Y, rectangle.Right - 1, rectangle.Bottom - 2);
+					}
+				}
+			}
+			Rectangle frect = new Rectangle(Point.Empty, Bounds.Size);
+			SizeF textsize = g.MeasureString(Text, Font);
+			using (Brush b = new SolidBrush(enabled ? Color : Color.DarkGray)) {
+				g.DrawString(Text, Font, b, new PointF(Bounds.Width / 2.0f - textsize.Width / 2.0f, Bounds.Height / 2.0f - textsize.Height / 2.0f));
+			}
+		}
+		protected override void MouseDown(Point position, MouseButtons buttons) {
+			pressed = true;
+			Invalidate();
+			CaptureMouse(true);
+			base.MouseDown(position, buttons);
+		}
+		protected override void MouseUp(Point position, MouseButtons buttons) {
+			pressed = false;
+			Invalidate();
+			CaptureMouse(false);
+			if (position.X >= 0 && position.X <= Bounds.Width && position.Y >= 0 && position.Y <= Bounds.Height && enabled) RaiseEvent(Click);
+			base.MouseUp(position, buttons);
+		}
+		protected override void KeyDown(Keys key) {
+			if (key == Keys.Return || key == Keys.Space) {
+				pressed = true;
+				Invalidate();
+			}
+			base.KeyDown(key);
+		}
+		protected override void KeyUp(Keys key) {
+			if (key == Keys.Return || key == Keys.Space) {
+				if (pressed) RaiseEvent(Click);
+				pressed = false;
+				Invalidate();
+			}
+			base.KeyUp(key);
+		}
+		public void Focus() {
+			CaptureKeyboard(true);
+		}
+	}
+	public class FBGCheckBox : FBGControl {
+		public FBGCheckBox(IFBGContainerControl parent) : base(parent) { }
+		private String text = String.Empty;
+		private Font font = SystemFonts.DefaultFont;
+		private Color color = SystemColors.ControlText;
+		private Boolean _checked = false;
+		public String Text { get { return text; } set { text = value; Invalidate(); } }
+		public Font Font { get { return font; } set { font = value; Invalidate(); } }
+		public Color Color { get { return color; } set { color = value; Invalidate(); } }
+		public Boolean Checked { get { return _checked; } set { _checked = value; Invalidate(); } }
+		public event EventHandler CheckedChanged;
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			ControlPaint.DrawCheckBox(g, 0, 0, 13, 13, _checked ? ButtonState.Checked : ButtonState.Normal);
+			g.DrawString(Text, Font, new SolidBrush(Color), 15, 0);
+		}
+		protected override void MouseDown(Point position, MouseButtons buttons) {
+			Checked = !Checked;
+			RaiseEvent(CheckedChanged);
+			base.MouseDown(position, buttons);
+		}
+	}
+	public class FBGImageBox : FBGControl {
+		Image image = null;
+		PictureBoxSizeMode sizeMode = PictureBoxSizeMode.Normal;
+		Rectangle imageRect;
+		public Image Image { get { return image; } set { image = value; UpdateImageRect(false); } }
+		public FBGImageBox(IFBGContainerControl parent) : base(parent) { }
+		public PictureBoxSizeMode SizeMode { get { return sizeMode; } set { sizeMode = value; UpdateImageRect(false); } }
+		public override Rectangle Bounds {
+			get {
+				return base.Bounds;
+			}
+			set {
+				UpdateImageRect(true);
+				base.Bounds = value;
+			}
+		}
+		private void UpdateImageRect(Boolean boundsset) {
+			if (image == null) return;
+			if (!boundsset && sizeMode == PictureBoxSizeMode.AutoSize) {
+				Size = Image.Size;
+				return;
+			}
+			switch (sizeMode) {
+				case PictureBoxSizeMode.AutoSize:
+				case PictureBoxSizeMode.Normal:
+					imageRect = new Rectangle(Point.Empty, image.Size);
+					break;
+				case PictureBoxSizeMode.CenterImage:
+					imageRect = new Rectangle(Width / 2 - image.Width / 2, Height / 2 - Image.Height / 2, Width, Height);
+					break;
+				case PictureBoxSizeMode.StretchImage:
+					imageRect = new Rectangle(Point.Empty, Size);
+					break;
+				case PictureBoxSizeMode.Zoom:
+					float xrat = (float)Width / (float)image.Width;
+					float yrat = (float)Height / (float)image.Height;
+					float rat = Math.Min(xrat, yrat);
+					SizeF dispsize = new SizeF(image.Width * rat, image.Height * rat);
+					imageRect = Rectangle.Round(new RectangleF(Width / 2f - dispsize.Width / 2f, Height / 2f - dispsize.Height / 2f, dispsize.Width, dispsize.Height));
+					break;
+			}
+			if (!boundsset) Invalidate();
+		}
+		protected override void Paint(Graphics g) {
+			if (!Visible) return;
+			base.Paint(g);
+			if (image != null) g.DrawImage(image, imageRect);
+		}
+	}
+	public class FBGListBox : FBGControl {
+		private List<Object> items = new List<object>();
+		private Object selected = null;
+		private Object highlighted = null;
+		private Boolean hasScrollBar = false;
+		private Boolean hitScrollBar = false;
+		private int offset = 0;
+		private ButtonState buttonUpState = ButtonState.Normal;
+		private ButtonState buttonDownState = ButtonState.Normal;
+		private Converter<Object, String> itemFormatter = null;
+		public Converter<Object, String> ItemFormatter { get { return itemFormatter; } set { itemFormatter = value; Invalidate(); } }
+		public FBGListBox(IFBGContainerControl parent)
+			: base(parent) {
+			BackColor = Color.White;
+		}
+		public void AddItem(Object item) {
+			items.Add(item);
+			Invalidate();
+		}
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			g.DrawRectangle(Pens.DarkBlue, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
+			int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight());
+			int th = lh * items.Count;
+			int y = 2;
+			using (Pen dottedpen = new Pen(Brushes.Black)) {
+				dottedpen.DashStyle = DashStyle.Dot;
+				using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap)) {
+					for (int i = offset; i < items.Count; i++) {
+						Object item = items[i];
+						String text = itemFormatter == null ? (item == null ? String.Empty : item.ToString()) : itemFormatter(item);
+						if (item == selected) g.FillRectangle(Brushes.DarkGray, 2, y, Bounds.Width - 4, lh);
+						if (item == highlighted) g.DrawRectangle(dottedpen, 2, y, Bounds.Width - 5, lh - 1);
+						g.DrawString(text, SystemFonts.DefaultFont, SystemBrushes.WindowText, new Rectangle(3, y, Bounds.Width - 6, lh), sf);
+						y += lh;
+						if (y + lh + 2 >= Bounds.Height) break;
+					}
+				}
+			}
+			if (y < th) hasScrollBar = true;
+			if (hasScrollBar) {
+				int xoff = Bounds.Width - 17;
+				using (Brush b = new LinearGradientBrush(new Rectangle(xoff, 0, 17, 1), Color.LightGray, Color.White, LinearGradientMode.Horizontal))
+					g.FillRectangle(b, xoff, 17, 16, Bounds.Height - 17 - 17);
+				ControlPaint.DrawScrollButton(g, xoff, 1, 16, 16, ScrollButton.Up, buttonUpState);
+				ControlPaint.DrawScrollButton(g, xoff, Bounds.Height - 17, 16, 16, ScrollButton.Down, buttonDownState);
+				g.DrawRectangle(Pens.Black, new Rectangle(xoff, 17 + offset * (Bounds.Height - 17 - 17 - 20) / (items.Count - 1), 15, 20));
+			}
+		}
+		protected override void MouseDown(Point position, MouseButtons buttons) {
+			if ((buttons & MouseButtons.Left) != 0) {
+				CaptureMouse(true);
+				if (hasScrollBar && position.X > Bounds.Width - 17) {
+					hitScrollBar = true;
+					if (position.Y < 17) {
+						offset--;
+						buttonUpState = ButtonState.Pushed;
+					} else if (position.Y > Bounds.Height - 17) {
+						offset++;
+						buttonDownState = ButtonState.Pushed;
+					} else {
+						offset = (int)Math.Round((position.Y - 17) * (items.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
+					}
+					if (offset < 0) offset = 0;
+					if (offset >= items.Count) offset = items.Count - 1;
+					Invalidate();
+				} else {
+					MouseHandler(position, buttons);
+				}
+			}
+		}
+		protected override void MouseMove(Point position, MouseButtons buttons) {
+			if (hitScrollBar) {
+				if (position.Y < 17) {
+				} else if (position.Y > Bounds.Height - 17) {
+				} else {
+					offset = (int)Math.Round((position.Y - 17) * (items.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
+					if (offset < 0) offset = 0;
+					if (offset >= items.Count) offset = items.Count - 1;
+					Invalidate();
+				}
+				return;
+			}
+			MouseHandler(position, buttons);
+		}
+		protected override void MouseUp(Point position, MouseButtons buttons) {
+			if ((buttons & MouseButtons.Left) != 0) {
+				CaptureMouse(false);
+				buttonUpState = buttonDownState = ButtonState.Normal;
+				Invalidate();
+				if (hitScrollBar) {
+					hitScrollBar = false;
+					return;
+				}
+			}
+			if (hitScrollBar) return;
+			MouseHandler(position, buttons);
+		}
+		private void MouseHandler(Point position, MouseButtons buttons) {
+			if ((buttons & MouseButtons.Left) != 0) {
+				int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight());
+				int i = (position.Y - 2) / lh + offset;
+				if (i < 0) i = 0;
+				if (i >= items.Count) i = items.Count - 1;
+				Boolean changed = false;
+				Object current = items[i];
+				if (!ReferenceEquals(highlighted, current)) changed = true;
+				highlighted = current;
+				if ((new Rectangle(Point.Empty, Bounds.Size)).Contains(position)) {
+					if (!ReferenceEquals(selected, current)) changed = true;
+					selected = current;
+				}
+				if (changed) Invalidate();
+			}
+		}
+	}
+	public class FBGDomainUpDown : FBGControl {
+		private List<Object> items = new List<object>();
+		private int selectedIndex = -1;
+		private ButtonState buttonUpState = ButtonState.Normal;
+		private ButtonState buttonDownState = ButtonState.Normal;
+		private Converter<Object, String> itemFormatter = null;
+		public Boolean AllowSelectEmpty { get; set; }
+		public Converter<Object, String> ItemFormatter { get { return itemFormatter; } set { itemFormatter = value; Invalidate(); } }
+		public event EventHandler SelectedIndexChanged;
+		public FBGDomainUpDown(IFBGContainerControl parent)
+			: base(parent) {
+			BackColor = Color.White;
+		}
+		public void AddItem(Object item) {
+			items.Add(item);
+			Invalidate();
+		}
+		public void RemoveItem(Object item) {
+			items.Remove(item);
+			FixSelectedIndex(0);
+		}
+		public void RemoveItem(int index) {
+			items.RemoveAt(index);
+			FixSelectedIndex(0);
+		}
+		public int SelectedIndex {
+			get { return selectedIndex; }
+			set {
+				if (value < -2 || value >= items.Count) throw new ArgumentOutOfRangeException("value", "Value must be between -1 and the number of items minus one");
+				if (selectedIndex == value) return;
+				selectedIndex = value;
+				Invalidate();
+				RaiseEvent(SelectedIndexChanged);
+			}
+		}
+		public Object SelectedItem {
+			get { return selectedIndex == -1 ? null : items[selectedIndex]; }
+			set {
+				if (value == null) {
+					SelectedIndex = -1;
+				} else {
+					for (int i = 0; i < items.Count; i++) {
+						if (items[i] == value) {
+							SelectedIndex = i;
+							break;
+						}
+					}
+				}
+			}
+		}
+		private void FixSelectedIndex(int change) {
+			int value = selectedIndex;
+			if (value == 0 && change == -1 && !AllowSelectEmpty) change = 0;
+			value += change;
+			if (value < -1) value = -1;
+			if (value >= items.Count) value = items.Count - 1;
+			SelectedIndex = value;
+		}
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+			g.DrawRectangle(Pens.DarkBlue, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
+			int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight());
+			if (selectedIndex == -1) {
+				g.FillRectangle(Brushes.DarkGray, 2, 2, Bounds.Width - 4 - 16, Bounds.Height - 4);
+			} else {
+				using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap)) {
+					sf.LineAlignment = StringAlignment.Center;
+					Object item = items[selectedIndex];
+					String text = itemFormatter == null ? (item == null ? String.Empty : item.ToString()) : itemFormatter(item);
+					g.FillRectangle(Brushes.LightGray, 2, 2, Bounds.Width - 4 - 16, Bounds.Height - 4);
+					g.DrawString(text, SystemFonts.DefaultFont, SystemBrushes.WindowText, new Rectangle(3, 2, Bounds.Width - 6 - 16, Bounds.Height - 4), sf);
+				}
+			}
+			int xoff = Bounds.Width - 17;
+			int he = (Bounds.Height - 2) / 2;
+			ControlPaint.DrawScrollButton(g, xoff, 1, 16, he, ScrollButton.Up, buttonUpState);
+			ControlPaint.DrawScrollButton(g, xoff, Bounds.Height - he - 1, 16, he, ScrollButton.Down, buttonDownState);
+		}
+		protected override void MouseDown(Point position, MouseButtons buttons) {
+			CaptureKeyboard(true);
+			if ((buttons & MouseButtons.Left) != 0) {
+				CaptureMouse(true);
+				if (position.X > Bounds.Width - 17) {
+					if (position.Y < Bounds.Height / 2) {
+						buttonUpState = ButtonState.Pushed;
+						FixSelectedIndex(-1);
+					} else {
+						buttonDownState = ButtonState.Pushed;
+						FixSelectedIndex(1);
+					}
+					Invalidate(new Rectangle(Bounds.Width - 16, 0, 16, Bounds.Height));
+				}
+			}
+		}
+		protected override void MouseUp(Point position, MouseButtons buttons) {
+			if ((buttons & MouseButtons.Left) != 0) {
+				CaptureMouse(false);
+				buttonUpState = buttonDownState = ButtonState.Normal;
+				Invalidate(new Rectangle(Bounds.Width - 16, 0, 16, Bounds.Height));
+			}
+		}
+		protected override void KeyDown(Keys key) {
+			base.KeyDown(key);
+			if (key == Keys.Down) {
+				FixSelectedIndex(1);
+			} else if (key == Keys.Up) {
+				FixSelectedIndex(-1);
+			}
+		}
+	}
+	public interface IFBGTreeParent {
+		FBGTreeView TreeView { get; }
+		int Depth { get; }
+		void AddChild(FBGTreeNode node);
+		void RemoveChild(FBGTreeNode node);
+	}
+	public class FBGTreeNode : IFBGTreeParent {
+		private List<FBGTreeNode> children = new List<FBGTreeNode>();
+		private Boolean expanded = true;
+		private Boolean hasCheckBox = false;
+		private Boolean isChecked = false;
+		private Object item;
+
+		public FBGTreeView TreeView { get; private set; }
+		public int Depth { get; private set; }
+		public Object Tag { get; set; }
+		public IFBGTreeParent Parent { get; private set; }
+
+		public IList<FBGTreeNode> Children { get { return children.AsReadOnly(); } }
+
+		public Object Item {
+			get { return item; }
+			set {
+				item = value;
+				Invalidate();
+			}
+		}
+		public Boolean Expanded {
+			get { return expanded; }
+			set {
+				if (expanded == value) return;
+				expanded = value;
+				UpdateTree();
+			}
+		}
+		public Boolean HasCheckBox {
+			get { return hasCheckBox; }
+			set {
+				if (hasCheckBox == value) return;
+				hasCheckBox = value;
+				Invalidate();
+			}
+		}
+		public Boolean Checked {
+			get { return isChecked; }
+			set {
+				if (isChecked == value) return;
+				isChecked = value;
+				Invalidate();
+				if (TreeView != null) TreeView.RaiseNodeCheckedChanged(this);
+			}
+		}
+
+		public FBGTreeNode(IFBGTreeParent parent, Object item) {
+			this.TreeView = parent.TreeView;
+			this.Depth = parent.Depth + 1;
+			this.Parent = parent;
+			this.item = item;
+			parent.AddChild(this);
+		}
+
+		public void Remove() {
+			Parent.RemoveChild(this);
+		}
+		void IFBGTreeParent.AddChild(FBGTreeNode node) {
+			children.Add(node);
+			if (Expanded) UpdateTree();
+			else Invalidate();
+		}
+		void IFBGTreeParent.RemoveChild(FBGTreeNode node) {
+			children.Remove(node);
+			TreeView.ReleaseNodeFromTree(node);
+			if (Expanded) UpdateTree();
+			else Invalidate();
+		}
+		public void Invalidate() {
+			if (TreeView != null) TreeView.Invalidate(this);
+		}
+		private void UpdateTree() {
+			if (TreeView != null) TreeView.UpdateView();
+		}
+		public FBGTreeNode AddNode(Object item) {
+			return new FBGTreeNode(this, item);
+		}
+	}
+	public class FBGTreeView : FBGControl, IFBGTreeParent {
+		private List<FBGTreeNode> items = new List<FBGTreeNode>();
+		private List<FBGTreeNode> itemsView = new List<FBGTreeNode>();
+		private FBGTreeNode selected = null;
+		private FBGTreeNode highlighted = null;
+		private Boolean hasScrollBar = false;
+		private Boolean hitScrollBar = false;
+		private int offset = 0;
+		private ButtonState buttonUpState = ButtonState.Normal;
+		private ButtonState buttonDownState = ButtonState.Normal;
+		private Converter<Object, String> itemFormatter = null;
+
+		public Converter<Object, String> ItemFormatter { get { return itemFormatter; } set { itemFormatter = value; Invalidate(); } }
+		public FBGTreeNode SelectedNode { get { return selected; } set { if (selected != value) { selected = value; Invalidate(); RaiseEvent(SelectedNodeChanged); } } }
+		public IList<FBGTreeNode> Nodes { get { return items.AsReadOnly(); } }
+
+		public event EventHandler SelectedNodeChanged;
+		public event EventHandler NodeCheckedChanged;
+		
+		public FBGTreeView(IFBGContainerControl parent) : base(parent) {
+			BackColor = Color.White;
+		}
+		FBGTreeView IFBGTreeParent.TreeView { get { return this; } }
+		int IFBGTreeParent.Depth { get { return -1; } }
+		void IFBGTreeParent.AddChild(FBGTreeNode node) {
+			items.Add(node);
+			UpdateView();
+		}
+		void IFBGTreeParent.RemoveChild(FBGTreeNode node) {
+			items.Remove(node);
+			ReleaseNodeFromTree(node);
+			UpdateView();
+		}
+		public FBGTreeNode AddNode(Object item) { return new FBGTreeNode(this, item); }
+		internal void ReleaseNodeFromTree(FBGTreeNode node) {
+			if (highlighted == node) highlighted = null;
+			if (selected == node) SelectedNode = null;
+		}
+		internal void RaiseNodeCheckedChanged(FBGTreeNode node) {
+			RaiseEvent(NodeCheckedChanged);
+		}
+		internal void UpdateView() {
+			List<FBGTreeNode> newView = new List<FBGTreeNode>();
+			Stack<Queue<FBGTreeNode>> stack = new Stack<Queue<FBGTreeNode>>();
+			stack.Push(new Queue<FBGTreeNode>(items));
+			while (stack.Count > 0) {
+				Queue<FBGTreeNode> list = stack.Peek();
+				if (list.Count == 0) {
+					stack.Pop();
+					continue;
+				}
+				FBGTreeNode item = list.Dequeue();
+				newView.Add(item);
+				if (item.Expanded) stack.Push(new Queue<FBGTreeNode>(item.Children));
+			}
+			itemsView = newView;
+			Invalidate();
+		}
+		internal void Invalidate(FBGTreeNode node) {
+			int i = itemsView.IndexOf(node);
+			if (i == -1) return;
+			int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight() / 2.0) * 2;
+			Invalidate(new Rectangle(1, i * lh, Bounds.Width - 1, lh));
+		}
+		protected override void Paint(Graphics g) {
+			base.Paint(g);
+
+			int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight() / 2.0) * 2;
+			int th = lh * itemsView.Count;
+			hasScrollBar = offset > 0 || th + 2 > Bounds.Height;
+			int y = 2;
+			using (Pen dottedpen = new Pen(Brushes.Black)) {
+				dottedpen.DashStyle = DashStyle.Dot;
+				using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap)) {
+					int lw = Bounds.Width - 2;
+					if (hasScrollBar) lw -= 17;
+					for (int i = offset; i < itemsView.Count; i++) {
+						FBGTreeNode item = itemsView[i];
+						if (y + 2 < Bounds.Height) {
+							Object obj = item.Item;
+							String text = itemFormatter == null ? (obj == null ? String.Empty : obj.ToString()) : itemFormatter(obj);
+							if (item == selected) g.FillRectangle(Brushes.DarkGray, 2, y, lw - 2, lh);
+							if (item == highlighted) g.DrawRectangle(dottedpen, 2, y, lw - 3, lh - 1);
+							int x = 3 + 19 * item.Depth + 14;
+							if (item.HasCheckBox) {
+								x += 2;
+								ControlPaint.DrawCheckBox(g, x, y, lh, lh, item.Checked ? ButtonState.Checked : ButtonState.Normal);
+								x += lh + 1;
+							}
+							g.DrawString(text, SystemFonts.DefaultFont, SystemBrushes.WindowText, new Rectangle(x, y, lw - x, lh), sf);
+						}
+						int upto = y + 2 + 4 - 8;
+						for (int j = i - 1; j >= 0; j--) {
+							if (itemsView[j].Depth < item.Depth) {
+								break;
+							}
+							if (itemsView[j].Depth == item.Depth) {
+								if (itemsView[j].Children.Count > 0) {
+									upto = 2 + lh * (j - offset) + 10;
+								} else {
+									upto = 2 + lh * (j - offset) + 6;
+								}
+								break;
+							}
+							if (j <= offset) {
+								upto = 2 + 2 + 4 - 8;
+								break;
+							}
+						}
+						if (item.Children.Count > 0) {
+							g.DrawRectangle(Pens.Black, 3 + 19 * item.Depth, y + 2, 8, 8);
+							g.DrawLine(Pens.Black, 3 + 19 * item.Depth + 2, y + 2 + 4, 3 + 19 * item.Depth + 6, y + 2 + 4);
+							if (!item.Expanded) g.DrawLine(Pens.Black, 3 + 19 * item.Depth + 4, y + 4, 3 + 19 * item.Depth + 4, y + 2 + 6);
+
+							g.DrawLine(dottedpen, 3 + 19 * item.Depth + 8, y + 2 + 4, 3 + 19 * item.Depth + 14, y + 2 + 4);
+							g.DrawLine(dottedpen, 3 + 19 * item.Depth + 4, y + 2 + 4 - 6, 3 + 19 * item.Depth + 4, upto);
+						} else {
+							g.DrawLine(dottedpen, 3 + 19 * item.Depth + 4, y + 2 + 4, 3 + 19 * item.Depth + 14, y + 2 + 4);
+							g.DrawLine(dottedpen, 3 + 19 * item.Depth + 4, y + 2 + 4 - 2, 3 + 19 * item.Depth + 4, upto);
+						}
+						y += lh;
+						//if (y + lh + 2 >= Bounds.Height && item.Depth == 0) break;
+						if (y + 2 >= Bounds.Height && item.Depth == 0) break;
+					}
+				}
+			}
+			//if (y < th) hasScrollBar = true;
+			//hasScrollBar = true;
+			if (hasScrollBar) {
+				int xoff = Bounds.Width - 17;
+				using (Brush b = new LinearGradientBrush(new Rectangle(xoff, 0, 17, 1), Color.LightGray, Color.White, LinearGradientMode.Horizontal))
+					g.FillRectangle(b, xoff, 17, 16, Bounds.Height - 17 - 17);
+				ControlPaint.DrawScrollButton(g, xoff, 1, 16, 16, ScrollButton.Up, buttonUpState);
+				ControlPaint.DrawScrollButton(g, xoff, Bounds.Height - 17, 16, 16, ScrollButton.Down, buttonDownState);
+				g.DrawRectangle(Pens.Black, new Rectangle(xoff, 17 + offset * (Bounds.Height - 17 - 17 - 20) / (itemsView.Count - 1), 15, 20));
+			}
+
+			g.DrawRectangle(Pens.DarkBlue, 0, 0, Bounds.Width - 1, Bounds.Height - 1);
+		}
+		protected override void MouseDown(Point position, MouseButtons buttons) {
+			CaptureKeyboard(true);
+			if ((buttons & MouseButtons.Left) != 0) {
+				CaptureMouse(true);
+				if (hasScrollBar && position.X > Bounds.Width - 17) {
+					hitScrollBar = true;
+					if (position.Y < 17) {
+						offset--;
+						buttonUpState = ButtonState.Pushed;
+					} else if (position.Y > Bounds.Height - 17) {
+						offset++;
+						buttonDownState = ButtonState.Pushed;
+					} else {
+						offset = (int)Math.Round((position.Y - 17) * (itemsView.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
+					}
+					if (offset < 0) offset = 0;
+					if (offset >= itemsView.Count) offset = itemsView.Count - 1;
+					Invalidate();
+				} else {
+					MouseHandler(position, buttons, true);
+				}
+			}
+		}
+		protected override void MouseMove(Point position, MouseButtons buttons) {
+			if (hitScrollBar) {
+				if (position.Y < 17) {
+				} else if (position.Y > Bounds.Height - 17) {
+				} else {
+					offset = (int)Math.Round((position.Y - 17) * (itemsView.Count - 1) / (double)(Bounds.Height - 17 - 17 - 10));
+					if (offset < 0) offset = 0;
+					if (offset >= itemsView.Count) offset = itemsView.Count - 1;
+					Invalidate();
+				}
+				return;
+			}
+			MouseHandler(position, buttons, false);
+		}
+		protected override void MouseUp(Point position, MouseButtons buttons) {
+			if ((buttons & MouseButtons.Left) != 0) {
+				CaptureMouse(false);
+				buttonUpState = buttonDownState = ButtonState.Normal;
+				Invalidate();
+				if (hitScrollBar) {
+					hitScrollBar = false;
+					return;
+				}
+			}
+			if (hitScrollBar) return;
+			MouseHandler(position, buttons, false);
+		}
+		private void MouseHandler(Point position, MouseButtons buttons, Boolean down) {
+			if ((buttons & MouseButtons.Left) != 0 && itemsView.Count > 0) {
+				int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight() / 2.0) * 2;
+				int i = (position.Y - 2) / lh + offset;
+				if (i < 0) i = 0;
+				if (i >= itemsView.Count) i = itemsView.Count - 1;
+				Boolean changed = false;
+				FBGTreeNode current = itemsView[i];
+				if (!ReferenceEquals(highlighted, current)) changed = true;
+				highlighted = current;
+				if (current.Children.Count > 0 && (new Rectangle(3 + 19 * current.Depth, 2 + lh * (i - offset) + 2, 8, 8)).Contains(position)) {
+					if (down) current.Expanded = !current.Expanded;
+				} else if (current.HasCheckBox && (new Rectangle(3 + 19 * current.Depth + 14 + 2, 2 + lh * (i - offset), lh, lh)).Contains(position)) {
+					if (down) current.Checked = !current.Checked;
+				} else if ((new Rectangle(Point.Empty, Bounds.Size)).Contains(position)) {
+					SelectedNode = current;
+					changed = false;
+				}
+				if (changed) Invalidate();
+			}
+		}
+		protected override void KeyDown(Keys key) {
+			base.KeyDown(key);
+			if (key == Keys.Up) {
+				int i = itemsView.IndexOf(selected);
+				i--;
+				if (i >= 0) SelectAndScrollIntoView(itemsView[i]);
+			} else if (key == Keys.Down) {
+				int i = itemsView.IndexOf(selected);
+				i++;
+				if (i < itemsView.Count) SelectAndScrollIntoView(itemsView[i]);
+			} else if (key == Keys.Left && selected != null) {
+				if (selected.Expanded && selected.Children.Count > 0) {
+					selected.Expanded = false;
+				} else {
+					FBGTreeNode tn = selected.Parent as FBGTreeNode;
+					if (tn != null) SelectAndScrollIntoView(tn);
+				}
+			} else if (key == Keys.Right && selected != null) {
+				if (!selected.Expanded && selected.Children.Count > 0) {
+					selected.Expanded = true;
+				} else if (selected.Children.Count > 0) {
+					SelectAndScrollIntoView(selected.Children[0]);
+				}
+			} else if (key == Keys.Space && selected != null) {
+				if (selected.HasCheckBox) selected.Checked = !selected.Checked;
+			}
+		}
+		private void SelectAndScrollIntoView(FBGTreeNode tn) {
+			int i = itemsView.IndexOf(tn);
+			if (i == -1) {
+				for (FBGTreeNode tp = tn.Parent as FBGTreeNode; tp != null; tp = tp.Parent as FBGTreeNode) if (!tp.Expanded) tp.Expanded = true;
+				i = itemsView.IndexOf(tn);
+			}
+			if (i != -1) {
+				int lh = (int)Math.Ceiling(SystemFonts.DefaultFont.GetHeight() / 2.0) * 2;
+				if (i < offset) offset = i;
+				offset = Math.Max(offset, i - Bounds.Height / lh + 1);
+			}
+			highlighted = tn;
+			SelectedNode = tn;
+		}
+	}
+}
--- a/UCIS.csproj	Mon Apr 15 01:17:21 2013 +0200
+++ b/UCIS.csproj	Mon Apr 15 02:04:36 2013 +0200
@@ -37,7 +37,9 @@
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Data" />
+    <Reference Include="System.Drawing" />
     <Reference Include="System.Management" />
+    <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
@@ -45,6 +47,9 @@
     <Compile Include="Cci\CciCommand.cs" />
     <Compile Include="Database.cs" />
     <None Include="DBReader.cs" />
+    <Compile Include="FBGUI.cs">
+      <SubType>Component</SubType>
+    </Compile>
     <Compile Include="NaCl\APIv2.cs">
       <SubType>Code</SubType>
     </Compile>
@@ -167,6 +172,8 @@
     <Compile Include="Util\PipeStream.cs" />
     <Compile Include="Util\PrebufferingStream.cs" />
     <Compile Include="Util\QueuedPacketStream.cs" />
+    <Compile Include="VNCServer\IFramebuffer.cs" />
+    <Compile Include="VNCServer\VNCServer.cs" />
     <Compile Include="Windows\ServiceManager.cs" />
     <Compile Include="Xml\PolicyFile.cs" />
     <Compile Include="Xml\Server.cs" />
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VNCServer/IFramebuffer.cs	Mon Apr 15 02:04:36 2013 +0200
@@ -0,0 +1,62 @@
+using System;
+using System.Drawing;
+
+namespace UCIS.VNCServer {
+	/// <summary>
+	/// A generic graphic framebuffer interface that provides functions to draw to and copy from the framebuffer
+	/// </summary>
+	public interface IFramebuffer {
+		/// <summary>
+		/// The width of the framebuffer in pixels
+		/// </summary>
+		int Width { get; }
+		/// <summary>
+		/// The height of the framebuffer in pixels
+		/// </summary>
+		int Height { get; }
+		/// <summary>
+		/// Clear the display area
+		/// </summary>
+		void Clear();
+		/// <summary>
+		/// Draw part of an Image to the screen
+		/// </summary>
+		/// <remarks>Best performance is provided with Bitmap images.</remarks>
+		/// <param name="image">The Image object to copy from</param>
+		/// <param name="srcrect">The area in the image to copy</param>
+		/// <param name="dest">The position on screen to copy to</param>
+		void DrawImage(Image image, Rectangle srcrect, Point dest);
+		/// <summary>
+		/// Draw part of a 32 bits per pixel bitmap to the screen
+		/// </summary>
+		/// <param name="bitmap">The array that contains the Bitmap data (one pixel per entry)</param>
+		/// <param name="bmwidth">The width of the Bitmap data</param>
+		/// <param name="srcrect">The area in the bitmap to copy</param>
+		/// <param name="dest">The position on screen to copy to</param>
+		void DrawPixels(int[] bitmap, int bmwidth, Rectangle srcrect, Point dest);
+		/// <summary>
+		/// Draw part of a 32 bits per pixel bitmap to the screen
+		/// </summary>
+		/// <param name="bitmap">The pointer to the start of the Bitmap data</param>
+		/// <param name="bmwidth">The width of the Bitmap data</param>
+		/// <param name="srcrect">The area in the bitmap to copy</param>
+		/// <param name="dest">The position on screen to copy to</param>
+		void DrawPixels(IntPtr bitmap, int bmwidth, Rectangle srcrect, Point dest);
+
+		/// <summary>
+		/// Copy an area on the display
+		/// </summary>
+		/// <param name="srcrect">The area to copy from</param>
+		/// <param name="dest">Where to copy the area to</param>
+		void CopyRectangle(Rectangle srcrect, Point dest);
+
+		/// <summary>
+		/// Copy an area from this framebuffer to another framebuffer
+		/// </summary>
+		/// <remarks>Not all framebuffer implementations may support this operation, notably because some framebuffers can only be written to</remarks>
+		/// <param name="srcrect">The area to copy</param>
+		/// <param name="destbuffer">The framebuffer to copy to</param>
+		/// <param name="destposition">The position in the destination framebuffer to copy to</param>
+		void CopyRectangleTo(Rectangle srcrect, IFramebuffer destbuffer, Point destposition);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VNCServer/VNCServer.cs	Mon Apr 15 02:04:36 2013 +0200
@@ -0,0 +1,1325 @@
+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 {
+		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);
+		}
+		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 = new HTTPContentProviderFunction(HandleClient);
+			httpserver.ServeFlashPolicyFile = true;
+		}
+		public override void Listen(EndPoint ep) {
+			httpserver.Listen(ep);
+		}
+		public override void Close() {
+			httpserver.Dispose();
+		}
+		public void HandleClient(HTTPContext context) {
+			WebSocketPacketStream stream = new WebSocketPacketStream(context);
+			ClientAccepted(new VNCServerConnection(stream, context.Socket));
+		}
+	}
+	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;
+			}
+		}
+	}
+}