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();
    }
  • 相关阅读:
    洛谷 P4708
    NFLSOJ 1060
    Codeforces 1264D
    Solution -「多校联训」Sample
    Solution -「多校联训」光影交错
    Solution -「LOJ #138」「模板」类欧几里得算法
    Solution -「LOJ #141」回文子串 ||「模板」双向 PAM
    Codeforces Round #700 (Div. 2)
    Codeforces Round #698 (Div. 2) C and D
    「NOI 2021」题解
  • 原文地址:https://www.cnblogs.com/pied/p/7766871.html
Copyright © 2011-2022 走看看