/// <summary> /// Decodes a <see cref="VncPixelFormat"/> from a <see cref="byte"/> array. /// </summary> /// <param name="buffer"> /// The <see cref="byte"/> array which contains the <see cref="VncPixelFormat"/> data. /// </param> /// <param name="offset"> /// The first index in the <paramref name="buffer"/> which contains the <see cref="VncPixelFormat"/> /// data. /// </param> /// <returns> /// A <see cref="VncPixelFormat"/> object. /// </returns> internal static VncPixelFormat Decode(byte[] buffer, int offset) { var bitsPerPixel = buffer[offset + 0]; var depth = buffer[offset + 1]; var isLittleEndian = buffer[offset + 2] == 0; var isPalettized = buffer[offset + 3] == 0; var redBits = BitsFromMax(VncUtility.DecodeUInt16BE(buffer, offset + 4)); var greenBits = BitsFromMax(VncUtility.DecodeUInt16BE(buffer, offset + 6)); var blueBits = BitsFromMax(VncUtility.DecodeUInt16BE(buffer, offset + 8)); var redShift = buffer[offset + 10]; var greenShift = buffer[offset + 11]; var blueShift = buffer[offset + 12]; return(new VncPixelFormat( bitsPerPixel, depth, redBits, redShift, greenBits, greenShift, blueBits, blueShift, isLittleEndian, isPalettized)); }
/// <summary> /// Writes a <see cref="VncRectangle"/> to the current position in the stream and advances the position within the stream by 8 bytes. /// </summary> /// <param name="region"> /// The <see cref="VncRectangle"/> to write to the stream. /// </param> public void SendRectangle(VncRectangle region) { var buffer = new byte[8]; VncUtility.EncodeUInt16BE(buffer, 0, (ushort)region.X); VncUtility.EncodeUInt16BE(buffer, 2, (ushort)region.Y); VncUtility.EncodeUInt16BE(buffer, 4, (ushort)region.Width); VncUtility.EncodeUInt16BE(buffer, 6, (ushort)region.Height); this.Send(buffer); }
/// <summary> /// Serializes this <see cref="VncPixelFormat"/> to a <see cref="byte"/> array. /// </summary> /// <param name="buffer"> /// The <see cref="byte"/> array to which to encode the <see cref="VncPixelFormat"/> object. /// </param> /// <param name="offset"> /// The first <see cref="byte"/> at which to store the <see cref="VncPixelFormat"/> data. /// </param> internal void Encode(byte[] buffer, int offset) { buffer[offset + 0] = (byte)this.BitsPerPixel; buffer[offset + 1] = (byte)this.BitDepth; buffer[offset + 2] = (byte)(this.IsLittleEndian ? 0 : 1); buffer[offset + 3] = (byte)(this.IsPalettized ? 0 : 1); VncUtility.EncodeUInt16BE(buffer, offset + 4, this.RedMax); VncUtility.EncodeUInt16BE(buffer, offset + 6, this.GreenMax); VncUtility.EncodeUInt16BE(buffer, offset + 8, this.BlueMax); buffer[offset + 10] = (byte)this.RedShift; buffer[offset + 11] = (byte)this.GreenShift; buffer[offset + 12] = (byte)this.BlueShift; }
private void SendFramebufferUpdateRequest(bool incremental, int x, int y, int width, int height) { var p = new byte[10]; p[0] = (byte)3; p[1] = (byte)(incremental ? 1 : 0); VncUtility.EncodeUInt16BE(p, 2, (ushort)x); VncUtility.EncodeUInt16BE(p, 4, (ushort)y); VncUtility.EncodeUInt16BE(p, 6, (ushort)width); VncUtility.EncodeUInt16BE(p, 8, (ushort)height); this.c.Send(p); }
public void SendKeyEvent(KeySym keysym, bool pressed) { var p = new byte[8]; p[0] = (byte)4; p[1] = (byte)(pressed ? 1 : 0); VncUtility.EncodeUInt32BE(p, 4, (uint)keysym); if (this.IsConnected) { this.c.Send(p); } }
/// <summary> /// Sends a pointer event to the VNC server to indicate mouse motion, a button click, etc. /// </summary> /// <param name="x">The X coordinate of the mouse.</param> /// <param name="y">The Y coordinate of the mouse.</param> /// <param name="pressedButtons"> /// A bit mask of pressed mouse buttons, in X11 convention: 1 is left, 2 is middle, and 4 is right. /// Mouse wheel scrolling is treated as a button event: 8 for up and 16 for down. /// </param> public void SendPointerEvent(int x, int y, int pressedButtons) { var p = new byte[6]; p[0] = (byte)5; p[1] = (byte)pressedButtons; VncUtility.EncodeUInt16BE(p, 2, (ushort)x); VncUtility.EncodeUInt16BE(p, 4, (ushort)y); if (this.IsConnected) { this.c.Send(p); } }
/// <summary> /// Notifies the server that the local clipboard has changed. /// If you are implementing clipboard integration, use this to set the remote clipboard. /// </summary> /// <param name="data">The contents of the local clipboard.</param> public void SendLocalClipboardChange(string data) { Throw.If.Null(data, "data"); var bytes = VncStream.EncodeString(data); var p = new byte[8 + bytes.Length]; p[0] = (byte)6; VncUtility.EncodeUInt32BE(p, 4, (uint)bytes.Length); Array.Copy(bytes, 0, p, 8, bytes.Length); if (this.IsConnected) { this.c.Send(p); } }
/// <summary> /// Writes a <see cref="uint"/> in big endian encoding to the current position in the stream and advances the position within the stream by four bytes. /// </summary> /// <param name="value"> /// The <see cref="uint"/> to write to the stream. /// </param> public void SendUInt32BE(uint value) { this.Send(VncUtility.EncodeUInt32BE(value)); }
/// <summary> /// Writes a <see cref="ushort"/> in big endian encoding to the current position in the stream and advances the position within the stream by two bytes. /// </summary> /// <param name="value"> /// The <see cref="ushort"/> to write to the stream. /// </param> public void SendUInt16BE(ushort value) { this.Send(VncUtility.EncodeUInt16BE(value)); }
/// <summary> /// Reads a <see cref="uint"/> in big-endian encoding from the stream and advances the /// position within the stream by two bytes. /// </summary> /// <returns> /// The <see cref="uint"/> which was read from the stream. /// </returns> public uint ReceiveUInt32BE() { return(VncUtility.DecodeUInt32BE(this.Receive(4), 0)); }
/// <summary> /// Reads a <see cref="ushort"/> in big-endian encoding from the stream and advances the /// position within the stream by two bytes. /// </summary> /// <returns> /// The <see cref="ushort"/> which was read from the stream. /// </returns> public ushort ReceiveUInt16BE() { return(VncUtility.DecodeUInt16BE(this.Receive(2), 0)); }
private bool HandleFramebufferUpdate() { this.c.ReceiveByte(); // padding var numRects = this.c.ReceiveUInt16BE(); var rects = new List <VncRectangle>(); var encoding = VncEncoding.DesktopSize; for (int i = 0; i < numRects; i++) { var r = this.c.ReceiveRectangle(); int x = r.X, y = r.Y, w = r.Width, h = r.Height; VncStream.SanityCheck(w > 0 && w < 0x8000); VncStream.SanityCheck(h > 0 && h < 0x8000); int fbW = this.Framebuffer.Width, fbH = this.Framebuffer.Height, bpp = this.Framebuffer.PixelFormat.BytesPerPixel; var inRange = w <= fbW && h <= fbH && x <= fbW - w && y <= fbH - h; byte[] pixels; encoding = (VncEncoding)this.c.ReceiveUInt32BE(); switch (encoding) { case VncEncoding.Hextile: // KVM seems to avoid this now that I support Zlib. var background = new byte[bpp]; var foreground = new byte[bpp]; for (int ty = 0; ty < h; ty += 16) { int th = Math.Min(16, h - ty); for (int tx = 0; tx < w; tx += 16) { int tw = Math.Min(16, w - tx); var subencoding = this.c.ReceiveByte(); pixels = this.AllocateFramebufferScratch(tw * th * bpp); if ((subencoding & 1) != 0) { // raw this.c.Receive(pixels, 0, tw * th * bpp); if (inRange) { lock (this.Framebuffer.SyncRoot) { this.CopyToFramebuffer(x + tx, y + ty, tw, th, pixels); } } } else { pixels = this.AllocateFramebufferScratch(tw * th * bpp); if ((subencoding & 2) != 0) { background = this.c.Receive(bpp); } if ((subencoding & 4) != 0) { foreground = this.c.Receive(bpp); } int ptr = 0; for (int pp = 0; pp < tw * th; pp++) { for (int pe = 0; pe < bpp; pe++) { pixels[ptr++] = background[pe]; } } int nsubrects = (subencoding & 8) != 0 ? this.c.ReceiveByte() : 0; if (nsubrects > 0) { var subrectsColored = (subencoding & 16) != 0; for (int subrect = 0; subrect < nsubrects; subrect++) { var color = subrectsColored ? this.c.Receive(bpp) : foreground; var srxy = this.c.ReceiveByte(); var srwh = this.c.ReceiveByte(); int srx = (srxy >> 4) & 0xf, srw = ((srwh >> 4) & 0xf) + 1; int sry = (srxy >> 0) & 0xf, srh = ((srwh >> 0) & 0xf) + 1; if (srx + srw > tw || sry + srh > th) { continue; } for (int py = 0; py < srh; py++) { for (int px = 0; px < srw; px++) { int off = bpp * (((py + sry) * tw) + (px + srx)); for (int pe = 0; pe < bpp; pe++) { pixels[off + pe] = color[pe]; } } } } } lock (this.Framebuffer.SyncRoot) { this.CopyToFramebuffer(x + tx, y + ty, tw, th, pixels); } } } } break; case VncEncoding.CopyRect: var srcx = (int)this.c.ReceiveUInt16BE(); var srcy = (int)this.c.ReceiveUInt16BE(); if (srcx + w > fbW) { w = fbW - srcx; } if (srcy + h > fbH) { h = fbH - srcy; } if (w < 1 || h < 1) { continue; } pixels = this.AllocateFramebufferScratch(w * h * bpp); lock (this.Framebuffer.SyncRoot) { this.CopyToGeneral(0, 0, w, h, pixels, srcx, srcy, fbW, fbH, this.Framebuffer.GetBuffer(), w, h); this.CopyToFramebuffer(x, y, w, h, pixels); } break; case VncEncoding.Raw: pixels = this.AllocateFramebufferScratch(w * h * bpp); this.c.Receive(pixels, 0, w * h * bpp); if (inRange) { lock (this.Framebuffer.SyncRoot) { this.CopyToFramebuffer(x, y, w, h, pixels); } } break; case VncEncoding.Zlib: int bytesDesired = w * h * bpp; int size = (int)this.c.ReceiveUInt32BE(); VncStream.SanityCheck(size >= 0 && size < 0x10000000); VncUtility.AllocateScratch(size, ref this.zlibScratch); this.c.Receive(this.zlibScratch, 0, size); this.zlibMemoryStream.Position = 0; this.zlibMemoryStream.Write(this.zlibScratch, 0, size); this.zlibMemoryStream.SetLength(size); this.zlibMemoryStream.Position = 0; if (this.zlibInflater == null) { // Zlib has a two-byte header. VncStream.SanityCheck(size >= 2); this.zlibMemoryStream.Position = 2; this.zlibInflater = new DeflateStream(this.zlibMemoryStream, CompressionMode.Decompress, false); } pixels = this.AllocateFramebufferScratch(bytesDesired); for (int j = 0; j < bytesDesired;) { int count = 0; try { count = this.zlibInflater.Read(pixels, j, bytesDesired - j); } catch (InvalidDataException) { VncStream.Require( false, "Bad data compressed.", VncFailureReason.UnrecognizedProtocolElement); } VncStream.Require( count > 0, "No data compressed.", VncFailureReason.UnrecognizedProtocolElement); j += count; } if (inRange) { lock (this.Framebuffer.SyncRoot) { this.CopyToFramebuffer(x, y, w, h, pixels); } } break; case VncEncoding.PseudoDesktopSize: this.Framebuffer = new VncFramebuffer(this.Framebuffer.Name, w, h, this.Framebuffer.PixelFormat); continue; // Don't call OnFramebufferChanged for this one. default: VncStream.Require( false, "Unsupported encoding.", VncFailureReason.UnrecognizedProtocolElement); break; } rects.Add(new VncRectangle(x, y, w, h)); } if (rects.Count > 0) { this.OnFramebufferChanged(new FramebufferChangedEventArgs(rects)); } return(encoding != VncEncoding.DesktopSize); }
private byte[] AllocateFramebufferScratch(int bytes) { return(VncUtility.AllocateScratch(bytes, ref this.framebufferScratch)); }