本期教程开始讲解STemWin的核心内容窗口管理器。通过这几期教程,大家应该明白最基本的窗口管理器知识。了解了这些基本的通信机制后,再学习控件的使用时,就会事半功倍。本期教程的内容主要来自官方手册,我这里只是将其总结一下,更详细的情况看英文版的手册。

使用STemWin窗口管理器(Window Manager)时,显示器上出现的任何内容都包含在窗口中– 屏幕上的一个矩形区域。窗口可以为任何尺寸,并且可在屏幕上一次显示多个窗口,甚至部分或整个窗口在其他窗口的前面也可。

窗口管理器提供一组例程,利用这些例程可以很容易地对窗口进行创建、移动、调整大小,另外还能操控任意数量的窗口。它还通过管理显示器中窗口的分层来提供更低级别的支持,并通过提醒应用程序来显示影响其窗口的更改。

术语说明

窗口是矩形的,由其原点(左上角的X和Y坐标)以及X和Y值(分别为宽度和高度)定义。STemWin中的窗口:特性如下:

  • 是矩形的。
  • 具有Z位置。
  • 可以隐藏或显示。
  • 可具有有效和/或无效区域。
  • 可以透明或不透明。
  • 可以具有或不具有回调例程。

下面讲的这些基础概念一定要懂!!

活动窗口

当前正用于绘制操作的窗口称为活动窗口,不一定就是最上面的窗口。

回调函数

回调函数由用户程序定义,指示在特定事件出现时图形系统调用特定的函数。它们通常用于在窗口内容更改时自动重绘窗口。

子窗口/父窗口, 同属窗口

子窗口是相对于其他窗口(称为父窗口)定义的。只要父窗口移动,其子窗口就会相应移动。子窗口始终完全包含在其父窗口中,并在必要时会被裁剪。具有相同父窗口的多个子窗口被视为 “同属”窗口。

客户区

窗口的客户区就是其可用区域。如果窗口包含边框或标题栏,则客户区是内部的矩形区域。如果没有这种边框,则客户区的坐标与窗口自身的坐标相同。

裁剪, 裁剪区域

裁剪是将输出限制为一个窗口或窗口一部分的过程。

窗口的裁剪区域是其可见区域。它是窗口区域减去被更高Z轴阶层的同属窗口遮挡的区域,然后减去没有放入父窗口可见区域的任何部分。

坐标

坐标通常是2维坐标,以像素单位表示。坐标由2个值组成。第一个值指定水平分量 (也称为x坐标),第二个值指定垂直分量 (也称为y坐标)。

桌面坐标

桌面坐标是桌面窗口的坐标。显示器的左上角位置 (原点)为(0,0)。

桌面窗口

桌面窗口由窗口管理器自动创建,并且始终覆盖整个显示区域。它始终是最底层的窗口,在没有定义其他窗口时,它是默认 (活动)窗口。所有窗口都是桌面窗口的后代窗口 (子窗口、孙窗口等)。

句柄

创建新窗口后,WM会分配一个称为句柄的唯一标识符。句柄用于在该特定窗口中执行的其他任何操作。

隐藏/显示窗口

隐藏的窗口不可见,尽管仍然存在 (有一个句柄)。创建窗口时,如果不指定创建标记,默认情况下是隐藏的。显示窗口使其可见,隐藏窗口则使其不可见。

父坐标

父坐标是与父窗口相关的窗口坐标。窗口的左上角位置 (原点)为(0,0)。

透明性

具有透明区域的窗口包含不与窗口其余部分一起重绘的区域。这些区域就象其背后窗口“透过”它们显示一样。在此情况下,在有透明区域的窗口之前重绘背后窗口非常重要。WM自动按正确的顺序进行重绘。

有效化/无效化

有效窗口是不需要重绘的完全更新窗口。

无效窗口不会反映所有更新,因此需要完全或部分重绘。作出的更改影响了特定窗口时,WM将该窗口标记为无效。下次窗口重绘时 (手动或通过回调例程),将进行验证。

窗口坐标

窗口坐标是窗口的坐标。窗口的左上角位置 (原点)为(0,0)。

Z位置, 底部/顶部

尽管窗口显示在以X和Y表示的二维屏幕上,但是WM也管理所谓的Z位置或深度坐标– 虚拟的第三维上的位置,该坐标确定从背景到前景的位置。各窗口因此可在其他窗口之上或之下出现。将某窗口设置为底部,会将该窗口置于其所有同属窗口(如果有)的“底部”;设置为顶部,则将其置于其同属窗口的 “顶部”。创建窗口时,如果不指定创建标记,默认情况下设置为顶部。

回调机制,无效化和渲染

WM可在有或无回调例程时使用。大多数情况下,最好使用回调。

STemWin为窗口和窗口对象 (小工具)提供回调机制的根本概念是一个事件驱动系统。因为在大多数窗口式系统中,其原理是控制流不仅仅是从用户程序到图形系统,而且还从用户程序到图形系统,然后再通过用户程序提供的回调例程返回用户程序。此机制通常称为 “好莱坞原则”(“不要打电话给我们,我们会给你打电话的!”),窗口管理器需要它的主要目的是触发窗口重绘。这与传统编程相反,但是它能利用窗口管理器的无效化逻辑。

不使用回调函数

回调例程不是必须使用的,但是如果这样做,WM会失去管理窗口重绘(更新)的能力。也可能混合使用,例如让有些窗口使用回调,有些不使用。当然,如果窗口不使用回调机制,则由应用程序负责更新其内容。

使用回调函数

要创建带回调的窗口,必须有一个回调例程。创建窗口(cb参数)时该例程用作 WM_CreateWindow()函数的一部分。

回调例程必须具有以下原型:

原型

1
void callback(WM_MESSAGE * pMsg);
参数 描述
pMsg 指向类型WM_MESSAGE的数据结构指针。

回调函数执行的操作取决于其收到的消息类型。上述原型通常后跟一个switch语句,它使用一个或多个case语句为不同消息定义不同的行为 (通常至少是WM_PAINT)。

处理WM_PAINT消息

窗口收到WM_PAINT消息时,应重绘自身。将此消息发送到窗口前,WM确保它已被选定。

非透明窗口 (默认!)必须重绘其整个无效区域。

最简单的方式是重新着色窗口的整个区域。WM的裁剪机制确保了仅重绘无效区域。为了加速绘制过程,仅重绘无效区域非常有用。本章稍后描述了如何获得无效区域 (信息是消息的一部分)。另一方面,透明窗口不必重绘整个无效区域;它可让窗口区域部分不受影响。此不受影响的区域然后会变成透明。WM发送WM_PAINT消息到透明窗口之前,以下区域已经重绘 (通过发送一条WM_PAINT消息到下

面窗口)。

警示:处理WM_PAINT时,不得执行某些操作

处理WM_PAINT消息时,回调例程除了重绘窗口的内容外,不得执行其它操作。处理WM_PAINT事件时,下列函数不能调用:WM_SelectWindow()、WM_Paint()、WM_DeleteWindow() 和WM_CreateWindow()。更改窗口属性的其他任何函数也不能调用:WM_Move()、WM_Resize()等。

示例

创建一个自动重绘窗口的回调例程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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不会自动重绘背景窗口的区域,因为没有默认的背景颜色。也就是说如果创建了另一个窗口然后将其删除,则删除的窗口仍然可见。需要指定例程WM_SetDesktopColor()以便为重绘背景窗口设置颜色。也可设置回调函数来处理此问题。如果如前所述创建然后删除了一个窗口,则回调例程将触发WM来确认背景窗口不再有效,并自动重绘它。后面会专门的做个例子给大家讲解这个问题。

无效化

无效化窗口或窗口的一部分会告诉WM该窗口的无效区域在下次调用GUI_Exec()或GUI_Delay()时应重绘。emWin的无效化例程不会重绘窗口的无效部分,只管理窗口的无效区域。

窗口的无效区域

对于每个窗口,WM只使用一个矩形来存储包含整个无效区域的最小矩形。例如,如果左上角的一小部分和右下角的一小部分变为无效,则整个窗口即无效。

使用无效化的原因

使用窗口无效化而非立即重绘每个窗口的优点是只绘制窗口一次,即使其被无效化多次。例如,如果窗口的多个属性需要更改(如背景颜色、字体和窗口大小),与所有属性都更改后重绘一次窗口相比,每个属性更改后立即重绘窗口需要更多的次数。

重绘无效窗口

函数GUI_Exec()重绘所有无效窗口。这通过向每个无效窗口发送一条或多条WM_PAINT消息完成。

大家不要小看上面讲的这几个概念,非常重要

渲染透明窗口

如果需要绘制透明窗口,则WM会自动确保在透明窗口收到WM_PAINT消息前绘制窗口的背景。方法为,在向透明窗口发送WM_PAINT消息前,首先重绘透明窗口无效区域下面的所有窗口区域。为确保窗口管理器能执行透明窗口的重绘,有必要响应WM_PAINT消息并重绘窗口。否则,不能保证透明窗口的外观是正确的。使用透明窗口比使用不透明窗口需要更多消耗CPU。如果需要考虑性能问题,尝试避免使用透明窗口或许是一个选择。

自动使用存储设备

窗口管理器的默认特性是向每个需要重绘的窗口发送一条WM_PAINT。这会导致闪变效应。为抑制这些每个窗口的闪变效应,可为重绘操作自动使用存储设备。方法为,在创建窗口时设置标记WM_CF_MEMDEV、使用函数WM_SetCreateFlags()设置默认创建标记,或使用函数WM_EnableMemdev()。WM然后会将WM_PAINT消息输出重定向到存储设备中,再复制到显示器中。如果整个窗口的内存不够,会自动使用分段。存储设备只是临时使用,在绘制操作完成后会移除。

自动使用多缓冲帧

WM可自动使用多帧缓冲 (如果可用),这可通过函数WM_MULTIBUF_Enable()实现。启用后,在绘制无效窗口前,窗口管理器会将所有绘制函数的输出重定向到不可见的后置缓冲。绘制最后一个无效窗口后,WM使后置缓冲可见。请注意,仅在显示驱动支持多缓冲,并且有至少够2帧缓冲使用的足够RAM时,该功能才可用。

自动使用显示驱动缓存

WM自动使用显示驱动缓存 (如果可用)。如果可用,它会在开始绘制无效窗口前锁定缓冲。最后一个窗口绘制完成后,WM解锁缓存。

总结

本期教程就跟大家讲这么多,有兴趣的可以在网上查找下回调相关的机制进行了解。下期教程我们会通过消息机制的实例来进一步的了解窗口管理器。