# HG changeset patch # User Ivo Smits # Date 1365984276 -7200 # Node ID 644a923bca98dd7ed82cd8bfce32f0a4eeb0140f # Parent 5b14fed54a899df78670435a77e286b8962f6f4f Added FBGUI and VNCServer diff -r 5b14fed54a89 -r 644a923bca98 FBGUI.cs --- /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(EventHandler 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 controls = new List(); + 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)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 dockStyles = new Dictionary(); + private Dictionary anchorStyles = new Dictionary(); + 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 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 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 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 windowstates = new Dictionary(); + 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 items = new List(); + 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 itemFormatter = null; + public Converter 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 items = new List(); + private int selectedIndex = -1; + private ButtonState buttonUpState = ButtonState.Normal; + private ButtonState buttonDownState = ButtonState.Normal; + private Converter itemFormatter = null; + public Boolean AllowSelectEmpty { get; set; } + public Converter 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 children = new List(); + 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 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 items = new List(); + private List itemsView = new List(); + 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 itemFormatter = null; + + public Converter 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 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 newView = new List(); + Stack> stack = new Stack>(); + stack.Push(new Queue(items)); + while (stack.Count > 0) { + Queue list = stack.Peek(); + if (list.Count == 0) { + stack.Pop(); + continue; + } + FBGTreeNode item = list.Dequeue(); + newView.Add(item); + if (item.Expanded) stack.Push(new Queue(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; + } + } +} diff -r 5b14fed54a89 -r 644a923bca98 UCIS.csproj --- 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 @@ + + @@ -45,6 +47,9 @@ + + Component + Code @@ -167,6 +172,8 @@ + + diff -r 5b14fed54a89 -r 644a923bca98 VNCServer/IFramebuffer.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 { + /// + /// A generic graphic framebuffer interface that provides functions to draw to and copy from the framebuffer + /// + public interface IFramebuffer { + /// + /// The width of the framebuffer in pixels + /// + int Width { get; } + /// + /// The height of the framebuffer in pixels + /// + int Height { get; } + /// + /// Clear the display area + /// + void Clear(); + /// + /// Draw part of an Image to the screen + /// + /// Best performance is provided with Bitmap images. + /// The Image object to copy from + /// The area in the image to copy + /// The position on screen to copy to + void DrawImage(Image image, Rectangle srcrect, Point dest); + /// + /// Draw part of a 32 bits per pixel bitmap to the screen + /// + /// The array that contains the Bitmap data (one pixel per entry) + /// The width of the Bitmap data + /// The area in the bitmap to copy + /// The position on screen to copy to + void DrawPixels(int[] bitmap, int bmwidth, Rectangle srcrect, Point dest); + /// + /// Draw part of a 32 bits per pixel bitmap to the screen + /// + /// The pointer to the start of the Bitmap data + /// The width of the Bitmap data + /// The area in the bitmap to copy + /// The position on screen to copy to + void DrawPixels(IntPtr bitmap, int bmwidth, Rectangle srcrect, Point dest); + + /// + /// Copy an area on the display + /// + /// The area to copy from + /// Where to copy the area to + void CopyRectangle(Rectangle srcrect, Point dest); + + /// + /// Copy an area from this framebuffer to another framebuffer + /// + /// Not all framebuffer implementations may support this operation, notably because some framebuffers can only be written to + /// The area to copy + /// The framebuffer to copy to + /// The position in the destination framebuffer to copy to + void CopyRectangleTo(Rectangle srcrect, IFramebuffer destbuffer, Point destposition); + } +} diff -r 5b14fed54a89 -r 644a923bca98 VNCServer/VNCServer.cs --- /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 clients = new List(); + 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 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 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 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 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 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 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 dirtyrects = new List(); + 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 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 sending = new List(); + 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 subrects = new List(); + 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 remaining = new Queue(); + 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; + } + } + } +}