MiniGUI之GAL启动流程
mg_InitGAL()
- 一些变量的含义
w: GVFB窗口内部显示区域的宽度
h: GVFB窗口内部显示区域的高度
depth: bpp--bits per pixel
-
调用GAL_VideoInit(engine, 0)
调用GetMgEtcValue()从MiniGUI.cfg中获取gal_engine的值,从图中可以看到,虽然没有定义NOUNIX这个宏,但可能没有定义“MG_GAL_ENGINE”这个环境变量,所以走到上面那个if的时候,engine == NULL。
打开/usr/local/etc/MiniGUI.cfg文件,system段如下:
可以看到gal_engine定义为pc_xvfb。
- 调用GAL_GetVideo(engine)
这里介绍一个结构体VideoBootStrap:
typedef struct VideoBootStrap {
const char *name; //Video Device的名称
const char *desc; //Video Device描述字符串
int (*available)(void); //判断当前情况下Video Device是否可用
GAL_VideoDevice *(*create)(int devindex); //创建Video Device
} VideoBootStrap;
再介绍一个全局数组bootstrap[]:
/* Available video drivers */
static VideoBootStrap *bootstrap[] = {
#ifdef _MGGAL_DUMMY
&DUMMY_bootstrap,
#endif
#ifdef _MGGAL_FBCON
&FBCON_bootstrap,
#endif
#ifdef _MGGAL_QVFB
&QVFB_bootstrap,
#endif
#ifdef _MGGAL_PCXVFB
&PCXVFB_bootstrap,
#endif
... ...
}
bootstrap[]定义了很多的可用的video drivers,适用于不同的设备环境。其中,在linux的情况下,FBCON和DUMMY这两个是默认提供的;PCXVFB或QVFB这两个需要在linux下装了GVFB或QVFB底层驱动后才有。
因为笔者测试的MiniGUI使用的是pcxvfb引擎,所以这里介绍一下bootstrap中的成员PCXVFB_bootstrap:
VideoBootStrap PCXVFB_bootstrap = {
"pc_xvfb", "PCX Virtual FrameBuffer",
PCXVFB_Available, PCXVFB_CreateDevice
};
- 调用PCXVFB_Available()
该函数永远返回1。 - 调用PCXVFB_CreateDevice(index)
在GAL_GetVideo()调用PCXVFB_CreateDevice(index)时,index = 0。
该函数主要干了两件事:申请内存,给结构体成员赋值。
申请内存:
this = (GAL_VideoDevice *)malloc(sizeof(GAL_VideoDevice));
this->hidden = (struct GAL_PrivateVideoData *) malloc ((sizeof *this->hidden));
给结构体成员赋值:
这里要介绍几个个数据结构,struct GAL_VideoDevice,struct GAL_PrivateVideoData,XVFBHeader。-
struct GAL_VideoDevice
因为该数据结构中的数据成员实在是太多,就删掉了注释,在此以图片的形式呈现:
- struct GAL_PrivateVideoData
该数据结构和MiniGUI与GVFB共享内存相关。
-
/* Private display data /
struct GAL_PrivateVideoData {
unsigned char shmrgn;
XVFBHeader* hdr;
};
3. XVFBHeader
用于控制显示,该数据结构也是MiniGUI同GVFB共享内存的一部分,下面介绍到的时候再细说。
![](https://img.haomeiwen.com/i1352462/68420b9d10f518ca.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
回到GAL_GetVideo()中来,在创建了video之后,对video->name赋值(若底层引擎是PCXVFB,那name就等于pcxvfb),然后做一些初始化工作。这里简单介绍一下GAL_VideoInfo结构体:
![](https://img.haomeiwen.com/i1352462/cb4df62c72de9e57.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
回到GAL_VideoInit()中,初始化video子系统,先介绍一下GAL_PixelFormat数据结构:
typedef struct GAL_PixelFormat {
GAL_Palette *palette;
Uint8 BitsPerPixel; //一个像素占用的位数,可以选择的是1,2,4,8,16,24,32
Uint8 BytesPerPixel; //一个像素占用的字节数,可以是,1,2,4等
/* The flag indicating dithered palette */
Uint8 DitheredPalette;
/* The flag indicating the Most Significant Bits (MSB)
* is left when depth is less than 8. */
Uint8 MSBLeft;
Uint8 Rloss;
Uint8 Gloss;
Uint8 Bloss;
Uint8 Aloss;
Uint8 Rshift;
Uint8 Gshift;
Uint8 Bshift;
Uint8 Ashift;
Uint32 Rmask;
Uint32 Gmask;
Uint32 Bmask;
Uint32 Amask;
/* RGB color key information */
gal_pixel colorkey;
/* Alpha value information (per-surface alpha) */
gal_uint8 alpha;
} GAL_PixelFormat;
其中,Xloss、Xshift、Xmask分别表示RGBA分量所丢失的位数、在像素中的偏移位数和掩码值。举例来说,假设一个像素值由16位来表示,某个像素点的RGB值为565,即R占5位,G占6位,B占5位。因为loss是相对于一个8位分量来说的,拿R来说,R bits=5,所以Rloss=8-5=3;shift是从bpp(或depth)的右边开始算,偏移了多少位,Rshift=16-5=11;mask是把相应颜色对应的位置1,其它位置0得到,Rmask=0x 1111 1000 0000 0000=0xF800。G和B的算法与此类似。
![](https://img.haomeiwen.com/i1352462/950ac05fadd1d5e3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
回到GAL_VideoInit()中,下面这句video->VideoInit(video, &vformat)将会调用PCXVFB_VideoInit()函数。
- 调用PCXVFB_VideoInit()
从MiniGUI.cfg中读取[pc_xvfb]字段下的key的值到execl_file、window_caption和mode中。
![](https://img.haomeiwen.com/i1352462/bca5850e75c36308.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
接下来就跳转到针对linux的代码中。这一段代码功能就是本地socket通信,通过sprintf(socket_file, "/tmp/pcxvfb_socket%d", getpid())和strcpy(server_address.sun_path, socket_file)将把本地套接字的名字设为/tmp/pcxvfb_socket+getpid(),而GVFB也通过这两句获取本地套接字,使其与MiniGUI通信。
1. unlink(socket_file);
是在没有进程打开或使用socket_file的时候,删除本地套接字对应的文件。
2. 开启GVFB子进程
通过调用execl_pcxvfb()开启gvfb进程。
![](https://img.haomeiwen.com/i1352462/159f65b1f231543a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
* 调用execl_pcxvfb()
通过MiniGUI.cfg和getpid(),获取“ch_pid, window_caption, mode, skin”,其中skin为null,最后再调用execlp()开启gvfb进程。
`execlp(execl_file, "pcxvfb", ch_pid, window_caption, mode, skin, NULL)`
3. 接受来自GVFB的连接
![](https://img.haomeiwen.com/i1352462/bc1b663aea4c3da8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
4. 读取GVFB发来的数据
![](https://img.haomeiwen.com/i1352462/061412c5b7fd026c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
注意:这两段代码中有个FD_ZERO()和FD_SET(),虽然从字面上能看懂意思,但总感觉不踏实,用SourceInsight定位也没有找到。后来到网上查资料才知道,这两句是socket编程里面用来操作fd_set的,具体含义请自行查找。
GVFB发给MiniGUI的正是共享内存的shmid,这块共享内存主要包含两部分(或者说三部分,另一部分是PalEntry,这应该是在bpp=8或者以下时才会用到):
1. GFVBHeader结构
用于控制显示,和XVFBHeader结构体相同,只是在不同层有不同的名字。
2. 屏幕显示区域
这块区域的大小是pitch*height,也就是一行像素的总大小乘以高度,这就是整个像素区域的大小。
所以MiniGUI需要将这块内存空间映射到自己的进程空间中,才能使用。
```
data->shmrgn = (unsigned char *)shmat (shmid, 0, 0);
data->hdr = (XVFBHeader *) data->shmrgn;
接下来就是根据这块共享内存中已经设置好的bpp的值(在GVFB创建这块共享内存的时候,会指定),对vformat的成员赋值。
再回到GAL_VideoInit()中,下面这段代码应该适合硬件有关,如果显卡有加速功能的话,才会切入到这块代码。
- 调用GAL_CreateRGBSurface()
接下来就会根据刚才GVFB传过来的共享内存中bpp的值设置的vformat的格式来创建Surface。
Surface是管理显存的一个重要对象。它负责管理显存的大小、色深等信息。而且,可以把一块普通的内存看做一个显存,这样,我们就可以在内存中首先构造出该对象。
该函数创建一个GAL_Surface结构体对象,并初始化了其成员。这其中,我们需要重点注意对format成员和map成员的初始化。对format成员,它调用了GAL_AllocFormat函数。 --doon的专栏
format的创建由GAL_AllocFormat()函数完成,现在看一下GAL_AllocFormat()这个函数。
+ 调用GAL_AllocFormat()
该函数中,主要看default分支代码,功能主要是根据mask的值完成对loss和shift值的计算。PCXVFB_VideoInit()主要是对format的mask值计算,GAL_AllocFormat()时根据mask值计算loss和shift值。
接下来是对surface的一些成员赋值。
surface->w = width;
surface->h = height;
surface->pitch = GAL_CalculatePitch(surface);
surface->pixels = NULL;
surface->offset = 0;
surface->hwdata = NULL;
surface->map = NULL;
surface->format_version = 0;
GAL_SetClipRect(surface, NULL);
再接下来申请一块屏幕大小的内存,和GVFB返回的共享内存的一部分大小相同,都是pitch*height。==这一块可能和显示有关,等以后验证。==
+ 调用GAL_AllocBlitMap()
首先,看一下GAL_BlitMap结构体的定义:
```
typedef struct GAL_BlitMap {
GAL_Surface *dst;
int identity;
Uint8 *table;
GAL_blit hw_blit;
GAL_blit sw_blit;
struct private_hwaccel *hw_data;
struct private_swaccel *sw_data;
/* the version count matches the destination; mismatch indicates an invalid mapping */
unsigned int format_version;
} GAL_BlitMap;
>这里需要关注的重点是hw_blit和sw_blit变量,它们是主要指向blit的操作的回调。GAL_blit的定义是:
```
typedef int (*GAL_blit)(struct GAL_Surface *src, GAL_Rect *srcrect, struct GAL_Surface *dst, GAL_Rect *dstrect);
hw_blit是用硬件加速实现的函数,所以,它是由GAL层实现和设置的。每种不同的开发板有字节的实现方法,所以,这不是我们讨论的重点。sw_blit只软件实现的混合函数,由MiniGUI自己提供,它的确定,是在GAL_MapSurface函数中确定。
接下来看一下GAL_AllocBlitMap函数,该函数主要做了两个分配工作:
`map = (GAL_BlitMap *)calloc(1, sizeof(*map));//malloc(sizeof(*map));`
`map->sw_data = (struct private_swaccel *)calloc(1, sizeof(*map->sw_data));`
现在介绍一下struct private_swaccel结构体:
```
/* This is the private info structure for software accelerated blits */
struct private_swaccel {
GAL_loblit blit;
void *aux_data;
};
GAL_loblit函数指针的类型宏定义:
```
/* The type definition for the low level blit functions */
typedef void (*GAL_loblit)(GAL_BlitInfo *info);
以及GAL_BlitInfo结构体:
```
/* The structure passed to the low level blit functions */
typedef struct {
Uint8 *s_pixels;
int s_width;
int s_height;
int s_skip;
Uint8 *d_pixels;
int d_width;
int d_height;
int d_skip;
void *aux_data;
GAL_PixelFormat *src;
Uint8 *table;
GAL_PixelFormat *dst;
} GAL_BlitInfo;
现在还不知道这些结构体和函数是做什么的,先注意一下。
回到GAL_CreateRGBSurface()中,在对surface的引用计数置1之后,就返回surface了。
回到GAL_VideoInit()中,剩下的代码也没干什么(将video赋值为current_video,对vformat赋值),但也没理解具体这样做的用处。
至此,GAL_VideoInit()函数的整体调用流程就分析完了,下一步是分析GVFB,以及它和MiniGUI是怎么通信的。
##参考链接
1. [MiniGUI源码分析:GDI(1)-- GDI概览及Surface](http://blog.csdn.net/doon/article/details/7091379)
网友评论