
一种嵌入式图形用户界面系统的设计与实现.docx
13页一种嵌入式图形用户界面系统的设计与实现摘要:为了满意嵌入式系统的界面设计需求,给出了一种图形用户界面系统SKY-GUI 的设计思路和其在嵌入式Linux 环境下的实现方法SKY-GUI 有四大组成部分:输入抽象层、显示抽象层、大事系统和窗口系统其特点是界面美观、占用资源少、运行效率高,现已应用于嵌入式视频监控项目试验证明其设计思路可行,性能优良,适用于典型的嵌入式系统项目 嵌入式图形用户界面( GUI, Graphic UserInterface)系统作为嵌入式系统中的一大关键技术,为用户供应设备的掌握接口,其性能的好坏,界面的美观程度,影响着用户对产品的购买意愿和使用感受 当前嵌入式系统中GUI 的实现方式主要有两种:一是采纳现有的GUI 库;其次种是开发商基于嵌入式操作系统设计特有的GUI 系统采纳第1种方式一般要对通用GUI 库进行剪裁和共性化定制,也往往要支出额外的成原来获得软件授权相对而言,第2 种方法实现的GUI 占用资源较小、简单满意嵌入式系统的实时性和共性化需求 本文采纳第2 种方式,在嵌入式Linux下使用C 语言实现了一个界面美观、轻量级、占用资源少、执行效率高的图形用户界面系统SKY-GUI.本文的结构如下:第1 部分介绍SKY-GUI 的基本结构;第2部分给出详细的模块设计;第3 部分给出其试验和测试结果;总结。
1 SKY-GUI 基本结构 SKY-GUI 系统的功能主要有以下四点: (1)接收各种输入设备的输入 (2)建立消息循环,将设备的输入翻译为抽象的大事 (3)建立窗口和控件对象系统,组织好各个抽象模块的关系,处理各种GUI 大事 (4)将GUI 对象通过详细外形显示在屏幕上,通过动画将系统的状态变化呈现给用户 基于这样的设计目标,SKY-GUI 结构如图1 所示 图1 SKY-GUI 的整体架构 它主要由输入抽象层、显示抽象层、大事系统和窗口系统四大部分组成输入抽象层管理全部的输入设备,将用户的操作转化成消息送入大事系统 显示抽象层操作显示设备,供应给窗口系统绘画、贴图、显示字体接口大事系统为窗口系统供应消息猎取、存储和处理的机制窗口系统是SKY-GUI 的,定义了各种控件和窗口,描述它们之间的规律关系和消息循环关系下面将分别对这四大组成部分的设计进行介绍 2 SKY-GUI 模块设计 2. 1 输入抽象层 输入抽象层为各种输入设备对大事系统的输入接口,它是一个单独的线程,其流程如图2 所示。
图2 输入抽象层流程 输入抽象层首先对GUI 所需的各种输入设备初始化,而后等待各个设备的输入当接到设备输入,就把用户对设备的操作翻译成消息,送至大事系统层消息队列(在异2. 3. 2 具体争论)中 设备输入的翻译过程依据详细的输入设备而定对于键盘,只要将其键值和该键的状态封成消息对于鼠标,除了要记录其按键状态,还要依据鼠标当前的位置和屏幕的大小将输入的位移重量转化成鼠标的新位置封入消息 2. 2 显示抽象层 显示抽象层的作用是为窗口系统供应显示接口函数,包括基本图形接口(画点、画线、填充矩形、区域拷贝、Alpha 混合等)、贴图接口和字体接口三大功能,其结构如图3 所示 图3 显示抽象层的结构 显示抽象层在嵌入式Linux 下的基础设备为帧缓冲,对其按坐标写入或读出颜色值即可实现基本的图形接口的功能 简洁的贴图功能用基本图形接口加bmp 格式的文件(图片不经过压缩,其颜色重量按坐标挨次存储)就可以实现为了让界面更加美观,SKY-GUI移植了开源的jpeg 库和png 库来解压相应格式的压缩图片文件,实现了对这两种图片格式的支持。
对字体的支持当然必不行少点阵字体把字体的位图按12伊12、16伊16 等格式存入二进制文件,可以比较简单地实现字体接口,但字体不能随便放大缩小,且放大后字体有明显的锯齿矢量字体用数学方程加字形上的关键点来描述字体,可以进行无级缩放,为界面的绘制带来极大的敏捷性SKY-GUI 移植了开源的Freetype 库,用其寻址矢量字体文件并生成字体位图,实现了对矢量字体的支持 2. 3 大事系统 大事系统为SKY-GUI 的其他三大部分供应消息发送、存储、猎取和处理的功能其为消息、消息队列和消息处理函数 2. 3. 1 消息定义 SKY-GUI 的消息定义为: typedef STruct __MSG { HWND hWnd;摇/ / 窗口指针 int event;摇/ / 大事编号 void* wParam;摇/ / 大事附加参数1 void* lParam;摇/ / 大事附加参数2 } MSG; hWnd 为指向窗口的指针,表明此消息需要发给哪个窗口event 为大事编号,用不同的整数代表不同的大事。
wParam 和lParam 为大事的附加参数,它们的含义依据大事类型的不同而定,例如,在鼠标消息中这两个参数就代表光标在屏幕上的坐标位置 2. 3. 2 消息队列 消息队列是大事系统中的消息的暂存处,它由一个环形先入先出结构的消息数组和一个消息链表组成消息数组的空间是固定的,一旦被写满,后来的消息只好被丢弃;而消息链表则可以动态扩充大小在SKY-GUI 中,消息数组主要用来存放底层输入设备的大事(如鼠标、键盘、时钟等等),而消息链表主要用来存放优先级更高且不行丢弃的上层大事(窗口大事和显示大事) 2. 3. 3 消息操作接口 SKY-GUI 定义了三类消息操作接口:消息发送函数、消息猎取函数和大事处理函数 消息发送函数为输入抽象层和窗口系统供应消息发送接口,包括Post_Msg 函数和Send_Msg 函数,其作用都是向消息队列发送消息,不同之处在于Post_Msg 发送的消息存入消息队列的数组之中,而Send_Msg 发送的消息则存入链表之中 消息猎取函数为Get_Msg 函数,它为窗口供应取得消息的接口拥有独立线程的窗口( 异2. 4 会描述其结构) 调用它从消息队列中取得一个消息,其中存在链表中的消息更为重要,优先取出。
大事处理函数是窗口处理消息大事的函数接口,在SKY-GUI 中,拥有独立线程的窗口调用Dispatch_Msg 函数来实现对自己消息处理函数的调用 2. 3. 4 消息处理函数 Dispatch_Msg 只是大事处理的调用接口,窗口收到消息后所实行的详细措施是由消息处理函数打算的,其定义为: int WndProc ( HWND hwnd, int event, void *wParam,void* lParam); 每一个窗口都有一个函数指针指向自己的消息处理函数,其功能依据不同的窗口有所不同,但总体结构是一样的,如图4 所示 图4 消息处理函数的结构 其本质上是一个消息处理的分类列表当窗口调用消息处理函数时,其依据消息类型的不同分别调用底层输入消息、控件消息或显示消息的处理函数,而后再依据详细的消息大事调用相应的处理函数,实现对各种大事的响应 2. 4 窗口系统 窗口系统为SKY-GUI 系统的,它维护了一个完整的窗口列表,定义了窗口系统和大事系统之间的关系,并制定了窗口之间的消息传递机制 2. 4. 1 窗口的定义 SKY-GUI 中,窗口既包含桌面、对话框这种狭义的窗口,也包含窗口控件( 如按钮、下拉菜单、编辑框等等)这样的广义窗口,其定义为: typedef struct __WINDOW { STR32 caption; / / 窗口的名称 RECT rect; / / 窗口的大小、位置 int style; / / 窗口的类型 MsgQueue* pMsgQ; / / 附属于窗口的消息队列 struct __WINDOW*pFocus; / / 活动窗口指针 struct __WINDOW*pParent; / / 父窗口指针 struct __WINDOW*pChldHead; / / 子窗口列表 struct __WINDOW*pNext; / / 兄弟窗口或控件指针 struct __WINDOW*pCtrlHead; / / 控件列表 WNDPROC WndProc; / / 消息处理函数指针 void* data1; / / 窗口私有数据 void* data2; / / 窗口私有数据 void* data3; / / 窗口私有数据 int msg1; / / 窗口状态变化消息 } WINDOW; caption 为窗口的名称;rect 为保存窗口位置和大小的矩形;style 为窗口的类型;pMsgQ 为窗口的消息队列的指针;pFocus 指向当前窗口的活动子窗口或控件;pParent 指向当前窗口的父窗口;pNext 指向当前窗口的兄弟窗口;pChldHead 用来保存当前窗口的子窗口列表;pCtrlHead 保存当前窗口的控件列表。
WndProc 指向当前窗口的消息处理函数;data1、data2、data3 为窗口的私有数据,msg1 为窗口状态变化时需要发出的控件消息,它们的意义依据窗口的类型而定 从窗口的定义可以看出,本文要实现的是一种树形的窗口关系,整个系统可以拥有一个或多个主窗口,每个主窗口拥有自己的控件和子窗口,而子窗口又可以拥有各自的子窗口和控件,依此类推 2. 4. 2 窗口与消息队列的关系 窗口定义中含有指向消息队列的指针,但并不是全部的窗口都有自己的消息队列主窗口(如桌面)需要随时呈现在用户的面前,可以拥有自己的消息队列;其他的子窗口、控件则没有必要拥有自己的消息队列这两类窗口用不同的方式使用大事系统 拥有消息队列的主窗口必需拥有自己独立的线程,其消息发送和处理的流程如图5 所示 图5 拥有消息队列的窗口的消息发送和处理流程 当其他窗口或输入抽象层需要操作主窗口时,就调用大事系统中的Post_Msg 或Send_Msg 函数向该窗口的消息队列发送一个消息而主窗口得知有消息输入,就调用大事系统中的Get_Msg 函数取出消息,并使用Dispatch_Msg 调用自己的消息处理函数,找到相应的大事处理方法处理事务。
这种消息传递的特点是消息的发送和处理分别在不同的窗口线程中完成,一般用于两个主窗口之间或者输入抽象层和主窗口之间的消息通信 没有消息队列的子窗口或控件处理消息的流程如图6 所示 图6 没有消息队列的窗口的消息处理流程 主窗口调用大事系统中的Post_Msg 或Send_Msg 函数向子窗口或控件发送消息,由于该窗口没有自己的消息队列,大事系统不会将该消息保存,而是直接调用该窗口的消息处理函数找到详细的大事处理方式完成这次窗口操作这种消息传递方式中,发送消息和处理消息都在主窗口的线程中完成,向一个窗口发送消息相当于要求该窗口立即对大事进行处理。
