⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 minigui 体系结构之四 图形抽象层和输入抽象层及 native engine 的实现(二).htm

📁 MiniGUI的体系结构
💻 HTM
📖 第 1 页 / 共 4 页
字号:
      href="http://www-900.ibm.com/developerWorks/cn/linux/embed/minigui/minigui-8/index.shtml">第一部分</A>)</I></P><A 
      id=3 name=3></A>
      <P><STRONG class=subhead>3 Native 图形引擎的实现</STRONG></P>
      <P>Native 图形引擎的图形驱动程序已经提供了基于Linux内核提供FrameBuffer之上的驱动,目前包括对线性 2 
      bpp、4bpp、8bpp和 16bpp 
      显示模式的支持。前面已经看到,GAL提供的接口函数大多数与图形相关,它们主要就是通过调用图形驱动程序来完成任务的。图形驱动程序屏蔽了底层驱动的细节,完成底层驱动相关的功能,而不是那么硬件相关的一些功能,如一些画圆,画线的GDI 
      函数。</P>
      <P>下面基于已经实现的基于FrameBuffer 的驱动程序,讲一些实现上的细节。首先列出的核心数据结构 
      SCREENDEVICE。这里主要是为了讲解方便,所以删除了一些次要的变量或者函数。</P>
      <TABLE class=code-sample cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD><PRE>                              清单 5  Native 图形引擎的核心数据结构

typedef struct _screendevice {
    int xres;       /* X screen res (real) */
    int yres;       /* Y screen res (real) */
    int planes;     /* # planes*/
    int bpp;        /* # bits per pixel*/
    int linelen;    /* line length in bytes for bpp 1,2,4,8, line length in pixels for bpp 16, 24, 32*/
    int size;       /* size of memory allocated*/
    gfx_pixel gr_foreground;      /* current foreground color */
    gfx_pixel gr_background;      /* current background color */
    int     gr_mode;
    int flags;      /* device flags*/
    void *  addr;       /* address of memory allocated (memdc or fb)*/

    PSD (*Open)(PSD psd);
    void    (*Close)(PSD psd);
    void    (*SetPalette)(PSD psd,int first,int count,gfx_color *cmap);
    void    (*GetPalette)(PSD psd,int first,int count,gfx_color *cmap);
    PSD (*AllocateMemGC)(PSD psd);
    BOOL    (*MapMemGC)(PSD mempsd,int w,int h,int planes,int bpp, int linelen,int size,void *addr);
    void    (*FreeMemGC)(PSD mempsd);
    void    (*FillRect)(PSD psd,int x,int y,int w,int h,gfx_pixel c);
    void     (*DrawPixel)(PSD psd, int x, int y, gfx_pixel c);
    gfx_pixel (*ReadPixel)(PSD psd, int x, int y);
    void    (*DrawHLine)(PSD psd, int x, int y, int w, gfx_pixel c);
    void    (*PutHLine) (GAL gal, int x, int y, int w, void* buf);
    void    (*GetHLine) (GAL gal, int x, int y, int w, void* buf);
    void    (*DrawVLine)(PSD psd, int x, int y, int w, gfx_pixel c);
    void    (*PutVLine) (GAL gal, int x, int y, int w, void* buf);
    void    (*GetVLine) (GAL gal, int x, int y, int w, void* buf);
    void (*Blit)(PSD dstpsd, int dstx, int dsty, int w, int h, PSD srcpsd, int srcx, int srcy);
    void    (*PutBox)( GAL gal, int x, int y, int w, int h, void* buf );
    void    (*GetBox)( GAL gal, int x, int y, int w, int h, void* buf );
    void    (*PutBoxMask)( GAL gal, int x, int y, int w, int h, void *buf);
    void    (*CopyBox)(PSD psd,int x1, int y1, int w, int h, int x2, int y2);
} SCREENDEVICE;
</PRE></TD></TR></TBODY></TABLE>
      <P>上面PSD 是 SCREENDEVICE 的指针,GAL 是GAL 接口的数据结构。</P>
      <P>我们知道,图形显示有个显示模式的概念,一个像素可以用一位比特表示,也可以用2,4,8,15,16,24,32个比特表示,另外,VGA16标准模式使用平面图形模式,而VESA2.0使用的是线性图形模式。所以即使是同样基于Framebuffer 
      的驱动,不同的模式也要使用不同的驱动函数:画一个1比特的单色点和画一个24位的真彩点显然是不一样的。</P>
      <P>所以图形驱动程序使用了子驱动程序的概念来支持各种不同的显示模式,事实上,它们才是最终的功能函数。为了保持数据结构在层次上不至于很复杂,我们通过图形驱动程序的初始函数Open直接将子驱动程序的各功能函数赋到图形驱动程序的接口函数指针,从而初始化结束就使用一个简单的图形驱动接口。下面是子图形驱动程序接口(清单 
      6)。</P>
      <TABLE class=code-sample cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD><PRE>                     清单 6  Native 图形引擎的子驱动程序接口

typedef struct {
    int  (*Init)(PSD psd);
    void     (*DrawPixel)(PSD psd, int x, int y, gfx_pixel c);
    gfx_pixel (*ReadPixel)(PSD psd, int x, int y);
    void    (*DrawHLine)(PSD psd, int x, int y, int w, gfx_pixel c);
    void    (*PutHLine) (GAL gal, int x, int y, int w, void* buf);
    void    (*GetHLine) (GAL gal, int x, int y, int w, void* buf);
    void    (*DrawVLine)(PSD psd, int x, int y, int w, gfx_pixel c);
    void    (*PutVLine) (GAL gal, int x, int y, int w, void* buf);
    void    (*GetVLine) (GAL gal, int x, int y, int w, void* buf);
    void (*Blit)(PSD dstpsd, int dstx, int dsty, int w, int h, PSD srcpsd, int srcx, int srcy);
    void    (*PutBox)( GAL gal, int x, int y, int w, int h, void* buf );
    void    (*GetBox)( GAL gal, int x, int y, int w, int h, void* buf );
    void    (*PutBoxMask)( GAL gal, int x, int y, int w, int h, void *buf);
    void    (*CopyBox)(PSD psd,int x1, int y1, int w, int h, int x2, int y2);
} SUBDRIVER, *PSUBDRIVER;
</PRE></TD></TR></TBODY></TABLE>
      <P>可以看到,该接口中除了 Init 函数指针外,其他的函数指针都与图形驱动程序接口中的函数指针一样。这里的Init 
      函数主要用来完成图形驱动部分与显示模式相关的初始化任务。</P>
      <P>下面介绍SCREENDEVICE数据结构,这样基本上就可以清楚图形引擎了。</P>
      <P>一个SCREENDEVICE代表一个屏幕设备,它即可以对应物理屏幕设备,也可以对应一个内存屏幕设备,内存屏幕设备的存在主要是为了提高GDI 
      质量,比如我们先在内存生成一幅位图,再画到屏幕上,这样给用户的视觉效果就比较好。</P>
      <P>首先介绍几个变量。</P>
      <UL>
        <LI>xres 表示屏幕的宽 (以像素为单位); 
        <LI>yres 表示屏幕的高 (以像素为单位); 
        <LI>planes :当处于平面显示模式时,planes 
        用于记录所使用的平面数,如平面模式相对的时线性模式,此时该变量没有意义。通常将其置为0。 
        <LI>bpp :表示每个像素所使用的比特数,可以为1,2,4,8,15,16,24,32。 
        <LI>linelen :对与1,2,4,8比特每像素模式,它表示一行像素使用的字节数,对于大于8比特每像素模式,它表示一行的像素总数。 
        <LI>size :表示该显示模式下该设备使用的内存数。linelen 和 size 的存在主要是为了方便为内存屏幕设备分配内存。 
        <LI>gr_foreground 和 gr_background :表示该内存屏幕的前景颜色和背景颜色,主要被一些GDI 函数使用。 
        <LI>gr_mode :说明如何将像素画到屏幕上,可选值为:MODE_SET MODE_XOR MODE_OR MODE_AND 
        MODE_MAX,比较常用的是MODE_SET和MODE_XOR 
        <LI>flags :该屏幕设备的一些选项,比较重要的是 PSF_MEMORY 标志,表示该屏幕设备代表物理屏幕设备还是一个内存屏幕设备。 
        <LI>addr :每个屏幕设备都有一块内存空间用来作为存储像素。addr 变量记录了这个空间的起始地址。 </LI></UL>
      <P>下面介绍各接口函数:</P>
      <UL>
        <LI>Open,Close 
        <LI style="LIST-STYLE-TYPE: none">
        <P>基本的初始化和终结函数。前面已经提到,在 Open 函数里要选择子图形驱动程序,将其实现的函数赋给本 PSD 
        结构的函数指针。这里我讲讲基于Frambebuffer 的图形引擎的初始化。</P>
        <P>fb_open 首先打开Framebuffer的设备文件 /dev/fb0,然后利用 ioctl 
        读出当前Framebuffer的各种信息。填充到PSD 
        结构中。并且根据这些信息选出子驱动程序。程序当前支持fbvga16,fblin16,fblin8,即VGA16 
        标准模式,VESA线性16位模式,VESA线性8位模式。然后将当前终端模式置于图形模式。并保存当前的一些系统信息如调色板信息。最后,系统利用mmap 
        将 /dev/fb0 映射到内存地址。以后程序访问 /dev/fb0 
        就像访问一个数组一样简单。当然,这是对线性模式而言的,如果是平面模式,问题要复杂的多。光从代码来看,平面模式的代码是线性模式的实现的将近一倍。后面的难点分析里将讲解这个问题。</P>
        <LI>SetPalette,GetPalette 
        <LI style="LIST-STYLE-TYPE: none">
        <P>当使用8位或以下的图形模式时,要使用系统调色板。这里是调色板处理函数,它们和Windows API 中的概念类似,linux 系统利用 
        ioctl 提供了处理调色板的接口。</P>
        <LI>AllocateMemGC,MapMemGC,FreeMemGC 
        <LI style="LIST-STYLE-TYPE: none">
        <P>前面屡次提到内存屏幕的概念,内存屏幕是一个伪屏幕,在对屏幕图形操作过程中,比如移动窗口,我们先生成一个内存屏幕,将物理屏幕的一个区域拷贝到内存屏幕,再拷贝到物理屏幕的新位置,这样就减少了屏幕直接拷贝的延时。AllocateMemGC 
        用于给内存屏幕分配空间,MapMemGC 做一些初始化工作,而FreeMemGC 则释放内存屏幕。</P>
        <LI>DrawPixel,ReadPixel,DrawHLine,DrawVLine,FillRect 
        <LI style="LIST-STYLE-TYPE: none">
        <P>这些是底层图形函数。分别是画点,读点,画水平线,画竖直线,画一个实心矩形。之所以在底层实现这么多函数,是为了提高效率。图形函数支持多种画图模式,常用的有直接设置,亦或,Alpha混合模式,从而可以支持各种图形效果。</P>
        <LI>PutHLine,GetHLine,PutVLine,GetVLine,PutBox,GetBox,PutBoxMask 
        <LI style="LIST-STYLE-TYPE: none">
        <P>Get* 函数用于从屏幕拷贝像素到一块内存区,而Put*函数用于将存放于内存区的像素画到屏幕上。PutBoxMask 
        与PutBox的唯一区别是要画的像素如果是白色,就不会被画到屏幕上,从而达到一种透明的效果。</P>
        <P>从上面可以看到,这些函数的第一个参数是GAL类型而不是PSD类型,这是因为它们需要GAL层的信息以便在函数内部实现剪切功能。之所以不和其他函数一样在上层实现剪切,是因为这里的剪切比较特殊。比如PutBox,</P>
        <P>在剪切输出域时,要同时剪切在缓冲中待输出的像素:超出剪切域的像素不应该被输出。所以,剪切已经不单纯是对线,矩形等GDI对象的剪切。对像素的剪切当然需要知道像素的格式,这些只是为底层所有,所以为了实现高效的剪切,我们选择在底层实现它们。这里所有的函数都有两个部分:先是剪切,再是读或者写像素。</P>
        <LI>Blit,CopyBox 
        <LI style="LIST-STYLE-TYPE: none">
        <P>Blit 
        用于在不同的屏幕设备(物理的或者内存的)之间拷贝一块像素点,CopyBox则用于在同一屏幕上实现区域像素的拷贝。如果使用的是线性模式,Blit的实现非常简单,直接memcpy 
        就可以了,而CopyBox 
        为了防止覆盖问题,必须根据不同的情况,采用不同的拷贝方式,比如从底到顶底拷贝,当新老位置在同一水平位置并且重复时,则需要利用缓冲间接拷贝。如果使用平面显示模式,这里就比较复杂了。因为内存设备总是采用线性模式的,所以就要判断是物理设备还是内存设备,再分别处理。这也大大地增加了fbvga16实现的代码。</P></LI></UL><A 
      id=4 name=4></A>
      <P><STRONG class=subhead>4 Native 输入引擎的实现</STRONG></P><STRONG>4.1 
      鼠标驱动程序</STRONG> 
      <P>鼠标驱动程序非常简单,抽象意义上讲,初始化鼠标后,每次用户移动鼠标,就可以得到一个X 和 Y 
      方向上的位移值,驱动程序内部维护鼠标的当前位置,用户移动了鼠标后,当前位置被加上位移值,并通过上层Cursor支持,反映到屏幕上,用户就会认为鼠标被他正确地“移动”了。</P>
      <P>事实上,鼠标驱动程序的实现是利用内核或者其他驱动程序提供的接口来完成任务的。Linux 
      内核驱动程序使用设备文件对大多数硬件进行了抽象,比如,我们眼中的 ps/2 鼠标就是 /dev/psaux, 鼠标驱动程序接口如清单 7 
      所示。</P>
      <TABLE class=code-sample cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD><PRE>                       清单 7  Native 输入引擎的鼠标驱动程序接口

typedef struct _mousedevice {
    int (*Open)(void);
    void    (*Close)(void);
    int (*GetButtonInfo)(void);
    void    (*GetDefaultAccel)(int *pscale,int *pthresh);
    int (*Read)(int *dx,int *dy,int *dz,int *bp);
    void (*Suspend)(void);
    void (*Resume)(void);
} MOUSEDEVICE;
</PRE></TD></TR></TBODY></TABLE>
      <P>现在有各种各样的鼠标,例如ms 鼠标, ps/2 鼠标,总线鼠标,gpm 鼠标,它们的主要差别在于初始化和数据包格式上。</P>
      <P>例如,打开一个GPM 鼠标非常简单,只要将设备文件打开就可以了,当前终端被切换到图形模式时,GPM 
      服务程序就会把鼠标所有的位移信息放到设备文件中去。</P>
      <TABLE class=code-sample cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD><PRE>static int GPM_Open(void)
{
    mouse_fd = open(GPM_DEV_FILE, O_NONBLOCK);
    if (mouse_fd &lt; 0)
        return -1;
    return mouse_fd;
}
</PRE></TD></TR></TBODY></TABLE>
      <P>对于PS/2 鼠标,不但要打开它的设备文件,还要往该设备文件写入控制字符以使得鼠标能够开始工作。</P>
      <TABLE class=code-sample cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD><PRE>static int PS2_Open(void)
{
uint8 initdata_ps2[] = { PS2_DEFAULT, PS2_SCALE11, PS2_ENABLE };
    mouse_fd = open(PS2_DEV_FILE, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (mouse_fd &lt; 0) 
            return -1;
    write(mouse_fd, initdata_ps2, sizeof(initdata_ps2));
    return mouse_fd;
}
</PRE></TD></TR></TBODY></TABLE>
      <P>各鼠标的数据包格式是不一样的。而且在读这些数据时,首先要根据内核驱动程序提供的格式读数据,还要注意同步:每次扫描到一个头,才能读后面相应的数据,象Microwindows由于没有同步,在某些情况下,鼠标就会不听“指挥”。</P>
      <P>鼠标驱动程序中,还有一个“加速”的概念。程序内部用两个变量:scale 和thresh 来表示。当鼠标的位移超过 thresh 时,就会被放大 
      scale 倍。这样,最后的位移就是:</P>
      <TABLE class=code-sample cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD><PRE>        dx = thresh + (dx - thresh) * scale;
        dy = thresh + (dy - thresh) * scale;
</PRE></TD></TR></TBODY></TABLE>
      <P>至此,mouse driver 基本上很清楚了,上面的接口函数中GetButtonInfo用来告诉调用者该鼠标支持那些button, 
      suspend 和resume 函数是用来支持虚屏切换的,下面的键盘驱动程序也一样。</P><STRONG>4.2 键盘驱动程序</STRONG> 
      <P>在实现键盘驱动程序中遇到的第一个问题就是使用设备文件 /dev/tty还是 /dev/tty0。</P>
      <TABLE class=code-sample cellPadding=0 width="100%" border=0>
        <TBODY>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -