教程不断更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429
本期为大家讲解emWin的窗口管理器。窗口管理是emWin比较核心的内容,但是官方手册中讲解的资料很少,而且emWin只有库,没有源码,导致研究比较困难,只能从应用上来认识。不过笔者想说的是,尽管只有这些知识点,也已经够用了。
本章节内容整理emWin官方手册。
41.1 初学者重要提示
41.2 什么是窗口管理器
41.3 窗口管理器术语说明(重要)
41.4 窗口管理器的回调机制,无效化,渲染和键盘输入
41.5 总结
1、 对于初学者来说,本章的知识点非常非常重要,当前可以不理解,但是必须要知道,随着以后章节的学习来逐渐强化对这几个知识点的认识。
2、 窗口管理器相关知识在emWin手册中都有讲解,下图是中文版讲解位置
下图是英文版手册讲解位置:
窗口就是屏幕上的一个个矩形区域,窗口管理器是这些矩形区域的管理者,它控制窗口的外表,位置和提供用户去操作这些窗口的方法。显示器上出现的任何内容都包含在窗口中,窗口可以为任何尺寸,并且可在屏幕上一次显示多个窗口,甚至部分或整个窗口显示在其他窗口的前面也可以。
窗口管理器提供一批API函数,利用这些函数可以很容易地对窗口进行创建、移动、调整大小等操作。emWin提供的窗口管理器API函数相对还是比较全面的。
下面这些术语比较重要,初学时可以不理解,但是务必要知道,随着以后的学习慢慢加深理解。
窗口是矩形的,由其原点(左上角的坐标)以及宽度和高度定义。emWin的窗口特性如下:
活动窗口:
当前正用于绘制操作的窗口称为活动窗口,不一定就是最上面的窗口。
回调函数:
回调函数由用户程序定义,在特定事件出现时指示图形系统调用特定的函数。它们通常用于窗口内容更改时自动重绘窗口。
子窗口/父窗口:
子窗口是相对于父窗口定义的。只要父窗口移动,其子窗口就会相应移动。子窗口始终完全包含在其父窗口中,并在必要时被裁剪。具有相同父窗口的多个子窗口被视为“同属”窗口。
客户区:
窗口的客户区就是其可用区域。如果窗口包含边框或标题栏,则客户区是内部的矩形区域。如果没有这种边框,则客户区的坐标与窗口自身的坐标相同。
裁剪, 裁剪区域
裁剪是将输出限制为一个窗口或窗口一部分的过程。
窗口的裁剪区域是其可见区域,它是窗口区域减去被更高Z轴阶层的同属窗口遮挡的区域,然后减去没有放入父窗口可见区域的任何部分。
桌面窗口:
桌面窗口由窗口管理器自动创建,并且始终覆盖整个显示区域。它始终是最底层的窗口,在没有定义其他窗口时,它是默认(活动)窗口。所有窗口都是桌面窗口的后代窗口(子窗口、孙窗口等)。
特别注意,系统初始化STemWin后,桌面窗口默认就是创建好的,之后所有的操作都是在桌面窗口上进行的。
坐标:
坐标通常是二维坐标,以像素单位表示。坐标由2个值组成。第一个值指定水平分量,也称为x坐标,第二个值指定垂直分量,也称为y坐标。
桌面坐标:
桌面坐标是桌面窗口的坐标,显示器的左上角原点位置为(0,0)。
句柄:
创建新窗口后,窗口管理器会分配一个称为句柄的唯一标识符。通过这个句柄就可以方便的对此窗口进行操作。
隐藏/显示窗口:
隐藏的窗口不可见,尽管还存在。创建窗口时,如果不设置立即显示标志,默认情况下是隐藏的。显示窗口使其可见,隐藏窗口则使其不可见。
父坐标:
父坐标是与父窗口相关的窗口坐标。窗口的左上角位置(原点)为(0,0)。
透明性:
具有透明区域的窗口包含不与窗口其余部分一起重绘的区域。这些区域就像其背后窗口“透过”它们显示一样。这种情况下,在有透明区域的窗口之前重绘背后窗口非常重要。窗口管理器自动按正确的顺序进行重绘。
有效化/无效化:
有效窗口是不需要重绘的完全更新窗口。
无效窗口不会反映所有更新,因此需要完全或部分重绘。作出的更改影响了特定窗口时,窗口管理器将该窗口标记为无效,下次窗口重绘时(手动或通过回调函数),将进行验证。
这个功能比较有用,后面章节中会用到。
窗口坐标:
窗口坐标是窗口显示区的坐标,窗口的左上角位置(原点)为(0,0)。
Z位置, 底部/顶部:
尽管窗口显示在以X和Y表示的二维屏幕上,但是窗口管理器也管理所谓的Z位置或深度坐标-- 虚拟的第三维上的位置,该坐标确定从背景到前景的位置。各窗口因此可在其他窗口之上或之下出现。将某窗口设置为底部,会将该窗口置于其所有同属窗口(如果有的话)的底部;设置为顶部,则将其置于其同属窗口的顶部。创建窗口时,如果不指定创建标记,默认情况下设置为顶部。
窗口管理器可以在有回调函数的例程中使用,也可以在无回调函数的例程中使用。不过还是建议使用回调函数。
emWin为窗口和窗口对象(小工具或者说控件)提供回调机制的根本概念是一个事件驱动系统。因为在大多数窗口式系统中,其控制方向不仅仅是从用户程序到图形系统,而且还可以从用户程序到图形系统后,再通过用户程序提供的回调例程返回用户程序。此机制通常称为 “好莱坞原则”(“不要打电话给我们,我们会给你打电话的!”),窗口管理器需要回调函数的主要目的是触发窗口重绘。这与传统编程相反,但是它能利用窗口管理器的无效化功能。
回调函数不是必须使用的,但是如果这样做,窗口管理器会失去管理窗口重绘(更新)的能力。也可以混合使用,例如让有些窗口使用回调,有些不使用。当然,如果窗口不使用回调机制,则由用户应用程序负责更新其内容。
要创建带回调的窗口,必须有一个回调函数。比如使用函数WM_CreateWindow()创建窗口时就可以通过其形参WM_CALLBACK * cb来指定回调函数。回调函数必须具有以下原型:
void callback(WM_MESSAGE * pMsg);
回调函数执行的操作取决于其收到的消息类型。回调函数中会有一个switch语句,它使用一个或多个case语句为不同消息定义不同的行为,其中重绘消息WM_PAINT是一个比较重要的消息。窗口收到WM_PAINT消息时,应重绘自身,将此消息发送到窗口前,窗口管理器要确保此窗口已被选定。处理WM_PAINT消息要分两种情况进行说明:
最简单的方式是重新绘制窗口的整个区域。窗口管理器的裁剪机制确保了仅重绘无效区域。为了加速绘制过程,仅重绘无效区域非常有用。本章稍后描述了如何获得无效区域 (信息是消息的一部分)。
透明窗口不必重绘整个无效区域,它可让窗口部分区域不受影响,此不受影响的区域会变成透明。窗口管理器发送WM_PAINT消息到透明窗口之前,位于透明窗口下面的区域已经重绘 (通过发送一条WM_PAINT消息到下面窗口)。
注意,处理WM_PAINT消息时,不得在此消息里面执行以下操作:
处理WM_PAINT消息时,下列函数不能调用:WM_SelectWindow()、WM_Paint()、WM_DeleteWindow() 和WM_CreateWindow()。更改窗口属性的其他任何函数也不能调用:WM_Move()、WM_Resize()等。
创建一个自动重绘窗口的回调函数实例(初学者在这里有个感性认识即可,后面章节详细讲解):
void WinHandler(WM_MESSAGE * pMsg) {
switch (pMsg->MsgId) {
case WM_PAINT:
GUI_SetBkColor(0xFF00);
GUI_Clear();
GUI_DispStringAt("Hello world",0,0);
break;
default:
WM_DefaultProc(pMsg);
}
}
注意,WM_PRE_PAINT和WM_POST_PAINT消息用于在WM_PAINT消息发送前和发送后处理。
初始化窗口管理器期间,会创建一个包含整个LCD 区域的窗口作为桌面窗口。此窗口的句柄为
WM_HBKWIN。窗口管理器不会自动重绘桌面窗口的区域,因为没有设置自动重绘,也就是说如果创建了另一个窗口然后将其删除,则删除的窗口仍然可见。
桌面窗口实现重绘有两种方法
关于这两个函数的使用,在后面章节中都会给大家进行演示。
无效化窗口或窗口的一部分会告诉窗口管理器该窗口的无效区域在下次调用WM_Exec,GUI_Exec()或GUI_Delay()时应重绘。STemWin的无效化函数不会重绘窗口的无效部分,只是管理窗口的无效区域。实际重绘工作是由WM_Exec,GUI_Exec()或GUI_Delay()来完成。
窗口的无效区域
对于每个窗口,窗口管理器只使用一个矩形来获取包含所有无效区域的最小矩形。例如,如果左上角的一小部分和右下角的一小部分变为无效,则整个窗口都是无效区。
使用无效化的原因
使用窗口无效化而非立即重绘每个窗口的优点是只需绘制窗口一次,即使其被无效化多次。例如,如果窗口的多个属性需要更改,如背景颜色、字体,窗口大小等,每个属性更改后就得重绘一次窗口,而使用无效化,可以让所有属性都更改后仅重绘一次即可。
重绘无效窗口
通过函数WM_Exec,GUI_Exec()或GUI_Delay()可以重绘所有无效窗口。这通过向每个无效窗口发送一条或多条WM_PAINT消息完成。
如果需要绘制透明窗口,则窗口管理器会自动确保在透明窗口收到WM_PAINT消息前绘制窗口的背景。其方法是在向透明窗口发送WM_PAINT消息前,首先重绘透明窗口无效区域下面的所有窗口区域。然后通过响应WM_PAINT消息来执行透明窗口的重绘。否则,不能保证透明窗口的外观是正确的。另外,使用透明窗口比使用不透明窗口需要更多消耗CPU。如果需要考虑性能问题,尝试避免使用透明窗口。
窗口管理器的默认特性是向每个需要重绘的窗口发送一条WM_PAINT消息,但这会导致窗口闪烁。为抑制每个窗口的闪烁,可使能重绘操作自动使用存储设备。
有三种方法:
通过这三种方法的任意一种,窗口管理器会将WM_PAINT消息输出重定向到存储设备中,再复制到显示器中。这样就有效避免了窗口闪烁。如果整个窗口的内存不够,会自动使用分段,存储设备只是临时使用,在绘制操作完成后会移除。
窗口管理器可自动使用多帧缓冲(如果可用),这可通过函数WM_MULTIBUF_Enable()来使能。启用后,在绘制无效窗口前,窗口管理器会将所有绘制函数的输出重定向到不可见的后置缓冲,绘制最后一个无效窗口后,窗口管理器使后置缓冲可见。请注意,仅在显示驱动支持多缓冲,并且至少有足够2帧缓冲使用的RAM时,该功能才可用。
当前,本教程所有配套的例子都是采用的三缓冲,没有再使用存储设备来避免闪烁,实际测试发现,STM32H7+SDRAM的形式,使用三缓冲性能更强,比使用存储要流畅很多。
这里称作键盘输入是将官方手册的英文Keyboard Input直译过来的,实际上不限制是键盘,任何其它输入设备都是可以的。
窗口管理器可以自动管理键盘输入,窗口管理器可以查询键盘缓冲并发送消息到当前聚焦的窗口,而键盘消息的存入是通过函数GUI_StoreKeyMsg()来实现的。关于键盘输入这块,后章节将专门进行讲解。
本期教程就跟大家讲这么多,望初学者务必掌握这些基础的知识点,还是非常重要的。当前可以不理解,但是一定要知道,方便以后的深入学习和理解。