zoukankan      html  css  js  c++  java
  • arm-linux 裸机下 VNC 的实现

    这里的 arm-linux 裸机指的是,只有基本 C 库和安装了 busybox 的嵌入式系统,没有 X11 或者 wayland 作为底层支援。

    这里的实现是基于 framebuffer 的,是将用于 LCD 显示的 /dev/fb* 设备中数据进行了拷贝(其实是 mmap 进行了数据共享,是直接取了 framebuffer 数据缓存),并通过 VNC 协议外发。本文只记录了一个没有键盘鼠标的,基于 framebuffer 的 VNC 桌面实验。本实验中,使用友善之臂设计的 s5pv21 开发板。

    今天目的只是实验是否可行,并没有详细 debug。这个明显是还有很大改进空间的。PS: 原来 VNC 的协议本身就是叫 rfb, remote framebuffer。

    下面是在 xtightvncviewer 中连接效果:

    实现路径:

    1. 为 arm 板编译安装了 libvncserver。编译的时候,进行了下面一些特殊操作:

    a. 有部分可以在 configure 时候关闭(比如 --without-gnutls)

    b. 对 SDL 和 GTK 的需求,我直接编辑了 configure 脚本,找到 HAVE_LIBSDL_FALSE= ,HAVE_LIBGTK_FALSE= 位置,去掉了外面的判断。即默认就是这两个状态。因为我不需要 GTK 和 SDL。

    c. 解压出来的工具无法正确判断系统类型,configure 时候指定了系统: build_os=gnu-linux ./configure --host=arm-linux --prefix=/tmp/install

    编译下面的文件,将二进制拷贝到开发板。

    该文件是从github获得的,作者实现的时候把键盘鼠标都删掉了,不过这个明显是可以添加的。网址:

    /*
     * $Id$
     *
     * This program is free software; you can redistribute it and/or modify it
     * under the terms of the GNU General Public License as published by the
     * Free Software Foundation; either version 2, or (at your option) any
     * later version.
     *
     * This program is distributed in the hope that it will be useful, but
     * WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * General Public License for more details.
     *
     * This project is an adaptation of the original fbvncserver for the iPAQ
     * and Zaurus.
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/ioctl.h>
    
    #include <sys/stat.h>
    #include <sys/sysmacros.h>             /* For makedev() */
    
    #include <fcntl.h>
    #include <linux/fb.h>
    #include <linux/input.h>
    
    #include <assert.h>
    #include <errno.h>
    
    /* libvncserver */
    #include "rfb/rfb.h"
    //#include "rfb/keysym.h"
    
    /*****************************************************************************/
    //#define LOG_FPS
    
    #define BITS_PER_SAMPLE     5
    #define SAMPLES_PER_PIXEL   2
    
    static char fb_device[256] = "/dev/fb0";
    static struct fb_var_screeninfo scrinfo;
    static int fbfd = -1;
    static unsigned short int *fbmmap = MAP_FAILED;
    static unsigned short int *vncbuf;
    static unsigned short int *fbbuf;
    
    static int vnc_port = 5900;
    static rfbScreenInfoPtr server;
    static size_t bytespp;
    
    
    /* No idea, just copied from fbvncserver as part of the frame differerencing
     * algorithm.  I will probably be later rewriting all of this. */
    static struct varblock_t
    {
        int min_i;
        int min_j;
        int max_i;
        int max_j;
        int r_offset;
        int g_offset;
        int b_offset;
        int rfb_xres;
        int rfb_maxy;
    } varblock;
    
    /*****************************************************************************/
    
    
    static void init_fb(void)
    {
        size_t pixels;
    
        if ((fbfd = open(fb_device, O_RDONLY)) == -1)
        {
            fprintf(stderr, "cannot open fb device %s
    ", fb_device);
            exit(EXIT_FAILURE);
        }
    
        if (ioctl(fbfd, FBIOGET_VSCREENINFO, &scrinfo) != 0)
        {
            fprintf(stderr, "ioctl error
    ");
            exit(EXIT_FAILURE);
        }
    
        pixels = scrinfo.xres * scrinfo.yres;
        bytespp = scrinfo.bits_per_pixel / 8;
    
        fprintf(stderr, "xres=%d, yres=%d, xresv=%d, yresv=%d, xoffs=%d, yoffs=%d, bpp=%d
    ",
                (int)scrinfo.xres, (int)scrinfo.yres,
                (int)scrinfo.xres_virtual, (int)scrinfo.yres_virtual,
                (int)scrinfo.xoffset, (int)scrinfo.yoffset,
                (int)scrinfo.bits_per_pixel);
        fprintf(stderr, "offset:length red=%d:%d green=%d:%d blue=%d:%d 
    ",
                (int)scrinfo.red.offset, (int)scrinfo.red.length,
                (int)scrinfo.green.offset, (int)scrinfo.green.length,
                (int)scrinfo.blue.offset, (int)scrinfo.blue.length
                );
    
        fbmmap = mmap(NULL, pixels * bytespp, PROT_READ, MAP_SHARED, fbfd, 0);
    
        if (fbmmap == MAP_FAILED)
        {
            fprintf(stderr, "mmap failed
    ");
            exit(EXIT_FAILURE);
        }
    }
    
    static void cleanup_fb(void)
    {
        if(fbfd != -1)
        {
            close(fbfd);
        }
    }
    
    /*****************************************************************************/
    
    static void init_fb_server(int argc, char **argv)
    {
        fprintf(stderr, "Initializing server...
    ");
    
        /* Allocate the VNC server buffer to be managed (not manipulated) by
         * libvncserver. */
        vncbuf = calloc(scrinfo.xres * scrinfo.yres, bytespp);
        assert(vncbuf != NULL);
    
        /* Allocate the comparison buffer for detecting drawing updates from frame
         * to frame. */
        fbbuf = calloc(scrinfo.xres * scrinfo.yres, bytespp);
        assert(fbbuf != NULL);
    
        /* TODO: This assumes scrinfo.bits_per_pixel is 16. */
        server = rfbGetScreen(&argc, argv, scrinfo.xres, scrinfo.yres, BITS_PER_SAMPLE, SAMPLES_PER_PIXEL, bytespp);
        assert(server != NULL);
    
        server->desktopName = "framebuffer";
        server->frameBuffer = (char *)vncbuf;
        server->alwaysShared = TRUE;
        server->httpDir = NULL;
        server->port = vnc_port;
    
        //    server->kbdAddEvent = keyevent;
        //    server->ptrAddEvent = ptrevent;
    
        rfbInitServer(server);
    
        /* Mark as dirty since we haven't sent any updates at all yet. */
        rfbMarkRectAsModified(server, 0, 0, scrinfo.xres, scrinfo.yres);
    
        /* No idea. */
        varblock.r_offset = scrinfo.red.offset + scrinfo.red.length - BITS_PER_SAMPLE;
        varblock.g_offset = scrinfo.green.offset + scrinfo.green.length - BITS_PER_SAMPLE;
        varblock.b_offset = scrinfo.blue.offset + scrinfo.blue.length - BITS_PER_SAMPLE;
        varblock.rfb_xres = scrinfo.yres;
        varblock.rfb_maxy = scrinfo.xres - 1;
    }
    
    // sec
    #define LOG_TIME    5
    
    int timeToLogFPS() {
        static struct timeval now={0,0}, then={0,0};
        double elapsed, dnow, dthen;
        gettimeofday(&now,NULL);
        dnow  = now.tv_sec  + (now.tv_usec /1000000.0);
        dthen = then.tv_sec + (then.tv_usec/1000000.0);
        elapsed = dnow - dthen;
        if (elapsed > LOG_TIME)
          memcpy((char *)&then, (char *)&now, sizeof(struct timeval));
        return elapsed > LOG_TIME;
    }
    
    /*****************************************************************************/
    //#define COLOR_MASK  0x1f001f
    #define COLOR_MASK  (((1 << BITS_PER_SAMPLE) << 1) - 1)
    #define PIXEL_FB_TO_RFB(p,r_offset,g_offset,b_offset) ((p>>r_offset)&COLOR_MASK) | (((p>>g_offset)&COLOR_MASK)<<BITS_PER_SAMPLE) | (((p>>b_offset)&COLOR_MASK)<<(2*BITS_PER_SAMPLE))
    
    static void update_screen(void)
    {
    #ifdef LOG_FPS
        static int frames = 0;
        frames++;
        if(timeToLogFPS())
        {
            double fps = frames / LOG_TIME;
            fprintf(stderr, "  fps: %f
    ", fps);
            frames = 0;
        }
    #endif
    
        varblock.min_i = varblock.min_j = 9999;
        varblock.max_i = varblock.max_j = -1;
    
        uint32_t *f = (uint32_t *)fbmmap;        /* -> framebuffer         */
        uint32_t *c = (uint32_t *)fbbuf;         /* -> compare framebuffer */
        uint32_t *r = (uint32_t *)vncbuf;        /* -> remote framebuffer  */
    
        int xstep = 4/bytespp;
    
        int y;
        for (y = 0; y < (int)scrinfo.yres; y++)
        {
            /* Compare every 1/2/4 pixels at a time */
            int x;
            for (x = 0; x < (int)scrinfo.xres; x += xstep)
            {
                uint32_t pixel = *f;
    
                if (pixel != *c)
                {
                    *c = pixel;
    
    #if 0
                    /* XXX: Undo the checkered pattern to test the efficiency
                     * gain using hextile encoding. */
                    if (pixel == 0x18e320e4 || pixel == 0x20e418e3)
                        pixel = 0x18e318e3;
    #endif
                    *r = PIXEL_FB_TO_RFB(pixel,
                                         varblock.r_offset, varblock.g_offset, varblock.b_offset);
    
                    if (x < varblock.min_i)
                        varblock.min_i = x;
                    else
                    {
                        if (x > varblock.max_i)
                            varblock.max_i = x;
    
                        if (y > varblock.max_j)
                            varblock.max_j = y;
                        else if (y < varblock.min_j)
                            varblock.min_j = y;
                    }
                }
    
                f++;
                c++;
                r++;
            }
        }
    
        if (varblock.min_i < 9999)
        {
            if (varblock.max_i < 0)
                varblock.max_i = varblock.min_i;
    
            if (varblock.max_j < 0)
                varblock.max_j = varblock.min_j;
    
            fprintf(stderr, "Dirty page: %dx%d+%d+%d...
    ",
                    (varblock.max_i+2) - varblock.min_i, (varblock.max_j+1) - varblock.min_j,
                    varblock.min_i, varblock.min_j);
    
            rfbMarkRectAsModified(server, varblock.min_i, varblock.min_j,
                                  varblock.max_i + 2, varblock.max_j + 1);
    
            rfbProcessEvents(server, 10000);
        }
    }
    
    /*****************************************************************************/
    
    void print_usage(char **argv)
    {
        fprintf(stderr, "%s [-f device] [-p port] [-h]
    "
                        "-p port: VNC port, default is 5900
    "
                        "-f device: framebuffer device node, default is /dev/fb0
    "
                        "-h : print this help
    "
                , *argv);
    }
    
    int main(int argc, char **argv)
    {
        if(argc > 1)
        {
            int i=1;
            while(i < argc)
            {
                if(*argv[i] == '-')
                {
                    switch(*(argv[i] + 1))
                    {
                    case 'h':
                        print_usage(argv);
                        exit(0);
                        break;
                    case 'f':
                        i++;
                        strcpy(fb_device, argv[i]);
                        break;
                    case 'p':
                        i++;
                        vnc_port = atoi(argv[i]);
                        break;
                    }
                }
                i++;
            }
        }
    
        fprintf(stderr, "Initializing framebuffer device %s...
    ", fb_device);
        init_fb();
    
        fprintf(stderr, "Initializing VNC server:
    ");
        fprintf(stderr, "      %d
    ", (int)scrinfo.xres);
        fprintf(stderr, "    height: %d
    ", (int)scrinfo.yres);
        fprintf(stderr, "    bpp:    %d
    ", (int)scrinfo.bits_per_pixel);
        fprintf(stderr, "    port:   %d
    ", (int)vnc_port);
        init_fb_server(argc, argv);
    
        /* Implement our own event loop to detect changes in the framebuffer. */
        while (1)
        {
            while (server->clientHead == NULL)
                rfbProcessEvents(server, 100000);
    
            rfbProcessEvents(server, 100000);
            update_screen();
        }
    
        fprintf(stderr, "Cleaning up...
    ");
        cleanup_fb();
    }
  • 相关阅读:
    java的构造方法 java程序员
    No result defined for action cxd.action.QueryAction and result success java程序员
    大学毕业后拉开差距的真正原因 java程序员
    hibernate的回滚 java程序员
    验证码 getOutputStream() has already been called for this response异常的原因和解决方法 java程序员
    浅谈ssh(struts,spring,hibernate三大框架)整合的意义及其精髓 java程序员
    你平静的生活或许会在某个不可预见的时刻被彻底打碎 java程序员
    Spring配置文件中使用ref local与ref bean的区别. 在ApplicationResources.properties文件中,使用<ref bean>与<ref local>方法如下 java程序员
    poj1416Shredding Company
    poj1905Expanding Rods
  • 原文地址:https://www.cnblogs.com/pied/p/7766871.html
Copyright © 2011-2022 走看看