请保留-> 【原文: https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
在开始这个探索系列的时候,我就计划在UEFI上移植一个完整的GUI库。
前面开发各种图形编程、特效实现的代码,其实有很大一部分来自于我之前另外一个项目-Foxdisk。在这个项目中,某种程度上实现了任务的切换,可以看做一个小型的、带有图形显示的shell界面。
不过,Foxdisk中的键盘处理是完全脱离于图形的,而且也没有实现鼠标的处理。简而言之,这是一个很松散的、模块化的GUI库。与我期望的,类似于MFC、QT、JUCE之类的库相差甚远。
UEFI不是一个完整的操作系统,所选用的GUI库实际上最好偏向于嵌入式的。在简单考察了几个开源的GUI库后,包括LearingGUi、GuiLite、littlevgl等,我选择了从GuiLite开始着手这项工作。
一方面是因为其代码量看起来不大,6000行左右,花费的时间应该不会太多;二是这款开源库是国人开发的,我也加入了作者建的QQ群,有什么问题可以很方便地请教。
1 GUILite类的组织
按照其文档介绍,GuiLite只做两个工作:界面元素管理和图形绘制。而图形绘制不依赖于界面管理,可以独立存在,以应对需要移植到资源有限的单片机环境。
其类的组织图如下:
GUILite的类图有点像MFC的结构,深入进去也能发现,其消息处理机制也很像MFC。作者提炼了需要的部分,做成了一个精简的、适用多平台的图形库,非常了不起。
c_cmd_target类主要用来处理消息的定位,核心函数是find_msg_entry();
c_wnd类定义了窗口的基本框架,大部分的控件类,包括c_dialog、c_button等均派生于它;
c_display和c_surface主要用来实现图形的虚拟绘制。所谓虚拟绘制,也就是说并不在实际硬件上绘制,而是定义内存缓冲区,在缓冲区内将像素点排列好。也就是提供了类似Linux的Framebuffer中间层机制,应用开发人员可以直接将缓冲区的数据写入到硬件中。
c_bitmap和c_word类实现了bmp图的绘制(仍旧将数据写入内存缓冲区)和文字的绘制; 大致的脉络就是如此,核心的消息传送和图形绘制,看懂了这几个类,就没有太大的问题了。
2 消息机制
GUILite的窗口(c_wnd)实例,在运行的时候,是全部联系在一起的。这是构建好的一个大链表,所有的消息(包括鼠标点击、键盘消息等)都可在此链表中溯源,找到其从属的窗口。
在c_wnd类中,包含以下成员变量:
c_wnd* m_parent;
c_wnd* m_top_child;
c_wnd* m_prev_sibling;
c_wnd* m_next_sibling;
分别为其父窗口、第一个子窗口、前兄弟窗口和后兄弟窗口,链表就是依赖这些类指针实现的。
与消息机制相关的另一关键数据结构为WND_TREE,其定义如下:
typedef struct struct_wnd_tree{
c_wnd* p_wnd;
unsigned int resource_id;
const char* str;
short x;
short y;
short width;
short height;
struct struct_wnd_tree* p_child_tree;
}WND_TREE;
它相当于窗口的资源文件,包含了窗口实例的指针、资源ID、字符串等信息,窗口链表的构建就是基于用户提供的此信息来实现的,核心的实现函数为c_wnd::connect()。
整个机制建立的步骤如下:
1) 构建WND_TREE型的数组,比如:
需要注意的是,数组最后一项必须为空,程序在扫描过程中,是以此作为结束判断的。
2) 使用connect()函数将所有窗口连接起来。比如:
s_my_ui.connect(NULL, ID_ROOT, 0, 0, 0, UI_WIDTH, UI_HEIGHT, s_main_widgets); (摘自HelloWidget的Uicode.cpp line166)。
此函数执行后,以s_my_ui为父窗口,上述所有的窗口(s_edit1、s_edit2等)均通过自身的成员变量m_parent、m_top_child、m_prev_sibling、m_next_sibling连接起来了。
3) 消息处理。
GUILite中准备了消息处理的回调机制,主要通过下面的宏和处理函数find_msg_entry()(c_cmd_target类的成员函数)来实现的:
数据结构GL_MSG_ENTRY中包含了消息ID和对应的回调函数。在c_wnd的成员函数notify_parent中调用find_msg_entry,根据消息ID调用回调函数。
建立了上述窗口链表和消息机制后,所有按键和手势消息(比如鼠标、触摸板等)均可以在链表中回溯,直到找到从属的窗口,调用相应的处理函数。
3 程序编写要点
GUILite目前还在维护中,所以随时有可能更新。不过,大部分的机制已经建立起来了,后续的应用代码编写流程都差不多。
一般可遵循以下步骤编写:
1) 构建自己的窗口类和显示需要的窗口元素,包括各种控件,通过connect函数将这些窗口联系起来;
2) 在编写的窗口类中实现消息处理函数,特别是对鼠标、键盘消息的处理;
3) 编写平台相关的处理函数。包括与平台的鼠标消息、键盘消息对接,以及图形的绘制等。以MFC的鼠标左击消息为例,它是建立了WM_LBUTTONDOWN消息的处理函数OnLButtonDown,应该在此函数中建立与GuiLite窗口类的on_touch的对应关系。
其余的细节不一一阐述了。学习到这儿,我觉得已经可以着手进行UEFI的移植工作了,下一篇先建立起支持C++的UEFI程序框架,完成最初的想法。
( 学习过程中问了不少问题,在此感谢作者的耐心指点^^ 开源代码地址:
https://gitee.com/idea4good/GuiLite/blob/master/documents/HowToWork-cn.md和https://github.com/idea4good/GuiLite)
4,110 total views, 1 views today
学习了,写的真好,网盘链接没有这章的code
还没整理好,有些代码我觉得很丑,准备找时间稍微调整下
感谢博主,希望博主能够做一个关于UEFI的开源项目。