MiniGUI显示引擎初始化流程

前言

这次主要分析在MiniGUI中,显示引擎的初始化流程,让大家理解MiniGUI.cfg配置文件怎么影响到使用哪一个引擎的,如果需要开发一个新引擎的话,本文章也有帮助

因为使用MiniGUI的人数也少,写这些文章没有什么价值,以后可能就不再写minigui的文章了,之后会多写一点QT的文章

1、MiniGUI支持的显示引擎

引擎名称 引擎含义 默认编译
videodummy 是否包括虚拟NEWGAL引擎,所有操作系统 yes
videofbcon 是否包括FrameBuffer控制台NEWGAL引擎,Linux/uClinux yes
videoqvfb 是否包括Qt Virtual FrameBuffer NEWGAL引擎,Linux yes
rtosxvfb 是否包括RTOS Virtual FrameBuffer NEWGAL引擎 注意:如果要启用rtosxvfb,必须禁用pcxvfb no
pcxvfb 是否包括PC Virtual FrameBuffer NEWGAL引擎,如qvfb,mvfb,gvfb或wvfb yes
videowvfb 是否包括Windows Virtual FrameBuffer NEWGAL引擎,Win32 no
videocommlcd 是否包括普通LCD的NEWGAL引擎,所有操作系统 no
videomlshadow 是否包括MLShadow NEWGAL引擎,所有操作系统,MiniGui-Threads 、MiniGui-Standalone no
videoshadow 是否包括Shadow NEWGAL引擎 no
videoem86gfx 是否包括EM86xx GFG的NEWGAL引擎,EM86开发板的GFX图形引擎 no
videoem85xxyuv 是否包括EM85xx YUV的NEWGAL引擎,EM85xx开发板的YUV图形引擎,uClinux no
videoem85xxosd 是否包括EM85xx OSD的NEWGAL引擎,EM85xx开发板的OSD图形引擎,uClinux no
videosvpxxosd 是否包括SVPXXOSD NEWGAL引擎,uClinux no
videobf533 是否包括通过SPI的BF533 OSD的NEWGAL引擎,uClinux no
videomb93493 是否包括mb93493的NEWGAL引擎YUV FrameBuffer驱动程序,uClinux no
videoutpmc 是否包括UTPMC的NEWGAL引擎,uClinux no
videodfb 是否包括DirectFB的NEWGAL引擎,将MiniGui运 行在DirectFB之上,Linux no
videost7167 是否包括ST7167的NEWGAL引擎 no
videostgfb 是否包括STGFB的NEWGAL引擎 no
videohi35xx 是否包括Hi35xx视频NEWGAL引擎,hi35xx开发板的图形引擎,Linux no
videohi3560a 是否包括Hi3560A视频NEWGAL引擎 no
videogdl 是否包括GDL Video NEWGAL引擎 no
videosigma8654 是否包括sigma8654 NEWGAL引擎 no
videomstar 是否包括mstar NEWGAL引擎 no
videocustom 是否包括自定义NEWGAL引擎 no
videonexus 是否包括nexus NEWGAL引擎 no
videos3c6410 是否包括s3c6410 NEWGAL引擎 no

2、引擎初始化时序

本文以videofbcon引擎为例,先看一下时序图
MiniGUI显示引擎初始化流程_第1张图片
在文件libminigui-gpl-3.2/src/kernel/init.c中,有入口函数InitGUI,主要关注mg_InitGAL和mg_InitScreenDC函数。mg_InitGAL函数里面会解析MiniGUI.cfg文件里面的配置,然后选择一种显示引擎初始化,之后需要显示的界面都是绘制到__gal_screen这个GAL_Surface上面。mg_InitScreenDC是初始化DC槽,一共会初始化16个,由DCSLOTNUMBER宏定义,也就是说,在应用中最多只能申请到16个DC句柄,所以开发应用的时候要注意

/* libminigui-gpl-3.2/src/kernel/init.c */
int GUIAPI InitGUI (int args, const char *agr[])
{
	/*......*/
    step++;
    /* 显示引擎初始化 */
    switch (mg_InitGAL ()) {
    case ERR_CONFIG_FILE:
        fprintf (stderr, 
            "KERNEL>InitGUI: Reading configuration failure!\n");
        return step;

    case ERR_NO_ENGINE:
        fprintf (stderr, 
            "KERNEL>InitGUI: No graphics engine defined!\n");
        return step;

    case ERR_NO_MATCH:
        fprintf (stderr, 
            "KERNEL>InitGUI: Can not get graphics engine information!\n");
        return step;

    case ERR_GFX_ENGINE:
        fprintf (stderr, 
            "KERNEL>InitGUI: Can not initialize graphics engine!\n");
        return step;
    }

    /* 初始化主屏幕DC */
    step++;
    if (!mg_InitScreenDC (__gal_screen)) {
        fprintf (stderr, "KERNEL>InitGUI: Can not initialize screen DC!\n");
        goto failure1;
    }
	/*......*/
}

在文件libminigui-gpl-3.2/src/newgal/newgal.c,看看mg_InitGAL函数里面具体是怎么实现的,其实MiniGUI不仅可以从配置文件读取配置,也可以使用环境变量,比如下面的MG_GAL_ENGINE,如果设置了该环境变量,那么则忽略gal_engine的配置。主要关注GAL_VideoInit和GAL_SetVideoMode函数

/* libminigui-gpl-3.2/src/newgal/newgal.c */
int mg_InitGAL (void)
{
	/*......*/
#ifndef __NOUNIX__
    /* 获取环境变量MG_GAL_ENGINE,如果有值则不再获取gal_engine的值 */
    if ((env_value = getenv ("MG_GAL_ENGINE"))) {
        strncpy (engine, env_value, LEN_ENGINE_NAME);
        engine [LEN_ENGINE_NAME] = '\0';
    }
    else
#endif
#ifndef _MG_MINIMALGDI
    /* 从MiniGUI.cfg获取gal_engine的值 */
    if (GetMgEtcValue ("system", "gal_engine", engine, LEN_ENGINE_NAME) < 0) {
        return ERR_CONFIG_FILE;
    }
#else /* _MG_MINIMALGDI */
#   ifdef _MGGAL_PCXVFB
    strcpy(engine, "pc_xvfb");
#   else
    strcpy(engine, "dummy");
#   endif
#endif /* _MG_MINIMALGDI */
    /* 显示引擎初始化 */
    if (GAL_VideoInit (engine, 0)) {
        GAL_VideoQuit ();
        fprintf (stderr, "NEWGAL: Does not find matched engine: %s.\n", engine);
        return ERR_NO_MATCH;
    }

#if defined (WIN32) || !defined(__NOUNIX__)
    if ((env_value = getenv ("MG_DEFAULTMODE"))) {
        strncpy (mode, env_value, LEN_MODE);
        mode [LEN_MODE] = '\0';
    }
    else
#endif
	/* 获取配置的分辨率和位深defaultmode */
    if (GetMgEtcValue (engine, "defaultmode", mode, LEN_MODE) < 0)
        if (GetMgEtcValue ("system", "defaultmode", mode, LEN_MODE) < 0)
            return ERR_CONFIG_FILE;
	/* 解析mode,赋值给w,h,depth */
    if (!GAL_ParseVideoMode (mode, &w, &h, &depth)) {
        GAL_VideoQuit ();
        fprintf (stderr, "NEWGAL: bad video mode parameter: %s.\n", mode);
        return ERR_CONFIG_FILE;
    }

	/* 设置引擎的分辨率和位深,一般要求和硬件一致 */
    if (!(__gal_screen = GAL_SetVideoMode (w, h, depth, GAL_HWPALETTE))) {
        GAL_VideoQuit ();
        fprintf (stderr, "NEWGAL: Set video mode failure.\n");
        return ERR_GFX_ENGINE;
    }
	/*......*/
    return 0;
}

在文件libminigui-gpl-3.2/src/newgal/video.c,GAL_VideoInit初始化显示引擎,确定本机像素格式,其中GAL_GetVideo函数获取需要初始化的引擎,比如fbcon,GAL_GetVideo函数里面是一个循环,匹配bootstrap结构体里面定义的引擎对象,再调用create创建具体的显示引擎,而video->VideoInit则是调用到需要使用的引擎对象的init函数中,fbcon的init函数是FB_VideoInit

/* libminigui-gpl-3.2/src/newgal/video.c */
int GAL_VideoInit (const char *driver_name, Uint32 flags)
{
    GAL_VideoDevice *video;
    GAL_PixelFormat vformat;
    Uint32 video_flags;
	/*......*/

	/* 该函数通过前面传入的driver_name,确定到底要初始化哪一个显示引擎,在bootstrap数组里面存着所有引擎 */
    video = GAL_GetVideo(driver_name);

    if ( video == NULL ) {
        return (-1);
    }
    video->screen = NULL;
    /* current_video是一个全局对象,方便调用对应引擎里面的函数 */
    current_video = video;

    /* 真正的初始化显示引擎 */
    memset(&vformat, 0, sizeof(vformat));
    if ( video->VideoInit(video, &vformat) < 0 ) {
        GAL_VideoQuit();
        return(-1);
    }
	/*......*/
    /* 创建适当格式大小为零的Surface,GAL_VideoSurface也就是current_video->screen,和__gal_screen是一个值 */
    video_flags = GAL_SWSURFACE;
    GAL_VideoSurface = GAL_CreateRGBSurface(video_flags, 0, 0,
            vformat.BitsPerPixel,
            vformat.Rmask, vformat.Gmask, vformat.Bmask, vformat.Amask);

    if ( GAL_VideoSurface == NULL ) {
        GAL_VideoQuit();
        return(-1);
    }

    GAL_VideoSurface->video = current_video;

    video->info.vfmt = GAL_VideoSurface->format;

    return(0);
}

bootstrap具体结构如下,在配置MiniGUi的时候使能不同的引擎,则会定义不同的宏。VideoBootStrap有两个函数指针,一个是available,一个是create,create可以指向具体某一个引擎的的create函数中,fbcon引擎是FB_CreateDevice函数,而FB_CreateDevice函数里也是一些函数指针的赋值

/* libminigui-gpl-3.2/src/newgal/video.c */
/* Available video drivers */
static VideoBootStrap *bootstrap[] = {
#ifdef _MGGAL_DUMMY
    &DUMMY_bootstrap,
#endif
#ifdef _MGGAL_SUNXIFBION
    &SUNXIFBION_bootstrap,
#endif
#ifdef _MGGAL_SUNXIFB
    &SUNXIFB_bootstrap,
#endif
#ifdef _MGGAL_SUNXI
	&SUNXI_bootstrap,
#endif 
#ifdef _MGGAL_FBCON
    &FBCON_bootstrap,
#endif
#ifdef _MGGAL_QVFB
    &QVFB_bootstrap,
#endif
/*......*/
    NULL
};

下面具体看看video->VideoInit函数,这里举例fbcon,也就是FB_VideoInit函数,主要是打开fb设备,映射内存

/* libminigui-gpl-3.2/src/newgal/fbcon/fbvideo.c */
static int FB_VideoInit(_THIS, GAL_PixelFormat *vformat)
{
    struct fb_fix_screeninfo finfo;
    struct fb_var_screeninfo vinfo;
    int i;
    const char *GAL_fbdev;

    /* 可以通过环境变量FRAMEBUFFER来设置打开哪个fb设备 */
    GAL_fbdev = getenv("FRAMEBUFFER");
    if ( GAL_fbdev == NULL ) {
        GAL_fbdev = "/dev/fb0";
    }
    console_fd = open(GAL_fbdev, O_RDWR, 0);
    if ( console_fd < 0 ) {
        GAL_SetError("NEWGAL>FBCON: Unable to open %s\n", GAL_fbdev);
        return(-1);
    }

    /* 获取一些硬件信息 */
    if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
        GAL_SetError("NEWGAL>FBCON: Couldn't get console hardware info\n");
        FB_VideoQuit(this);
        return(-1);
    }

    /*......*/
    /* 内存映射,之后minigui绘制的窗口数据就在这个mapped_mem地址,然后会显示在LCD上 */
    mapped_offset = (((long)finfo.smem_start) -
                    (((long)finfo.smem_start)&~(getpagesize () - 1)));
    mapped_memlen = finfo.smem_len+mapped_offset;
    mapped_mem = mmap(NULL, mapped_memlen,
                            PROT_READ|PROT_WRITE, MAP_SHARED, console_fd, 0);

    /*......*/
    /* We're done! */
    return(0);
}

找到并且打开了显示引擎,那么回到video.c,在调用GAL_VideoInit之后,会调用GAL_SetVideoMode

/* libminigui-gpl-3.2/src/newgal/video.c */
GAL_Surface * GAL_SetVideoMode (int width, int height, int bpp, Uint32 flags)
{
    GAL_VideoDevice *video, *this;
    GAL_Surface *prev_mode, *mode;
    /*......*/
    /* 获取与请求最接近的模式 */
    if (!GAL_GetVideoMode(&video_w, &video_h, &video_bpp, flags)) {
            GAL_SetError ("NEWGAL: GAL_GetVideoMode error, "
                            "not supported video mode.\n");
            return(NULL);
    }
    /*......*/
    /* 调用SetVideoMode去设置模式,video是前面实例化的具体引擎 */
    prev_mode = GAL_VideoSurface;
    GAL_VideoSurface = NULL;    /* In case it's freed by driver */
    mode = video->SetVideoMode(this, prev_mode,video_w,video_h,video_bpp,flags);

    GAL_VideoSurface = (mode != NULL) ? mode : prev_mode;
    /*......*/
    video->info.vfmt = GAL_VideoSurface->format;
    GAL_VideoSurface->video = current_video;

    return(GAL_PublicSurface);
}

/* 这里注意下GAL_PublicSurface的定义 */
/* libminigui-gpl-3.2/src/newgal/sysvideo.h */
extern GAL_VideoDevice *current_video;

#define GAL_VideoSurface	(current_video->screen)
#define GAL_PublicSurface	(current_video->screen)

调用SetVideoMode去设置模式,video是前面实例化的具体引擎,这里用fbcon举例,那么最终调用的是FB_SetVideoMode,这个函数很重要,一些硬件特性在这里面指定

GAL_DOUBLEBUF这个flag目前是没有用到的,这个的作用是什么呢,我们知道framebuffer里面一般会有两个buffer,一个buffer用来显示,一个buffer用来绘制,绘制完成后切换这两个buffer,这样可以解决整屏绘制时肉眼可见的切线问题,这个是硬件上的双缓冲,minigui中也有软件上的双缓冲,请往下看

/* libminigui-gpl-3.2/src/newgal/fbcon/fbvideo.c */
static GAL_Surface *FB_SetVideoMode(_THIS, GAL_Surface *current,
                int width, int height, int bpp, Uint32 flags)
{
    /*......*/
    /* 获取屏幕信息 */
    if (ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0 ) {
        GAL_SetError("NEWGAL>FBCON: Couldn't get console screen info");
        return(NULL);
    }

    /* GAL_DOUBLEBUF这里是没有实现的,但是可以根据需要实现它 */
    if ( ((vinfo.xres != width) || (vinfo.yres != height) ||
         (vinfo.bits_per_pixel != bpp) /* || (flags & GAL_DOUBLEBUF) */) ) {
        /*......*/
        if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
            vinfo.yres_virtual = height;
            if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
                GAL_SetError("NEWGAL>FBCON: Couldn't set console screen info");
                return(NULL);
            }
        }
    } else {
        int maxheight;
        maxheight = height;
        if ( vinfo.yres_virtual > maxheight ) {
            vinfo.yres_virtual = maxheight;
        }
    }
    cache_vinfo = vinfo;

    if ( ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo) < 0 ) {
        GAL_SetError("NEWGAL>FBCON: Couldn't get console hardware info");
        return(NULL);
    }

    /* 这里最重要,设置了minigui绘制时需要的属性,current->pixels是绘制时的首地址 */
    /* 如果需要实现GAL_DOUBLEBUF,那么current->pixels需要根据时机动态切换地址 */
    current->flags = (GAL_FULLSCREEN|GAL_HWSURFACE);
    current->w = vinfo.xres;
    current->h = vinfo.yres;
    current->pitch = finfo.line_length;
    current->pixels = mapped_mem+mapped_offset;

    /* Set up the information for hardware surfaces */
    surfaces_mem = (char *)current->pixels +
                            vinfo.yres_virtual*current->pitch;
    surfaces_len = (mapped_memlen-(surfaces_mem-mapped_mem));

    /* 返回current,也就是current_video和__gal_screen */
    return(current);
}

终于初始化完毕硬件,那么到软件了,也就是mg_InitScreenDC,在dc_InitClipRgnInfo中初始化16个DC,在创建创建双缓冲风格WS_EX_AUTOSECONDARYDC的窗口会使用DC,在调用类似CreateCompatibleDCEx的函数时也会使用DC,所以需要注意不要超过使用数量,__mg_screen_dc是屏幕的DC,在应用的MSG_PAINT消息中调用BeginPaint函数就可以获取到这个对象。创建双缓冲风格的窗口可以在一定程度上改善切线和闪烁现象,但是还是不能和硬件上的相比

/* libminigui-gpl-3.2/src/newgdi/gdi.c */
BOOL mg_InitScreenDC (void* surface)
{
    /* 初始化用于分配剪切矩形的专用块数据堆 */
    InitFreeClipRectList (&__mg_FreeClipRectList, SIZE_CLIPRECTHEAP);
    
    INIT_LOCK (&__mg_gdilock, NULL);
    INIT_LOCK (&dcslot, NULL);
    /* 此函数初始化所有DC插槽中的剪辑区域 */
    dc_InitClipRgnInfo ();
    /* __mg_screen_dc和__mg_screen_sys_dc是屏幕的DC */
    dc_InitScreenDC (&__mg_screen_dc, (GAL_Surface *)surface);
    dc_InitScreenDC (&__mg_screen_sys_dc, (GAL_Surface *)surface);
    return TRUE;
}

/* DCSLOTNUMBER定义在libminigui-gpl-3.2/src/include/dc.h */
static void dc_InitClipRgnInfo(void)
{
    int i;

    for (i=0; i<DCSLOTNUMBER; i++) {
        /* Local clip region */
        InitClipRgn (&DCSlot[i].lcrgn, &__mg_FreeClipRectList);
        MAKE_REGION_INFINITE(&DCSlot[i].lcrgn);

        /* Global clip region info */
        DCSlot[i].pGCRInfo = NULL;
        DCSlot[i].oldage = 0;

        /* Effective clip region */
        InitClipRgn (&DCSlot[i].ecrgn, &__mg_FreeClipRectList);
    }
}

你可能感兴趣的:(MiniGUI,Linux,minigui,gui,fbcon,lcd,disp)