嵌入式Linux系统的很多应用领域,诸如消费类电子产品、测量控制设备等,图形用户界面不仅在技术上是软件系统设计的一个重点,而且在商业上也关系到用户对该产品接受的程度。根据产品功能低高端的不同定位,系统设计对图形用户界面的要求也不同,一般大致可以分为单进程方式的轻量级图形界面和多任务窗口系统图形用户界面GUI,前者主要用于低端的信息终端和工业控制系统,后者主要用于PDA、机顶盒、DVD/VCD播放机、WAP手机等产品中。
目前,在Linux操作系统中,一般可将图形应用库粗略地分为三个不同层次。第一层次是图形基础设施,它们本身没有提供相应的应用程序编程接口,而是集成在操作系统中或采用某种封装形式,用作其他高级图形或者应用程序的基本函数库,较典型的有X Window、SVGALib、framebuffer(帧缓冲)和LibGGI等;第二层次是高级函数库,它们提供了大量的应用程序编程接口,较典型的有Xlib、GDK、GTK+、QT、SDL、OpenGL、PEG和DirectFB等;第三层次多任务窗口系统图形用户界面GUI,较典型的有Microwindows、OpenGUI、Qt/Embedded和MiniGUI等。
在低端的嵌入式系统中, 由于用户图形界面仅仅需要用到一些简单的画点、画线、图片显示和中西文输入/显示等,同时考虑到成本、占资源大小和稳定性等诸多因素,因此在对Microwindows进行相应裁减的基础上进行应用图形库的设计。
1 Microwindows的分层结构
Microwindows是一个著名的开放源码的嵌入式GUI软件,专门用于小型嵌入式设备上开发高性能图形应用程序和多任务窗口系统。它用C语言实现,可移植性好,能够在嵌入式Linux上运行。目前,它不仅可以在支持Framebuffer的32位嵌入式Linux系统上运行,还可以在SVGALib库上运行,甚至可以被移植到16位的ELKS和实模式的MSDOS上。
Microwindows采用分层结构设计,共有三层(见图1)。底层驱动层是面向基本的图形输出和键盘、鼠标或触摸屏的驱动程序,在程序中通过相应的数据结构就能访问实际的硬件设备;中间引擎层提供底层硬件的抽象接口,是一个可移植的图形引擎,提供点线绘制、区域填充、多边形绘制、裁减和RGB颜色模式使用等;最高应用层分别提供兼容于X Window 和Windows CE(Win32子集)的API,同时提供窗口管理。
底层驱动层也叫设备与平台相关层,这一层的功能是将系统与设备和操作系统平台的具体细节屏蔽起来。它通过实际的设备驱动程序接口或者OS系统调用来与硬件设备交互,这些硬件设备主要包括屏幕、鼠标和键盘等。我们使用设备对象(device object)的概念来描述一类设备,每一个对象描述了一类实际设备的属性和方法。比如,屏幕设备对象就描述了其各种属性(屏幕尺寸、分辨率、像素深度、像素格式、逻辑显存首地址等)和基本方法(打开和关闭显示器、设置调色板、返回屏幕属性、读写像素点等)。最底层实际上是以设备对象的方式为中间层提供了一个抽象的设备驱动界面。Microwindows在这一层中对屏幕、鼠标、触摸屏和键盘等设备分别定义了一个对应的数据结构。其中,屏幕设备驱动结构体SCREENDEVICE指定了诸如设备的大小、硬件使用的图形模式等底层的显示情况以及打开、关闭、画点线等方法;键盘设备驱动结构体KBDDEVICE定义打开、关闭和读取键值等方法; 屏幕信息的结构体MWSCREENINFO和位图信息的结构体MWIMAGEINFO是两个常用的结构体,用以取得当前打开的显示屏幕和位图的长、宽、位色等属性值。
中间引擎层也叫设备与平台无关层,这一层的功能是提供一个可以为各种应用层共享的与设备无关的核心图形引擎,其中的主要工作就是实现各种图形函数和输入设备的功能函数。对于中间层,它向下看到的是各类设备对象,向上则是要提供一个抽象的核心图形界面,使得上面的应用层对它所使用的到底是什么设备对象不用去理会。当运行在Linux系统中时,Microwindows提供的所有绘图函数都是通过调用底层屏幕驱动Framebuffer或SVGALib来实现的。它支持行绘制、区域填充、剪切以及RGB颜色模型,控制字体的显示等。
最高层即应用层,这一层的功能是按照应用的具体要求为应用程序提供适当的应用层用户界面。当应用程序不需要窗口系统的时候,用户自定义图形界面将十分简单,甚至可以什么都不做而直接使用中间层提供的抽象核心界面(本文讨论的图形应用库就是基于该原理来构建的)。如果用户需要完善多任务窗口系统,则可以使用抽象核心界面来实现其应用程序编程接口(API)以及窗口和消息机制等。Microwindows实现了MicrosoftWin32/WinCE图形显示接口(GDI)和Xlib(XWindows)接口两种API以适应不同的应用环境。其中前者应用于所有的Windows CE和Win32应用程序,用于设计类Win32图形用户界面GUI;后者就像Nano-X,应用于所有Linux X插件集的最底层,这样可让Linux图形程序员X接口开发图形应用程序。
显然,Microwindows的分层设计使得其能够在需要的时候易于改写和定制,能够运行在任何支持Framebufer的Linux系统(2.2以上版本的内核)中,这些特点使得Microwindows在嵌入式系统设计中的应用十分广泛。
2 图形应用库的设计
尽管Microwindwos已经提供了一个全功能的可视化图形用户界面开发工具,但是由它生成的代码量很大,在某些类低端的嵌入式Linux系统中不适合,因此,设计一个面向低端的、非窗口管理的基本图形应用库就显得非常重要。它占用较少的磁盘空间和较少的内存开销,旨在为嵌入式系统构建基本的图形用户界面提供编程接口。基本图形应用库的设计思路是以Microwindows驱动层和独立图形引擎层为核心,将它们抽取出来,不再采用分层结构,最后构建一个尽可能小的、满足绘图、显示、中文输入等功能的轻量级图形应用库。该图形应用库类似于Turboc C,支持灰度/彩色LCD和PS/2键盘,屏幕驱动支持1/2/4/8/l6/32bpp,能进行相应的中西文输入和显示;具有强大的绘图功能,包括画线、区域填充、画多边形、剪贴和图形模块等。显然,由于图形库以framebufer为基础,无需特殊操作系统或图形系统的支持,能很好的在嵌入式Linux系统上运行,具有较好的移植性、易使用性、稳定性。
这里基于Linux2.4.19和Microwindows0.89进行讨论,主要难点:一是将Microwindows层次打乱后如何进行代码的重构,用最少的代码实现最有效的功能,二是提供中文显示和中文输入的支持。
2.1 结构重构
(1)底层驱动层
整个系统的核心是键盘和屏幕数据结构,它们在Linux系统中都是被当作文件来进行访问,其C代码主要在src/drivers和src/engine目录下。
键盘是通过fd=open("/dev/tty",O_NONBLOCK)打开,利用ioctl来进行操作的,涉及到的文件有kbd_ttyscan.c(提供键盘的打开、关闭等支持)。
屏幕驱动是基于Linux内核中framebuffer,这要求在编译内核的时候选择支持framebuffer编译参数选项。它是通过fd=open(env="/dev/fb0")打开,用SCREENDEVICE的指针PSD指向这片显存,然后对这片显存根据屏幕的不同位色设置情况为中间引擎层提供相应的图形操作支持,包括画点线、图片显示、屏幕拷贝以及中西文字的显示等等。其涉及到的文件较多,类型定义与函数声明的头文件有fb.h、genfont.h和genmem.h,C代码文件有src_fb.c(提供基本的framebuffer打开和关闭等支持)、fb*.c(*为2、4、8、16、24、32,提供对应不同灰度级别和不同位色屏幕的支持)、genmem.c(提供显存分配)和genfont.c(提供中西文字体显示支持)。
(2)中间引擎层
这一层是在底层驱动层提供的设备对象支持下,完成图形在实虚屏之间的转换(以PSD指针作为参数来进行),实现各种图形功能函数(以Gd...为开头)。相关的类型定义与函数声明头文件有include目录下的mwtypes.h、swap.h和winkbd.h,C代码主要有src/engine目录下的devarc.c(提供弧线和椭圆绘制支持)、devclip.c(提供剪贴支持)、devdraw.c(提供基本的绘图支持)、devfont.c(提供字体字库支持)、devimage(提供图片绘制拷贝支持)、devkbd.c(提供键盘控制支持)、devrgn.c(提供区域操作动态分配支持)和devpal*.c(*为1、2、4、8,提供调色板支持)。
在分析完驱动层和引擎层后,将它们的相关文件放在同一个目录下,利用gcc编译器编译,链接生成目标文件,然后用ar归档命令即可生成库文件(动态库和静态库),只需要将这个库文件提交给二次开发人员即可进行图形应用程序的开发。
2.2 中文支持
在嵌入式Linux应用系统中,控制台驱动程序和Framebuffer驱动程序对字符的处理都是以单字节为基础的,所以需要进行中文化的改造。
(1)中文显示的支持
这里采用16×16点阵的GB2312字库,字模文件hzk.bin存放在/font/chinese目录下。对于一个需要显示的字符串,首先判断其是属于哪种编码集,如果是ASCII码,就调用Microwindows提供的GdText函数进行显示;如果是汉字,则根据其机内码得到区位码,计算该汉字字模在字模文件中的偏移量,读出该汉字字模,调用底层DrawPixel函数的像素点,并显示这个汉字。
(2)中文输入的支持
由于Microwindows对输入法没有任何支持,所以这一块几乎所有的代码都需要重新编写。目前只提供GB2312字库的拼音输入方法,且只能逐字输入(见图2),同时也默认字模文件hzk.bin存放在/fonts/chinese目录下。
先定义一个拼音结构体:
struct PY_index
{ char PY[6];//拼音的韵母
char *PY_mb;//对应的汉字机内码
};
然后根据GB2312字库和汉字的声母、韵母定义拼音输入法查询码表。查询码表分两部分,第一部分是二级索引表,它将每个拼音和汉字对应起来:
stmct PY_index PY_index_a[5]={
{"","阿啊呵腌嗄锕吖"},
……
{"i","爱哀挨碍埃癌艾唉矮哎皑蔼隘暖霭捱嗳瑷嫒锿嗌砹”},
{"o","奥澳傲熬敖凹袄懊坳嗷拗鏖骜鳌翱岙廒遨獒聱媪螯鏊"}};
……
strucet PY_index PY_index_z[36]={{"a","杂扎砸咋咂匝拶"},
{"ai","在再载灾仔宰哉栽崽甾"),
{"an","咱赞暂攒簪糌瓒拶昝趱錾"),
……
{"un","尊遵樽鳟撙“},
{"uo","作做坐座左昨琢佐凿撮柞嘬怍胙唑笮阼祚酢"}};
street PY_index PY_index_end [1] = {{"",
PY_mb_space}
};
其中PY_mb_space为常量0xffff,它用于两个地方,一是i,v,u三个字母不能作为声母,所以它们没有对应的汉字,这里就以0xffff来约定;二是表示拼音表的结束。
第二部分是一级索引表,它将26个首字母(即声母)和其韵母对应起来:
street PY_index code *code PY_index_headletter[27]=
{
PY_index_a,
PY_index_b,
PY_index_z,
PY_ index_end
};
有了上面定义的两个索引表,就可以进行汉字的输入了。
3 结论
图形应用库提供了图形系统的初始化、键盘操作、区域块拷贝、中西文的输入显示、基本图形绘制等共计40多个API功能函数,很好的满足了低端嵌入式Linux系统的图形应用程序开发的需要。该图形应用库只有70多KB大小,占用资源少,性能稳定,很好的满足了低端信息终端和控制系统等嵌入式Linux产品设计的需要, 目前已经成功应用于嵌入式税控收款机(pos)、自动柜员机(ATM)等嵌入式产品中。