Serves a VNC client with framebuffer information and receives keyboard and mouse interactions.
Exemplo n.º 1
0
        static void Main(string[] args)
        {
            Console.WriteLine("Listening on local port 5900.");
            Console.WriteLine("Try to connect! The password is: {0}", Password);

            // Wait for a connection.
            var listener = new TcpListener(IPAddress.Any, 5900);
            listener.Start();
            var client = listener.AcceptTcpClient();

            // Set up a framebuffer and options.
            var options = new VncServerSessionOptions();
            options.AuthenticationMethod = AuthenticationMethod.Password;

            // Create a session.
            Session = new VncServerSession();
            Session.Connected += HandleConnected;
            Session.ConnectionFailed += HandleConnectionFailed;
            Session.Closed += HandleClosed;
            Session.PasswordProvided += HandlePasswordProvided;
            Session.SetFramebufferSource(new VncScreenFramebufferSource("Hello World", Screen.PrimaryScreen));
            Session.Connect(client.GetStream(), options);

            // Let's go.
            Application.Run();
        }
Exemplo n.º 2
0
        /// <summary>
        /// The main loop of the VNC server. Listens for new connections, and spawns new <see cref="VncServerSession"/>s.
        /// </summary>
        /// <returns>
        /// A <see cref="Task"/> which represents the asynchronous operation.
        /// </returns>
        protected async Task Run()
        {
            while (true)
            {
                var client = await this.listener.AcceptTcpClientAsync();

                // Set up a framebuffer and options.
                var options = new VncServerSessionOptions();
                options.AuthenticationMethod = AuthenticationMethod.Password;

                // Create a session.
                var session = new VncServerSession(
                    new VncPasswordChallenge(),
                    this.logger);

                session.Connected        += this.OnConnected;
                session.Closed           += this.OnClosed;
                session.PasswordProvided += this.OnPasswordProvided;
                session.SetFramebufferSource(this.framebufferSource);
                session.Connect(client.GetStream(), options);

                if (this.keyboard != null)
                {
                    session.KeyChanged += this.keyboard.HandleKeyEvent;
                }

                if (this.controller != null)
                {
                    session.PointerChanged += this.controller.HandleTouchEvent;
                }

                this.sessions.Add(session);
            }
        }
        public bool RespondToUpdateRequest(VncServerSession session)
        {
            var fb = Framebuffer; var fbr = session.FramebufferUpdateRequest;

            if (fb == null || fbr == null)
            {
                return(false);
            }

            var incremental = fbr.Incremental; var region = fbr.Region;

            session.FramebufferManualBeginUpdate();

            var buffer = fb.GetBuffer();
            int bpp    = fb.PixelFormat.BytesPerPixel;

            lock (fb.SyncRoot)
            {
                int ymax = Math.Min(region.Y + region.Height, fb.Height);
                int xmax = Math.Min(region.X + region.Width, fb.Width);

                for (int y = region.Y; y < ymax; y += TileSize)
                {
                    for (int x = region.X; x < xmax; x += TileSize)
                    {
                        int w = Math.Min(TileSize, xmax - x);
                        int h = Math.Min(TileSize, ymax - y);

                        var subregion = new VncRectangle(x, y, w, h);

                        VncPixelFormat.Copy(buffer, fb.Stride, fb.PixelFormat, subregion,
                                            _pixelBuffer, w * bpp, Framebuffer.PixelFormat);

                        int ix = x / TileSize, iy = y / TileSize;
                        var tileHash = _hash.ComputeHash(_pixelBuffer, 0, w * h * bpp);

                        if (_hashes[iy, ix] == null || !_hashes[iy, ix].SequenceEqual(tileHash))
                        {
                            _hashes[iy, ix] = tileHash;
                            if (incremental)
                            {
                                session.FramebufferManualInvalidate(subregion);
                            }
                        }
                    }
                }
            }

            if (!incremental)
            {
                session.FramebufferManualInvalidate(region);
            }

            return(session.FramebufferManualEndUpdate());
        }
        /// <summary>
        /// Responds to a <see cref="VncServerSession"/> update request.
        /// </summary>
        /// <param name="session">
        /// The session on which the update request was received.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if the operation completed successfully; otherwise,
        /// <see langword="false"/>.
        /// </returns>
        public unsafe bool RespondToUpdateRequest(VncServerSession session)
        {
            VncRectangle subregion = default(VncRectangle);

            var fb = this.Framebuffer;
            var fbr = session.FramebufferUpdateRequest;
            if (fb == null || fbr == null)
            {
                return false;
            }

            var incremental = fbr.Incremental;
            var region = fbr.Region;
            int bpp = fb.PixelFormat.BytesPerPixel;

            session.FramebufferManualBeginUpdate();

            // Take a lock here, as we will modify
            // both buffers heavily in the next block.
            lock (fb.SyncRoot)
            {
                lock (this.cachedFramebuffer.SyncRoot)
                {
                    // Get the buffers (byte arrays) of data. We'll compare the data one pixel at a time.
                    var actualBuffer = this.Framebuffer.GetBuffer();
                    var bufferedBuffer = this.cachedFramebuffer.GetBuffer();

                    for (int y = region.Y; y < region.Y + region.Height; y++)
                    {
                        subregion.X = region.X;
                        subregion.Y = y;
                        subregion.Width = region.Width;
                        subregion.Height = 1;

                        bool isValid = true;

                        // For a given y, the x pixels are stored sequentially in the array
                        // starting at y * stride (number of bytes per row); for each x
                        // value there are bpp bytes of data (4 for a 32-bit integer); we are looking
                        // for pixels between x and x + w so this translates to
                        // y * stride + bpp * x and y * stride + bpp * (x + w)
                        int srcOffset = (y * this.Framebuffer.Stride) + (bpp * region.X);
                        int length = bpp * region.Width;

                        fixed (byte* actualLinePtr = actualBuffer, bufferedLinePtr = bufferedBuffer)
                        {
                            isValid = NativeMethods.memcmp(actualLinePtr + srcOffset, bufferedLinePtr + srcOffset, (uint)length) == 0;
                        }

                        if (!isValid)
                        {
                            Buffer.BlockCopy(actualBuffer, srcOffset, bufferedBuffer, srcOffset, length);
                        }

                        this.isLineInvalid[y - region.Y] = !isValid;
                    }
                } // lock
            } // lock

            if (incremental)
            {
                int? y = null;

                for (int line = 0; line < region.Height; line++)
                {
                    if (y == null && this.isLineInvalid[line])
                    {
                        y = region.Y + line;
                    }

                    if (y != null && (!this.isLineInvalid[line] || line == region.Height))
                    {
                        // Flush
                        subregion.X = region.X;
                        subregion.Y = region.Y + y.Value;
                        subregion.Width = region.Width;
                        subregion.Height = line - y.Value;
                        session.FramebufferManualInvalidate(subregion);
                        y = null;
                    }
                }
            }
            else
            {
                session.FramebufferManualInvalidate(region);
            }

            return session.FramebufferManualEndUpdate();
        }
Exemplo n.º 5
0
        /// <summary>
        /// Responds to a <see cref="VncServerSession"/> update request.
        /// </summary>
        /// <param name="session">
        /// The session on which the update request was received.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if the operation completed successfully; otherwise,
        /// <see langword="false"/>.
        /// </returns>
        public unsafe bool RespondToUpdateRequest(VncServerSession session)
        {
            VncRectangle subregion = default(VncRectangle);

            var fb  = this.Framebuffer;
            var fbr = session.FramebufferUpdateRequest;

            if (fb == null || fbr == null)
            {
                return(false);
            }

            var incremental = fbr.Incremental;
            var region      = fbr.Region;
            int bpp         = fb.PixelFormat.BytesPerPixel;

            session.FramebufferManualBeginUpdate();

            // Take a lock here, as we will modify
            // both buffers heavily in the next block.
            lock (fb.SyncRoot)
            {
                lock (this.cachedFramebuffer.SyncRoot)
                {
                    // Get the buffers (byte arrays) of data. We'll compare the data one pixel at a time.
                    var actualBuffer   = this.Framebuffer.GetBuffer();
                    var bufferedBuffer = this.cachedFramebuffer.GetBuffer();

                    for (int y = region.Y; y < region.Y + region.Height; y++)
                    {
                        subregion.X      = region.X;
                        subregion.Y      = y;
                        subregion.Width  = region.Width;
                        subregion.Height = 1;

                        bool isValid = true;

                        // For a given y, the x pixels are stored sequentially in the array
                        // starting at y * stride (number of bytes per row); for each x
                        // value there are bpp bytes of data (4 for a 32-bit integer); we are looking
                        // for pixels between x and x + w so this translates to
                        // y * stride + bpp * x and y * stride + bpp * (x + w)
                        int srcOffset = (y * this.Framebuffer.Stride) + (bpp * region.X);
                        int length    = bpp * region.Width;

                        fixed(byte *actualLinePtr = actualBuffer, bufferedLinePtr = bufferedBuffer)
                        {
                            isValid = NativeMethods.memcmp(actualLinePtr + srcOffset, bufferedLinePtr + srcOffset, (uint)length) == 0;
                        }

                        if (!isValid)
                        {
                            Buffer.BlockCopy(actualBuffer, srcOffset, bufferedBuffer, srcOffset, length);
                        }

                        this.isLineInvalid[y - region.Y] = !isValid;
                    }
                } // lock
            }     // lock

            if (incremental)
            {
                int?y = null;

                for (int line = 0; line < region.Height; line++)
                {
                    if (y == null && this.isLineInvalid[line])
                    {
                        y = region.Y + line;
                    }

                    if (y != null && (!this.isLineInvalid[line] || line == region.Height))
                    {
                        // Flush
                        subregion.X      = region.X;
                        subregion.Y      = region.Y + y.Value;
                        subregion.Width  = region.Width;
                        subregion.Height = line - y.Value;
                        session.FramebufferManualInvalidate(subregion);
                        y = null;
                    }
                }
            }
            else
            {
                session.FramebufferManualInvalidate(region);
            }

            return(session.FramebufferManualEndUpdate());
        }
Exemplo n.º 6
0
        /// <summary>
        /// Responds to a <see cref="VncServerSession"/> update request.
        /// </summary>
        /// <param name="session">
        /// The session on which the update request was received.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if the operation completed successfully; otherwise,
        /// <see langword="false"/>.
        /// </returns>
        public unsafe bool RespondToUpdateRequest(VncServerSession session)
        {
            VncRectangle subregion = default(VncRectangle);

            var fb  = this.Framebuffer;
            var fbr = session.FramebufferUpdateRequest;

            if (fb == null || fbr == null)
            {
                return(false);
            }

            var incremental = fbr.Incremental;
            var region      = fbr.Region;
            int bpp         = fb.PixelFormat.BytesPerPixel;

            Logger.Debug($"Responding to an update request for region {region}.");

            session.FramebufferManualBeginUpdate();

            // Take a lock here, as we will modify
            // both buffers heavily in the next block.
            lock (fb.SyncRoot)
            {
                lock (this.cachedFramebuffer.SyncRoot)
                {
                    var actualBuffer   = this.Framebuffer.GetBuffer();
                    var bufferedBuffer = this.cachedFramebuffer.GetBuffer();

                    // In this block, we will determine which rectangles need updating. Right now, we consider
                    // each line at once. It's not a very efficient algorithm, but it works.
                    // We're going to start at the upper-left position of the region, and then we will work our way down,
                    // on a line by line basis, to determine if each line is still valid.
                    // isLineInvalid will indicate, on a line-by-line basis, whether a line is still valid or not.
                    for (int y = region.Y; y < region.Y + region.Height; y++)
                    {
                        subregion.X      = region.X;
                        subregion.Y      = y;
                        subregion.Width  = region.Width;
                        subregion.Height = 1;

                        bool isValid = true;

                        // For a given y, the x pixels are stored sequentially in the array
                        // starting at y * stride (number of bytes per row); for each x
                        // value there are bpp bytes of data (4 for a 32-bit integer); we are looking
                        // for pixels between x and x + w so this translates to
                        // y * stride + bpp * x and y * stride + bpp * (x + w)
                        int srcOffset = (y * this.Framebuffer.Stride) + (bpp * region.X);
                        int length    = bpp * region.Width;

                        fixed(byte *actualLinePtr = actualBuffer, bufferedLinePtr = bufferedBuffer)
                        {
                            isValid = NativeMethods.memcmp(actualLinePtr + srcOffset, bufferedLinePtr + srcOffset, (uint)length) == 0;
                        }

                        if (!isValid)
                        {
                            try
                            {
                                Buffer.BlockCopy(actualBuffer, srcOffset, bufferedBuffer, srcOffset, length);
                            }
                            catch
                            {
                                throw;
                            }
                        }

                        this.isLineInvalid[y - region.Y] = !isValid;
                    }
                } // lock
            }     // lock

            if (incremental)
            {
                // Determine logical group of lines which are invalid. We find the first line which is invalid,
                // create a new region which contains the all invalid lines which immediately follow the current line.
                // If we find a valid line, we'll create a new region.
                int?y = null;

                for (int line = 0; line < region.Height; line++)
                {
                    if (y == null && this.isLineInvalid[line])
                    {
                        y = region.Y + line;
                    }

                    if (y != null && (!this.isLineInvalid[line] || line == region.Height - 1))
                    {
                        // Flush
                        subregion.X      = region.X;
                        subregion.Y      = region.Y + y.Value;
                        subregion.Width  = region.Width;
                        subregion.Height = line - y.Value + 1;
                        session.FramebufferManualInvalidate(subregion);
                        y = null;
                    }
                }
            }
            else
            {
                session.FramebufferManualInvalidate(region);
            }

            return(session.FramebufferManualEndUpdate());
        }