14.2. 窗口管理器的回调机制、无效化和渲染¶
emWin为窗口和控件对象提供了回调机制,其背后的思想是试图构建一个事件驱动系统。与大多数窗口界面系统一样,
事件驱动的原则是控制流不仅从用户程序流向图形系统,而且也从用户程序流向图形系统,并通过用户提供的回调函数返回到用户应用程序。
窗口管理器使用这种机制,主要是为了触发窗口的绘制与重绘。虽然这与经典编程不同,但它能很好的利用emWin窗口管理器的无效化逻辑。
14.2.1. 使用回调函数渲染窗口¶
想要创建具有回调的窗口,必须定义一个回调函数。当使用WM_CreateWindow()函数创建窗口时,这个回调函数将作为参数。
所有回调函数都必须具有 代码清单:窗口管理-1 所示的原型:
代码清单:窗口管理-1 回调函数原型¶
1void Callback(WM_MESSAGE * pMsg);
1) pMsg:
指向WM_MESSAGE类型结构体的指针。
回调函数中执行的具体操作取决于它接收的消息类型。窗口回调函数中需要构建一个switch语句,它使用一个或多个case语句为不同的消息定义不同的行为。
通常至少需要存在一个WM_PAINT消息。
窗口通过WM_PAINT消息重绘自身,不过在将此消息发送到窗口之前,窗口管理器需要确保需要重绘的窗口已被选中。普通窗口收到WM_PAINT消息后,
默认情况下会重绘自身整个区域。为了加快绘制过程,窗口管理器的裁剪机制会确保只重绘窗口的无效区域。
注意,在WM_PAINT消息中,除了重绘窗口内容外不得执行其他操作。不得在WM_PAINT消息中调用以下函数:WM_SelectWindow()、WM_PAINT()、
WM_DeleteWindow()和WM_CreateWindow()。此外,类似WM_Move()和WM_Resize()这类改变窗口属性的函数也都不得在WM_PAINT消息中调用。
14.2.2. 桌面窗口重绘和回调¶
在窗口管理器初始化期间,会创建一个包含整个LCD区域的窗口作为桌面窗口。这个窗口的句柄是WM_HBKWIN。由于没有默认的背景颜色,
窗口管理器并不会自动重绘桌面窗口区域。这就意味着如果你创建了一个新的窗口然后删除它,这个窗口仍然可见不会消失。
解决办法是通过WM_SetDesktopColor()函数设置用于重绘桌面窗口的颜色。或者可以设置一个回调函数来处理这个问题。如果像上面一样创建并删除一个窗口,
回调函数将触发窗口管理器确认桌面窗口不再有效并自动重绘。
14.2.3. 窗口无效化¶
无效窗口或窗口的一部分失效区域会告诉窗口管理器,在下一次调用重绘函数时重绘窗口的无效区域。emWin提供的无效化函数不负责重绘窗口的无效部分,
它们只负责管理窗口的无效区域。真正负责重绘的是GUI_Exec()或GUI_Delay()等函数。
14.2.4. 裁剪机制¶
在上面所讲的内容中,窗口的绘制或重绘通常是通过发送一次WM_PAINT消息来完成的。但是,如果一个窗口的部分区域被子窗口或任何其他窗口覆盖,
那么被覆盖的窗口将会重复接收到若干次的WM_PAINT消息。窗口管理器将窗口未被覆盖的区域裁剪成若干子矩形。在此过程中,窗口管理器将每个子矩形都设置为裁剪区域,
并将所有子矩形的WM_PAINT消息都发送给被覆盖的窗口。窗口被覆盖的区域越碎片,存在的矩形就越多,发送的消息就越多。因此,
在处理WM_PAINT消息的程序中不应该执行费时的操作。
图 裁剪机制演示 显示了窗口管理器如何通过裁剪机制绘制被框架窗口控件覆盖的桌面窗口。
框架窗口控件由主窗口和客户区组成。主窗口是一个透明窗口,它绘制在桌面窗口的顶部,对裁剪算法没有影响,
而客户区是不透明的,会导致桌面窗口被裁剪。窗口管理器会从上到下和从左到右的围绕控件客户区将桌面窗口裁剪成若干个区块。
裁剪算法裁剪出的区块数量随窗口被覆盖区域的数量而增加。
图 多窗口的裁剪机制演示 显示了附加一个小窗口时的裁剪机制。在开始绘制这个不透明小窗口的过程时,
窗口管理器首先向窗口发送WM_PRE_PAINT消息。之后,窗口周围所有的被裁剪区块都向被裁剪窗口发送一条WM_PAINT消息。
当绘制完最后一个区块后,窗口管理器会发送WM_POST_PAINT消息。
不透明窗口覆盖的区域越多,绘制窗口所需的区块就越多。
14.2.5. 渲染透明窗口¶
如果需要绘制或重绘透明窗口,窗口管理器将自动确保在透明窗口收到 WM_PAINT 消息之前绘制窗口的背景。实现方法是,
在向透明窗口发送 WM_PAINT 消息前,先重绘透明窗口无效区域下的所有窗口区域,然后再向透明窗口发送WM_PAINT消息。
透明窗口的重绘操作必须在WM_PAINT消息内进行,否则可能无法正确绘制外观。透明窗口比普通不透明窗口更耗费CPU。
如果性能有问题,请尝试避免使用透明窗口。
14.2.6. 自动使用内存设备¶
窗口管理器的默认行为是向每个需要重绘的窗口发送WM_PAINT消息,这可能导致闪烁现象。使用内存设备可以在绘图操作时自动抑制每个窗口的闪烁现象。
通过在创建窗口时设置WM_CF_MEMDEV标志,或使用WM_SetCreateFlags()函数设置默认创建标志即可使用内存设备进行重绘操作,
或使用函数WM_EnableMemdev()为指定窗口开启内存设备。此时,窗口管理器将WM_PAINT消息的输出位置重定向到内存设备中,
重绘完成后把内存设备中的内容复制到屏幕上。如果没有足够的内存用于整个窗口的重绘,则会自动使用分段内存设备。
以上这些内存设备都是在窗口管理器发送WM_PAINT消息之前在内部创建的,并在重绘完成后立即删除。如果使用内存设备重绘透明窗口,
那么窗口无效区域下方的内容也会放到内存设备中进行重绘。