关于操作系统和上层软件的关系,某些操作系统被称为内核空间(kernel space),而操作系统以上的部分被称为用户空间(user space)。按照功能,操作系统以上的部分可以分为中间件和上层应用两个部分。中间件一般提供了一些相对底层的软件层次的功能。它的实现一般不包括应用程序的逻辑,而是向上层软件提供了各种方便的应用程序接口(API)。
GUI系统的移植实现基础包含输出设备和输入设备两个方面
在学习一个嵌入式GUI特性和功能的时候,需要关注可移植性、稳定性和可靠性、系统开销、可配置性等几个方面的内容。
作为操作系统和应用程序之间的中间件,MiniGUI将底层操作系统及硬件平台差别隐藏了起来,并对上层应用程序提供了一致的功能特性,这些功能特性主要包括:
1) 支持不同的硬件开发平台
2) 跨操作系统支持
3) 多运行模式支持。为了适应不同的操作系统运行环境,MiniGUI可配置成三种运行模式:MiniGUI-Threads(线程模式)、MiniGUI-Processes(进程模式)及MiniGUI-Standalone(独立模式)
4) 内建资源支持。可以将MiniGUI所使用的资源,诸如位图、图标和字体等编译到函数库中。
5) 完备的多窗口机制和消息传递机制
6) 提供常用的控件类。
7) 对话框和消息框支持
8) 其他GUI元素
9) 界面皮肤支持
10) 支持低端显示设备(比如单色LCD)和高端显示设备(8位色及以上显示设备)。通过MiniGUI的图形抽象层及图形引擎技术,还可以支持特殊的显示设备,比如YUV显示设备。
11) 提供增强GDI函数。
12) Windows的资源文件支持
13) 各种流行图像文件支持
14) 多字符集和多字体支持
15) 多种键盘布局的支持
16) 针对嵌入式系统的特殊支持,包括一般性的I/O流操作,字节序相关函数等
17) 副屏支持。
硬件适配性:可运行于各种含有MMU(内存管理单元)的32位处理器架构之上。
MiniGUI的集成开发环境mStudio
下层移植层、中间的核心实现、上层API
所有顶层的图形操作和输入处理都建立在抽象接口之上,而用于实现这一抽象接口的底层代码称为“图形引擎”或“输入引擎”。
MiniGUI核心部分的组成模块:
1. 图形抽象层(GAL)(MiniGUI的移植层)
图形抽象层将来自不同操作系统或设备的图形接口进行抽象,为MiniGUI上层提供统一的图形接口。具体通过调用底层设备的接口来实现具体的图形抽象层操作(ep:打开设备、设置分辨率及显示模式、关闭设备等),将这些拥有适配图形抽象层接口的软件组成部分称为“引擎(engine)”,其概念和操作系统中的设备驱动程序类似。
2. 输入抽象层(IAL)(MiniGUI的移植层)
要支持不同的键盘、触摸屏或者鼠标接口,则通过为IAL编写不同的输入引擎实现。
3. 图形设备接口(GDI)(MiniGUI的核心实现)
该模块基于图形抽象层为上层应用程序提供图形相关的接口,如绘制曲线、输出文本、填充矩形等。
4. 消息处理模块(messaging module) (MiniGUI的核心实现)
该模块在输入抽象层基础上,实现了MiniGUI的消息处理机制,为上层提供了完备的消息管理接口。几乎所有的GUI系统本质上都是事件驱动的,系统自身的运行,以及GUI应用程序的运行,都依赖于消息处理模块。
5. 多窗口处理模块(windowing module)和控件(control或widget)(MiniGUI的核心实现)
基于图形设备接口和消息处理模块,MiniGUI实现了多窗口处理模块。该模块为上层应用程序提供了创建主窗口和控件的基本接口,并负责维护控件类。控件类是用来实现控件代码重用的重要概念,利用控件类(control class),可以创建属于某个控件类的多个控件实例(instance),从而可以最大程度上重复利用已有代码,并提高软件的可维护性。MiniGUI的控件模块实现了常见的GUI控件,如静态框、按钮、编辑框、列表框、下拉框等。
6. 外观支持(look and feel)
在MiniGUI V3.0之前的版本中,对主窗口和控件的定制能力,还没有被抽象出来形成独立的模块,但仍然可以通过配置选项让MiniGUI的主窗口、控件具有三种显示风格(类似PC的三维风格(PC3D)、平板风格(FLAT)、流行风格(FASHION))。
MiniGUI-Threads是基于线程(或者RTOS中的任务(task))运行的。
线程通常被定义为一个进程中代码的不同执行路线。
MiniGUI-Threads利用线程来支持多窗口。从本质上讲,每个线程有一个消息队列,消息队列是实现线程数据交换和同步的关键数据接口。一个线程向消息队列中发送消息,而另一个线程从这个消息队列中获取消息,同一个线程中创建的窗口可共享同一个消息队列。
what is POSIX Thread?
GUI系统在运行期间,需要保持一个或者若干个循环来监视系统的输入,根据输入驱动系统的运行。
GUI系统通常是一个事件驱动的系统。消息机制是系统处理事件的核心,各种不同的消息可以视为系统对不同事件的抽象描述。
在GUI系统中,显示的内容和执行的逻辑通常是分离的两个方面。消息实质上就是联系二者的纽带。
MiniGUI为每一个MiniGUI程序维护一个消息队列之中。在发生时间之后,MiniGUI将事件转换为一个消息,并将消息放入目标程序的消息队列中。
13431818355
GetMessage()函数调用从应用程序的消息队列中取出一个消息。
TranslateMessage()函数把击键消息转换为MSG_CHAR消息,然后直接发送到窗口过程函数。
DispatchMessage()函数最终将把消息发往该消息的目标窗口的窗口过程,让它进行处理。
消息触发的条件
消息中的两个参数(wParam和lParam)的使用
自定义消息的使用
在主窗口中使用附加信息表示私有数据
一个应用程序窗口一般包括如下部分:
一个可视的边界
一个窗口ID,客户程序使用该ID来操作窗口
一些其他特性:高、宽、背景色等
可能有菜单和滚动条等附加窗口元素
在MiniGUI中,窗口ID的表现形式为:窗口句柄
MiniGUI窗口系统:
在MiniGUI中,窗口共有三种类型:
主窗口(MainWindows)
对话框(dialogBox)
控件窗口(control),也称为子窗口
主窗口是每一个MiniGUI应用程序都需要创建的,它作为应用程序的主界面或开始界面。控件窗口也称为子窗口,通常在主窗口中被包含,这些子窗口通常是控件窗口,也可以是自定义窗口类。对话框本质上就是主窗口,应用程序一般通过对话框提示用户进行输入操作。
消息(message)是GUI系统中的一个通信手段。在GUI系统中,根据产生源头的不同,消息通常有两种类型,一种是系统消息,另一种是外部消息。系统消息通常是由GUI系统自己产生,然后传递给应用程序,例如窗口建立和销毁消息、焦点消息等。外部消息由外部的输入产生,同样经过GUI系统传递给应用程序,例如鼠标消息、键盘消息等。很多消息可能不需要用户程序处理的,只需要调用系统的默认处理方式即可。
消息常常和回调函数结合使用,在回调函数中根据当前的消息来对程序的逻辑进行处理。在实现方式上,回调函数是GUI应用程序实现的函数,将其指针传递给GUI系统的库之后。核心库在消息发送的情况下,调用回调函数,所需要处理的消息通常将作为参数传入。
对于minigui中的每一个主窗口,在建立的过程中,需要确定窗口的样式、大小、位置和控制窗口行为的窗口过程函数。进一步,对于在应用程序中建立的每一个窗口,可能还包含着自己的一些特性。在程序中,这些窗口特性的体现,需要通过数据结构来体现。
在minigui中,体现这一特性的编程方式是在主窗口中附加一个信息。DwAddData是MAINWINCREATE中的一个成员,用于在主窗口中增加附加的信息。
对于该数据结构的操作,可以使用如下两个函数:
MG_EXPORT DWORD GUIAPI GetWindowAdditionalData(HWND hwnd);
MG_EXPORT DWORD GUIAPI SetWindowAdditionalData(HWND hwnd, DWORD newData);
在窗口建立的时候,使用createinfo.dwAddData = 0的形式指定;在窗口建立后,如果需要更改,可以通过SetWindowAdditionalData()函数来设定。
事实上,由于dwAddData是一个DWORD(32位)类型的成员。这个成员可以表示一个指针,在指针上可以开辟任意大小的内存,从而保留附加给窗口的私有数据结构。这是在程序中达到附加数据的一种通用的方法,使用这种方式,可以避免使用全局的变量,却同样达到传递更多信息的目的。
对于一些在窗口运行的过程中使用的数据结构,可以将其设置为主窗口的私有数据,这数据结构将和这个主窗口的句柄绑定在一起。使用这种方式可以避免使用静态变量,让程序保持更好的结构。
窗口和消息(或者说事件)是图形用户界面编程中的两个重要概念。窗口是显示器屏幕上的一个矩形区域,应用程序使用窗口来显示输出信息并接受用户的输入。流行的GUI编程一般都采用事件驱动机制。
事件驱动的含义就是,程序的流程不再是只有一个入口和若干个出口的串行线路;相反,程序会一直处于一个循环状态,在这个循环当中,程序不断从外部或内部获取某些事件,比如用户的按键或者鼠标的移动,然后根据这些事件作出某种响应,并完成一定的功能,这个循环直到程序接收到某个消息为止。“事件驱动”的底层设施,就是常说的“消息队列”和“消息循环”。
窗口模型,消息处理机制。
屏幕上的重叠窗口的关系:
l 窗口一般组织为层次体系结构的形式(或者说,树的形式)。
l 根窗口(root window)是所有窗口的祖先,占满整个屏幕的表面,也称为桌面窗口。
l 除了根窗口外的所有窗口都有父窗口,每一个窗口都可能有子窗口、兄弟窗口、祖先窗口和子孙窗口等。
l 子窗口含在父窗口内,同一个父窗口内的子窗口为同级窗口。
l 重叠窗口的可见性取决于它们之间的关系,一个窗口只有当它的父窗口可见时才是可见的,子窗口可以被父窗口剪切。
l 同级窗口可以重叠,但是某个时刻只能有一个窗口输出到重叠区域。
l 框架窗口(frame window/main window)包括可用的客户区和由窗口系统管理的修饰区(也称为“非客户区”)。
l 桌面窗口的子窗口通常为框架窗口。
l 窗口有从属关系,也就是说,某些窗口的生命周期和可见性由它的所有者决定。父窗口通常拥有它们的子窗口。
MiniGUI中有三种窗口类型:主窗口、对话框和控件窗口(子窗口)。每一个MiniGUI应用程序一般都要创建一个主窗口,作为应用程序的主界面或开始界面。主窗口通常包括一些子窗口,这些子窗口通常是控件窗口,也可以是自定义窗口类。应用程序还会创建其它类型的窗口,例如对话框和消息框。对话框本质上就是主窗口,应用程序一般通过对话框提示用户进行输入操作。消息框是用于给用户一些提示或警告的主窗口,属于内建的对话框类型。
在MiniGUI的UI设计中,对话框的实现有两种方式:
第一种方式是使用分别建立控件的方式,即使用createwindow()函数建立对话框上面的每一个UI元素。
第二种方式是使用对话框模板,即构造一个对话框模板,使用它建立了一组控件。
对话框分为模式对话框和非模式对话框两种。
模式对话框出现之后,用户将不能再切换到其他主窗口,只有在对话框关闭之后,才能操作其他窗口。
非模式对话框在出现之后,用户还可以切换到其他窗口,而不需要关闭这个对话框。在某种程度上,非模式对话框更像一个以对话框形式出现的窗口。
对话框的建立和过程处理函数的使用是一种回调函数经典的使用方式。第二个参数的传入,可以表示在这个对话框中的私有数据的信息。对于同一个对话框模板,可以通过过程处理函数及其第二个参数的不同,表示建立不同的对话框。回调函数表示对话框的行为,第二个参数表示对话框的数据。
第七章.MiniGUI的控件编程
控件的消息(message):可以用于应用程序向控件发送的命令,通常使用SendMessage()函数来完成这一工作。对于每一种消息的两个参数(wParam和lParam)表示不同含义,可以使用不同的数据结构,每种消息的返回值有各自定义。
控件的通知代码(Notification Code):用于在控件过程处理函数MSG_COMMAND消息中,根据wParam参数的高16位进行区分。很多控件都定义一种名称为XXX_NOTIFY的风格(style),只有建立控件的时候具有这种风格,运行中才为控件产生通知代码。
控件的标识符(ID):与以上的定义不同,控件的标识符是在运行时指定的,程序运行的消息处理中根据控件的ID来判断发生事件的源头,根据wParam消息的低16位进行区分。
lWaram表示子控件的句柄。
第八章、MiniGUI的菜单
MiniGUI的菜单有两种:一种是在主窗口中使用的主菜单,固定在窗口的上部,另一种是在窗口任意区域内弹出的快速菜单。
创建一个主菜单可以分成三个层次:第一个层次是一个空的菜单栏,第二个层次是一个子菜单,第三个层次是每个菜单中的若干项。
第九章、MiniGUI的键盘和鼠标
键盘的处理分为击键消息和字符消息;鼠标消息分为客户区鼠标消息和非客户区鼠标消息。
对于键盘消息,不仅可以表示PC的键盘,也可以表示嵌入式系统中的其他形状的键盘。对于鼠标消息,可以表示PC的鼠标,也可以表示嵌入式系统的触摸屏等硬件。
minigui通过键盘设备驱动程序从键盘接收原始的输入事件或数据,把它转换为minigui抽象的键盘事件和数据。相关的底层事件处理例程把这些键盘事件转换为上层的击键消息,放到相应的消息队列中。应用程序通过消息循环获取这些消息的,交由窗口过程处理。
在minigui中,与键盘相关的消息有击键消息和字符消息。前者表示的是真实的物理按键,用扫描码来表示;后者表示的是字符,用ASCII码来表示。
击键消息的wParam参数表示该键的扫描码,lParam表示参数特殊键(通常是组合使用的功能键)的状态标志,例如:shift,alt,ctrl等。
对于几个击键消息的处理,参数使用的方法如下所示:
int scancode = (int)wParam;
DWORD key_flags = (DWORD)lParam;
扫描码是物理按键原始的表现,扫描码直接对应于真实的键盘的按键。
在minigui中,使用MSG_CHAR表示字符消息。一个典型的窗口过程通常不直接处理字符键的击键消息,而是处理字符键的字符消息MSG_CHAR。
MSG_CHAR消息通常由TramslateMessage函数产生,该函数在收到击键消息MSG_KEYDOWN和MSG_SYSKEYDOWN时检查该键的扫描码和相关键的状态,如果能转换为某个字符的话,就产生相应字符的MSG_CHAR或MSG_SYSCHAR消息,直接发送击键消息的目标窗口。
字符消息的wParam参数就是代表该字符的编码值(ASCII码),lParam表示参数特殊键的状态标志,与击键消息相同。
处理击键消息和字符消息的基本规则是:如果需要读取输入到窗口的键盘字符,那么用户可以处理MSG_CHAR消息。如果需要读取游标键、功能键、Delete、Insert、Shift、Ctrl以及Alt键的,那么用户可以处理MSG_KEYDOWN消息。
对于键盘上的一个按键,它所产生的击键消息用唯一的扫描码来表示(即wParam是唯一的),但是它所能产生的字符消息可能有两个——根据CAPS键或Shift键的不同,分别表示大小写。