全文预览已结束
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Framebuffer的配置及应用*一、FrameBuffer的原理*FrameBuffer是出现在2.2.xx内核当中的一种驱动程序接口。Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。但Framebuffer本身不具备任何运算数据的能力,就只好比是一个暂时存放水的水池.CPU将运算后的结果放到这个水池,水池再将结果流到显示器.中间不会对数据做处理.应用程序也可以直接读写这个水池的内容.在这种机制下,尽管Framebuffer需要真正的显卡驱动的支持,但所有显示任务都有CPU完成,因此CPU负担很重.framebuffer的设备文件一般是/dev/fb0、/dev/fb1等等。可以用命令:#dd if=/dev/zero of=/dev/fb清空屏幕.如果显示模式是1024x768-8位色,用命令:$dd if=/dev/zero of=/dev/fb0 bs=1024 count=768清空屏幕;用命令:#dd if=/dev/fb of=fbfile可以将fb中的内容保存下来;可以重新写回屏幕:#dd if=fbfile of=/dev/fb;在使用Framebuffer时,Linux是将显卡置于图形模式下的.在应用程序中,一般通过将FrameBuffer设备映射到进程地址空间的方式使用,比如下面的程序就打开/dev/fb0设备,并通过mmap系统调用进行地址映射,随后用memset将屏幕清空(这里假设显示模式是1024x768-8位色模式,线性内存模式):int fb;unsigned char*fb_mem;fb=open(/dev/fb0,O_RDWR);fb_mem=mmap(NULL,1024*768,PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);memset(fb_mem,0,1024*76;FrameBuffer设备还提供了若干ioctl命令,通过这些命令,可以获得显示设备的一些固定信息(比如显示内存大小)、与显示模式相关的可变信息(比如分辨率、象素结构、每扫描线的字节宽度),以及伪彩色模式下的调色板信息等等。通过FrameBuffer设备,还可以获得当前内核所支持的加速显示卡的类型(通过固定信息得到),这种类型通常是和特定显示芯片相关的。比如目前最新的内核(2.4.9)中,就包含有对S3、Matrox、nVidia、3Dfx等等流行显示芯片的加速支持。在获得了加速芯片类型之后,应用程序就可以将PCI设备的内存I/O(memio)映射到进程的地址空间。这些memio一般是用来控制显示卡的寄存器,通过对这些寄存器的操作,应用程序就可以控制特定显卡的加速功能。PCI设备可以将自己的控制寄存器映射到物理内存空间,而后,对这些控制寄存器的访问,给变成了对物理内存的访问。因此,这些寄存器又被称为memio。一旦被映射到物理内存,Linux的普通进程就可以通过mmap将这些内存I/O映射到进程地址空间,这样就可以直接访问这些寄存器了。当然,因为不同的显示芯片具有不同的加速能力,对memio的使用和定义也各自不同,这时,就需要针对加速芯片的不同类型来编写实现不同的加速功能。比如大多数芯片都提供了对矩形填充的硬件加速支持,但不同的芯片实现方式不同,这时,就需要针对不同的芯片类型编写不同的用来完成填充矩形的函数。FrameBuffer只是一个提供显示内存和显示芯片寄存器从物理内存映射到进程地址空间中的设备。所以,对于应用程序而言,如果希望在FrameBuffer之上进行图形编程,还需要自己动手完成其他许多工作。*二、FrameBuffer在Linux中的实现和机制*Framebuffer对应的源文件在linux/drivers/video/目录下。总的抽象设备文件为fbcon.c,在这个目录下还有与各种显卡驱动相关的源文件。(一)、分析Framebuffer设备驱动需要特别提出的是在INTEL平台上,老式的VESA1.2卡,如CGA/EGA卡,是不能支持Framebuffer的,因为Framebuffer要求显卡支持线性帧缓冲,即CPU可以访问显缓冲中的每一位,但是VESA1.2卡只能允许CPU一次访问64K的地址空间。FrameBuffer设备驱动基于如下两个文件:1)linux/include/linux/fb.h 2)linux/drivers/video/fbmem.c下面分析这两个文件。1、fb.h几乎主要的结构都是在这个中文件定义的。这些结构包括:1)fb_var_screeninfo这个结构描述了显示卡的特性:struct fb_var_screeninfo_u32 xres;/*visible resolution*/_u32 yres;_u32 xres_virtual;/*virtual resolution*/_u32 yres_virtual;_u32 xoffset;/*offset from virtual to visible resolution*/_u32 yoffset;_u32 bits_per_pixel;/*guess what*/_u32 grayscale;/*!=0 Gray levels instead of colors*/struct fb_bitfield red;/*bitfield in fb mem if true color,*/struct fb_bitfield green;/*else only length is significant*/struct fb_bitfield blue;struct fb_bitfield transp;/*transparency*/_u32 nonstd;/*!=0 Non standard pixel format*/_u32 activate;/*see FB_ACTIVATE_*/_u32 height;/*height of picture in mm*/_u32 width;/*width of picture in mm*/_u32 accel_flags;/*acceleration flags(hints)*/*Timing:All values in pixclocks,except pixclock(of course)*/_u32 pixclock;/*pixel clock in ps(pico seconds)*/_u32 left_margin;/*time from sync to picture*/_u32 right_margin;/*time from picture to sync*/_u32 upper_margin;/*time from sync to picture*/_u32 lower_margin;_u32 hsync_len;/*length of horizontal sync*/_u32 vsync_len;/*length of vertical sync*/_u32 sync;/*see FB_SYNC_*/_u32 vmode;/*see FB_VMODE_*/_u32 reserved6;/*Reserved for future compatibility*/;2)fb_fix_screeninfon这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改;比如FrameBuffer内存的起始地址。它依赖于被设定的模式,当一个模式被设定后,内存信息由显示卡硬件给出,内存的位置等信息就不可以修改。struct fb_fix_screeninfochar id16;/*identification string egTT Builtin*/unsigned long smem_start;/*Start of frame buffer mem*/*(physical address)*/_u32 smem_len;/*Length of frame buffer mem*/_u32 type;/*see FB_TYPE_*/_u32 type_aux;/*Interleave for interleaved Planes*/_u32 visual;/*see FB_VISUAL_*/_u16 xpanstep;/*zero if no hardware panning*/_u16 ypanstep;/*zero if no hardware panning*/_u16 ywrapstep;/*zero if no hardware ywrap*/_u32 line_length;/*length of aline in bytes*/unsigned long mmio_start;/*Start of Memory Mapped I/O*/*(physical address)*/_u32 mmio_len;/*Length of Memory Mapped I/O*/_u32 accel;/*Type of acceleration available*/_u16 reserved3;/*Reserved for future compatibility*/;3)fb_cmap描述设备无关的颜色映射信息。可以通过FBIOGETCMAP和FBIOPUTCMAP对应的ioctl操作设定或获取颜色映射信息.struct fb_cmap_u32 start;/*First entry*/_u32 len;/*Number of entries*/_u16*red;/*Red values*/_u16*green;_u16*blue;_u16*transp;/*transparency,can be NULL*/;4)fb_info定义当显卡的当前状态;fb_info结构仅在内核中可见,在这个结构中有一个fb_ops指针,指向驱动设备工作所需的函数集。struct fb_infochar modename40;/*default video mode*/kdev_t node;int flags;int open;/*Has this been open already?*/#define FBINFO_FLAG_MODULE 1/*Low-level driver is amodule*/struct fb_var_screeninfo var;/*Current var*/struct fb_fix_screeninfo fix;/*Current fix*/struct fb_monspecs monspecs;/*Current Monitor specs*/struct fb_cmap cmap;/*Current cmap*/struct fb_ops*fbops;char*screen_base;/*Virtual address*/struct display*disp;/*initial display variable*/struct vc_data*display_fg;/*Console visible on this display*/char fontname40;/*default font name*/devfs_handle_t devfs_handle;/*Devfs handle for new name*/devfs_handle_t devfs_lhandle;/*Devfs handle for compat.symlink*/int(*changevar)(int);/*tell console var has changed*/int(*switch_con)(int,struct fb_info*);/*tell fb to switch consoles*/int(*updatevar)(int,struct fb_info*);/*tell fb to update the vars*/void(*blank)(int,struct fb_info*);/*tell fb to(un)blank the screen*/*arg=0:unblank*/*arg 0:VESA level(arg-1)*/void*pseudo_palette;/*Fake palette of 16 colors and the cursors color for non palette mode*/*From here on everything is device dependent*/void*par;5)struct fb_ops用户应用可以使用ioctl()系统调用来操作设备,这个结构就是用一支持ioctl()的这些操作的。struct fb_ops/*open/release and usage marking*/struct module*owner;int(*fb_open)(struct fb_info*info,int user);int(*fb_release)(struct fb_info*info,int user);/*get non settable parameters*/int(*fb_get_fix)(struct fb_fix_screeninfo*fix,int con,struct fb_info*info);/*get settable parameters*/int(*fb_get_var)(struct fb_var_screeninfo*var,int con,struct fb_info*info);/*set settable parameters*/int(*fb_set_var)(struct fb_var_screeninfo*var,int con,struct fb_info*info);/*get colormap*/int(*fb_get_cmap)(struct fb_cmap*cmap,int kspc,int con,struct fb_info*info);/*set colormap*/int(*fb_set_cmap)(struct fb_cmap*cmap,int kspc,int con,struct fb_info*info);/*pan display(optional)*/int(*fb_pan_display)(struct fb_var_screeninfo*var,int con,struct fb_info*info);/*perform fb specific ioctl(optional)*/int(*fb_ioctl)(struct inode*inode,struct file*file,unsigned int cmd,unsigned long arg,int con,struct fb_info*info);/*perform fb specific mmap*/int(*fb_mmap)(struct fb_info*info,struct file*file,struct vm_area_struct*vma);/*switch to/from raster image mode*/int(*fb_rasterimg)(struct fb_info*info,int start);6)structure map struct fb_info_gen|struct fb_info|fb_var_screeninfo|fb_fix_screeninfo|fb_cmap|modename40|fb_ops-|-ops on var|.|fb_open|fb_release|fb_ioctl|fb_mmap|struct fbgen_hwswitch-|-detect|encode_fix|encode_var|decode_fix|decode_var|get_var|set_var|getcolreg|setcolreg|pan_display|blank|set_disp编排有点困难,第一行的第一条竖线和下面的第一列竖线对齐,第一行的第二条竖线和下面的第二列竖线对齐就可以了这个结构fbgen_hwswitch抽象了硬件的操作.虽然它不是必需的,但有时候很有用.2、fbmem.c fbmem.c处于Framebuffer设备驱动技术的中心位置.它为上层应用程序提供系统调用也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向系统内核注册它们自己.fbmem.c为所有支持FrameBuffer的设备驱动提供了通用的接口,避免重复工作.1)全局变量struct fb_info*registered_fbFB_MAX;int num_registered_fb;这两变量记录了所有fb_info结构的实例,fb_info结构描述显卡的当前状态,所有设备对应的fb_info结构都保存在这个数组中,当一个FrameBuffer设备驱动向系统注册自己时,其对应的fb_info结构就会添加到这个结构中,同时num_registered_fb为自动加1.static structconst char*name;int(*init)(void);int(*setup)(void);fb_drivers _initdata=.;如果FrameBuffer设备被静态链接到内核,其对应的入口就会添加到这个表中;如果是动态加载的,即使用insmod/rmmod,就不需要关心这个表。static struct file_operations fb_ops=owner:THIS_MODULE,read:fb_read,write:fb_write,ioctl:fb_ioctl,mmap:fb_mmap,open:fb_open,release:fb_release;这是一个提供给应用程序的接口.2)fbmem.c实现了如下函数.register_framebuffer(struct fb_info*fb_info);unregister_framebuffer(struct fb_info*fb_info);这两个是提供给下层FrameBuffer设备驱动的接口,设备驱动通过这两函数向系统注册或注销自己。几乎底层设备驱动所要做的所有事情就是填充fb_inf o结构然后向系统注册或注销它。(二)一个LCD显示芯片的驱动实例以Skeleton LCD控制器驱动为例,在LINUX中存有一个/fb/skeleton.c的skeleton的Framebuffer驱动程序,很简单,仅仅是填充了fb_info结构,并且注册/注销自己。设备驱动是向用户程序提供系统调用接口,所以我们需要实现底层硬件操作并且定义file_operations结构来向系统提供系统调用接口,从而实现更有效的LCD控制器驱动程序。1)在系统内存中分配显存在fbmem.c文件中可以看到,file_operations结构中的open()和release()操作不需底层支持,但read()、write()和mmap()操作需要函数fb_get_fix()的支持.因此需要重新实现函数fb_get_fix()。另外还需要在系统内存中分配显存空间,大多数的LCD控制器都没有自己的显存空间,被分配的地址空间的起始地址与长度将会被填充到fb_fix_screeninfo结构的smem_start和smem_len的两个变量中.被分配的空间必须是物理连续的。2)实现fb_ops中的函数用户应用程序通过ioctl()系统调用操作硬件,fb_ops中的函数就用于支持这些操作。(注:fb_ops结构与file_operations结构不同,fb_ops是底层操作的抽象,而file_operations是提供给上层系统调用的接口,可以直接调用ioctl()系统调用在文件fbmem.c中实现,通过观察可以发现ioctl()命令与fb_opss中函数的关系:FBIOGET_VSCREENINFO fb_get_var FBIOPUT_VSCREENINFO fb_set_var FBIOGET_FSCREENINFO fb_get_fix FBIOPUTCMAP fb_set_cmap FBIOGETCMAP fb_get_cmap FBIOPAN_DISPLAY fb_pan_display如果我们定义了fb_XXX_XXX方法,用户程序就可以使用FBIOXXXX宏的ioctl()操作来操作硬件。文件linux/drivers/video/fbgen.c或者linux/drivers/video目录下的其它设备驱动是比较好的参考资料。在所有的这些函数中fb_set_var()是最重要的,它用于设定显示卡的模式和其它属性,下面是函数fb_set_var()的执行步骤:1)检测是否必须设定模式2)设定模式3)设定颜色映射4)根据以前的设定重新设置LCD控制器的各寄存器。第四步表明了底层操作到底放置在何处。在系统内存中分配显存后,显存的起始地址及长度将被设定到LCD控制器的各寄存器中(一般通过fb_set_var()函数),显存中的内容将自动被LCD控制器输出到屏幕上。另一方面,用户程序通过函数mmap()将显存映射到用户进程地址空间中,然后用户进程向映射空间发送的所有数据都将会被显示到LCD显示器上。*三、FrameBuffer的应用*(一)、一个使用FrameBuffer的例子1.FrameBuffer主要是根据VESA标准的实现的,所以只能实现最简单的功能。2.由于涉及内核的问题,FrameBuffer是不允许在系统起来后修改显示模式等一系列操作。(好象很多人都想要这样干,这是不被允许的,当然如果你自己写驱动的话,是可以实现的).3.对FrameBuffer的操作,会直接影响到本机的所有控制台的输出,包括XWIN的图形界面。好,现在可以让我们开始实现直接写屏:1、打开一个FrameBuffer设备2、通过mmap调用把显卡的物理内存空间映射到用户空间3、直接写内存。/*File name:fbtools.h*/#ifndef _FBTOOLS_H_#define _FBTOOLS_H_#include linux/fb.h/a framebuffer device structure;typedef struct fbdevint fb;unsigned long fb_mem_offset;unsigned long fb_mem;struct fb_fix_screeninfo fb_fix;struct fb_var_screeninfo fb_var;char dev20;FBDEV,*PFBDEV;/open&init aframe buffer/to use this function,/you must set FBDEV.dev=/dev/fb0/or/dev/fbX/its your frame fb_open(PFBDEV pFbdev);/close aframe buffer int fb_close(PFBDEV pFbdev);/get display depth int get_display_depth(PFBDEV pFbdev);/full screen clear void fb_memset(void*addr,int c,size_t len);#endif/*File name:fbtools.c*/#include stdio.h#include stdlib.h#include fcntl.h#include unistd.h#include string.h#include sys/ioctl.h#include sys/mman.h#include asm/page.h#includefbtools.h#define TRUE 1#define FALSE 0#define MAX(x,y)(x)(y)?(x)y)#define MIN(x,y)(x)(y)?(x)y)/open&init aframe buffer int fb_open(PFBDEV pFbdev)pFbdev-fb=open(pFbdev-dev,O_RDWR);if(pFbdev-fb 0)printf(Error opening%s:%m.Check kernel confign,pFbdev-dev);return FALSE;if(-1=ioctl(pFbdev-fb,FBIOGET_VSCREENINFO,&(pFbdev-fb_var)printf(ioctl FBIOGET_VSCREENINFOn);return FALSE;if(-1=ioctl(pFbdev-fb,FBIOGET_FSCREENINFO,&(pFbdev-fb_fix)printf(ioctl FBIOGET_FSCREENINFOn);return FALSE;/map physics address to virtual address pFbdev-fb_mem_offset=(unsigned long)(pFbdev-fb_fix.smem_start)&(PAGE_MASK);pFbdev-fb_mem=(unsigned long int)mmap(NULL,pFbdev-fb_fix.smem_len+pFbdev-fb_mem_offset,PROT_READ|PROT_WRITE,MAP_SHARED,pFbdev-fb,0);if(-1L=(long)pFbdev-fb_mem)printf(mmap error!mem:%d offset:%dn,pFbdev-fb_mem,pFbdev-fb_mem_offset);return FALSE;return TRUE;/close frame buffer int fb_close(PFBDEV pFbdev)close(pFbdev-fb);pFbdev-fb=-1;/get display depth int get_display_depth(PFBDEV pFbdev);if(pFbdev-fb=0)printf(fb device not open,open it firstn);return FALSE;return pFbdev-fb_var.bits_per_pixel;/full screen clear void fb_memset(void*addr,int c,size_t len)memset(addr,c,len);/use by test#define DEBUG#ifdef DEBUG main()FBDEV fbdev;memset(&fbdev,0,sizeof(FBDEV);strcpy(fbdev.dev,/dev/fb0);if(fb_open(&fbdev)=FALSE)printf(open frame buffer errorn);return;fb_memset(fbdev.fb_mem+fbdev.fb_mem_offset,0,fbdev.fb_fix.smem_len);fb_close(&fbdev);(二)基于Linux核心的汉字显示的尝试我们以一个简单的例子来说明字符显示的过程。我们假设是在虚拟终端1(/dev/tty1)下运行一个如下的简单程序。main()puts(hello,world.n);puts函数向缺省输出文件(/dev/tty1)发出写的系统调用write(2)。系统调用到linux核心里面对应的核心函数是console.c中的con_w rite(),con_write()最终会调用do_con_write()。在do_con_write()中负责把hello,world.n这个字符串放到tty1对应的缓冲区中去。do_con_write()还负责处理控制字符和光标的位置。让我们来看一下do_con_write()这个函数的声明。static int do_con_write(struct tty_struct*tty,int from_user,const unsigned char*buf,int count)其中tty是指向tty_struct结构的指针,这个结构里面存放着关于这个tty的所有信息(请参照linux/include/linux/tty.h)。Tty_struct结构中定义了通用(或高层)tty的属性(例如宽度和高度等)。在do_con_ write()函数中用到了tty_struct结构中的driver_data变量。driver_data是一个vt_struct指针。在vt_struct结构中包含这个tty的序列号(我们正使用tty1,所以这个序号为1)。Vt_struct结构中有一个vc结构的数组vc_cons,这个数组就是各虚拟终端的私有数据。static int do_con_write(struct tty_struct*tty,int from_user,const unsigned char*buf,int count)struct vt_struct*vt=(struct vt_struct*)tty-driver_data;/我们用到了driver_data变量.currcons=vt-vc_num;file:/我们在这里的vc_nums就是1.要访问虚拟终端的私有数据,需使用vc_conscurrcons.d指针。这个指针指向的结构含有当前虚拟终端上光标的位置、缓冲区的起始地址、缓冲区大小等等。hello,world.n中的每一个字符都要经过conv_uni_to_pc()这个函数转换成8位的显示字符。这要做的主要目的是使不同语言的国家能把16位的UniCode码映射到8位的显示字符集上,目前还是主要针对欧洲国家的语言,映射结果为8位,不包含对双字节(double byte)的范围。这种UNICODE到显示字符的映射关系可以由用户自行定义。在缺省的映射表上,会把中文的字符映射到其他的字符上,这是我们不希望看到也是不需要的。所以我们有两个选择1.不进行conv_uni_to_pc()的转换。2.加载符合双字节处理的映射关系,即对非控制字符进行1对1的不变映射。我们自己定制的符合这种映射关系的UNICODE码表是direct.uni。要想查看/装载当前系统的unicode映射表,可使外部命令loadunima p。经过conv_uni_to_pc()转换之后,hello,world.n中的字符被一个一个地填写到tty1的缓冲区中。然后do_con_write()调用下层的驱动,把缓冲区中的内容输出到显示器上(也就相当于把缓冲区的内容拷贝到VGA显存中去)。sw-con_putcs(vc_conscurrcons.d,(u16*)draw_from,(u16*)draw_to-(u16*)draw_from,y,draw_x);之所以要调用底层驱动,是因为存在不同的显示设备,其对应VGA显存的存取方式也不一样。上面的Sw-con_putcs()就会调用到fbcon.c中的fbcon_putcs()函数(con_putcs是一个函数的指针,在Framebuffer模式下指向fbcon_putcs()函数)。也就是说在do_con_write()函数中是直接调用了fbcon_putcs()函数来进行字符的绘制。比如说在256色模式下,真正负责输出的函数是void fbcon_cfb8_putcs(struct vc_data*conp,struct display*p,const unsigned short*s,int count,int yy,int xx)显示中文比如说我们试图输出一句中文putcs(你好n);(你好的内码为0xc4,0xe3,0xba,0xc3)。这时候会怎么样呢,有一点可以肯定,你好肯定不会出现在屏幕上,国为核心中没有汉字字库,中文显示就是无米之炊了.1在负责字符显示的void fbcon_cfb8_putcs()函数中,原有操作如下对于每个要显示的字符,依次从虚拟终端缓冲区中以WORD为单位读取(低位字节是ASCII码,高8位是字符的属性),由于汉字是双字节编码方式,所以这种操作是不可能显示出汉字的,只能显示出xxxx_putcs()是一个一个VGA字符.要解决的问题确保在do_con_write()时unipc转换不会改变原有编码。一个很直接的实现方式就是加载一个我们自己定制的UNICODE映射表,loadunimapdirect.uni,或者直接把direct.uni置为核心的缺省映射表。针对如上问题,我们要做的第一个尝试方案是如下。首先需要在核心中加载汉字字库,然后修改fbcon_cfb8_putcs()函数,在fbcon_cfb8_putcs()中一次读两个WORD,检查这两个WORD的低位字节是否能拼成一个汉字,如果发现能拼成一个汉字,就算出这个汉字在汉字字库中的偏移,然后把它当成一个16 x16的VGA字符来显示。试验的结果表明1.能够输出汉字,但仍有许多不理想的地方,比如说,输出以半个汉字开始的一串汉字,则这半个汉字后面的汉字都会是乱码。这是半个汉字的问题。2.光标移动会破坏汉字的显示。表现为,光标移动过的汉字会变成乱码。这是因为光标的更新是通过xxxx_putc()函数来完成的。xxxx_putc()函数与xxxx_putcs()函数实现的功能类似,但是xxxx_putc()函数只刷新一个字符而不是一个字符串,因而xxxx_putc()的输入参数是一个整数,而不是一个字符串的地址。Xxxx_putc()函数的声明如下void fbcon_cfb8_putc(struct vc_data*conp,struct display*p,int c,int yy,int xx)下一个尝试方案就是同时修改xxxx_putcs()函数和xxxx_putc()函数。为了解决半个汉字的问题,每一次输出之前,都从屏幕当前行的起始位置开始扫描,以确定要输出的字符是否落在半个汉字的位置上。如果是半个汉字的位置,则进行相应的调整,即从向前移动一个字节的位置开始输出。这个方案有一个困难,即xxxx_putc()函数不用缓冲区的地址,而是用一个整数作为参数。所以xxxx_putc()无法直接利用相邻的字符来判别该定符是否是汉字。解决方案是,利用xxxx_putc()的光标位置参数(yy,xx),可以逆推出该字符在缓冲区中的位置。但仍有一些小麻烦,在Linux的虚拟终端下,用户可能会上卷该屏幕(shift+pageup),导致光标的y座标和相应字符在缓冲区的行数不一致。相应的解决方案是,在逆推的过程中,考虑卷屏的参量。这样一来,我们就又进了一步,得到了一个相对更好的版本。但仍有问题没有解决。敲入turbonetcfg,会发现菜单的边框字符也被当成汉字显示。这是因为,这种边框字符是扩展字符,也使用了字符的第8位,因而被当作汉字来显示。例如,单线一的制表符内码为0xC4,当连成一条长线就是由一连串0xC4组成,而0x C4C4正是汉字哪。于是水平的制表符被一连串的哪字替代了。要解决这个问题就非常不容易了,因为制表符的种类比较多,而且垂直制表符与其后面字符的组合型式又多种多样,因而很难判断出相应位置的字符是不是制表符,从理论上说,无论采取什么样的排除算法,都必然存在误判的情况,因为总存在二义性,没有充足的条件来推断出当前字符究竟是制表符还是汉字。我们一方面寻找更好的排除组合算法,一方面试图寻找其它的解决方案。要想从根本上解决定个问题,必须利用其它的辅助信息,仅仅从缓冲区的字符来判断是不够的。经过一番努力,我们发现,在UNIX中使用扩展字符时,都要先输出字符转义序列(Escape sequence)来切换当前字符集。字符转义序列是以控制字符Esc为首的控制命令,在UNIX的虚拟终端中完成终端控制命令,这种命令包括,移动光标座标、卷屏、删除、切换字符集等等。也就是说在输出代表制表符的字符串之前,通常是要先输出特定的字符转义序列。在console.c里,有根据字符转义序列命令来记录字符状态的变量。结合该变量提供的信息,就可以非常干净地把制表符与汉字区别开来。在如上思路的指引下,我们又产生了新的解决方案。经过改动得到了另一各版本.在这个新版本上,turbonetcfg在初次绘制的时候,制表符与汉字被清晰地区分开来,结果是非常正确的。但还有新的问题存在turbonetcfg在重绘的时候(如切换虚拟终端或是移动鼠标光标的时候),制表符还是变成了汉字,因为重绘完全依赖于缓冲区,而这时用来记录字符集状态的变量并不反映当前字符集状态。问题还是没有最终解决。我们又回到了起点。(看来问题的最终解决手段必须是把字符集的状态伴随每一个字符存在缓冲区中。让我们来研究一下缓冲区的结构。每一个字符占用16bit的缓冲区,低8位是ASCI I值,完全被利用,高8位包含前景颜色和背景颜色的属性,也没有多余的空间可以利用。因而只能另外开辟新的缓冲区。为了保持一致性,我们决定在原来的缓冲区后面添加相同大小的缓冲区,用来存放是否是汉字的信息。也许有读者会问,我们只需要为每个字符添加一bit的信息来标志是否是汉字就足够了,为什么还要开辟与原缓冲区大小相同的双倍缓冲区,是不是太浪费呢?我们先放下这个问题,稍后再作回答。其实,如果再添加一bit来标志是当前字符是汉字的左半边还是右半边的话,就会省去扫描屏幕上当前整行字符串的工作,这样一来,编程会更简单。但是有读者会问,即使是这样,使用8bit总够用了吧?为什么还要使用16bit呢?我们的作法是用低8位来存放汉字另外一半的内码,用高8位中的2 bit来存放上面所讲的辅助信息,高8位的剩余6位可以用来存放汉字或其它编码方式(如BIG5或日文、韩文)的信息,从而使我们可以实现同屏显示多种双字节语言的字符而不会有相互干扰。另外,在编程时,双倍缓冲也比较容易计算。这样我们就回答了如上的两个问题。迄今为止,我们有了一套彻底解决汉字和制表符相互干扰、半个汉字的刷新、重绘等问题的方案。剩下的就是具体编程实现的问题了。但是,由于Framebuffer的驱动很多,修改每一个驱动的xxxx_putc()函数和xxxx_putcs()函数会是一项不小的工作,而且,改动驱动程序后,每种驱动的测试也是很麻烦的,尤其是对于有硬件加速的显卡,修改和测试会更不容易。那么,存不存在一种不需要修改显卡驱动程序的方法呢?经过努力,我们发现,可以在调用xxxx_putcs()或xxxx_putc()函数输出汉字之前,修改vga字库的指针使其指向所需显示的汉字在汉字字库中的位置,即把一个汉字当成两个vga ASCII字符输出。也就是说,在内核中存在两个字库,一个是原有的vga字符字库,另一个是汉字字库,当我们需要输出汉字的时候,就把vga字库的指针指向汉字字库的相应位置,汉字输出完之后,再把该指针指向vga字库的原有位置。这样一来,我们只需要修改fbcon.c和console.c,其中console.c负责维护双倍缓冲区,把每一个字符的信息存入附加的缓冲区;而fbcon.c负责利用双倍缓冲区中附加的信息,调整vga字库的指针,调用底层的显示驱动程序。这里还有几个需要注意的地方1.由于屏幕重绘等原因,调用底层驱动xxxx_putc()和xxxx_putcs()的地方有多处。我们作了两个函数分别包装这两个调用,完成替换字库、调用xxxx_putcs()或xxxx_putc()、恢复字库等功能。2.为了实现向上滚屏(shift+pageup)时也能看到汉字,我们需要作另外的修改。Linux在设计虚拟终端的时候,提供了回顾被卷出屏幕以外的信息的功能,这就是用热键来向上滚屏(shift+pageup)。当前被使用的虚拟终端拥有一个公共的缓冲区(soft back),用来存放被滚出屏幕以外的信息。当切换虚拟终端的时候,公共缓冲区的内容会被清除而被新的虚拟终端使用。向上滚屏的时候,显示的是公共缓冲区中的内容。因此,如果我们想在向上滚屏的时候看到汉字,公共缓冲区也必须加倍,以确保没有信息丢失。当滚出屏幕的信息向公共缓冲区填写的时候,必须把相应的附加信息也填写进公共缓冲区的附加区域。这就要求fbcon.c必须懂得利用公共缓冲区的附加信息。当然,有另外一种偷懒的方法,那就是不允许用户向上滚屏,从而避免对公区缓冲区的处理。3.把不同的编码方式(GB、BIG5、日文和韩文)写成不同的module,以实现动态加载,从而使得扩展新的编码方式不需要重新编译核心。测试本文实现的Kernel Patch文件(patch.kernel.chinese)可以从下载。Cd/usr/src/(该目录下应有Linux核心源程序所在的目录linux/)patch-p0-b patch.kernel.chinesemake menuconfig请选择Console drivers选项中的*Double Byte Character Display Support(EXPERIMENTAL)*Double Byte GB encode(module only)*VESA VGA graphics console*Virtual Frame Buffer support(ONLY FOR TESTING!)*8 bpp packed pixels support*16 bpp packed pixels support*VGA characters/attributes support*Select compiled-in fonts*VGA 8x8 font*VGA 8x16 font
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 赤峰翁牛特旗公务员考试试题及答案
- 电缆辅材生产线项目建设工程方案
- 城市生态综合整治项目施工方案
- 北辰区公务员行测考试试题及答案
- 片区城中村改造项目施工方案
- 乡镇污水处理及配套管网工程初步设计
- 十五五规划纲要:碳市场扩容与减排激励机制创新
- 2026年建筑装饰公司装饰工艺研发立项评审管理制度
- 数据要素治理体系:“十五五”制度创新重点
- 信阳航空考试题库及答案
- 战略性矿产资源的探产供储销体系优化
- 《红日》读书分享模板
- 机械伤害事故应急演练方案(现场处置方案)
- 2024年国家电网网络安全专业考试题库(附答案)
- 钢结构桁架吊装安装专项施工方案
- 消防知识竞赛考试题(附答案)
- 2025年浙能集团甘肃有限公司新能源项目招聘22人笔试历年参考题库附带答案详解
- 佛教协会人员管理制度
- 厂区雨污水管网施工方案
- 碳排放交易课件
- 乳品微生物知识培训课件
评论
0/150
提交评论