OpenHarmony开发者论坛
标题:
OpenHarmony之WindowsManger介绍
[打印本页]
作者:
深开鸿_王清
时间:
2023-10-30 09:47
标题:
OpenHarmony之WindowsManger介绍
[md]# OpenHarmony之WindowsManger介绍
## window\_manager是什么?
** **我们可以在OpenHarmony,IOS,Android,windows等图形界面系统中都可以看到一个名为窗口管理的框架模块,至于为什么叫window\_manager,而不是什么door\_manager,desktop\_manager,原因就是长期占据图形界面系统霸主地位的就是微软的windows,受众最广,使用的开发者最多,导致后续的系统的设计者和开发者重复造轮子的时候总会借鉴这个成熟的框架。
** **我们可以参考下微软在前几代图形操作系统中的主要特点来理解下window\_manager这个模块的地位:
** ** 1983年,微软为IBM涉及的首款windows程序:
![IBM1]
![IBM1.png](data/attachment/forum/202310/30/094529f55eoq3gf2325dlo.png "IBM1.png")
**特点:多窗口,可改变大小窗口**
** **1985年,微软发布windows首个版本windows1.0:
** **![windows1.0]![windows1.0.jpg](data/attachment/forum/202310/30/093902zsgf8wgw4gtgz0td.jpg "windows1.0.jpg")
** **特点:窗口不可重叠,但是可以平铺,窗口不可以覆盖下方图标区域。
---
** **1987年,微软发布windows2.03:
** **![windows2.0]
![windows2.0.jpg](data/attachment/forum/202310/30/093929udnezgye8dzcuebe.jpg "windows2.0.jpg")
** ** 特点:窗口可以改变大小,可以重叠,添加部分窗口控制功能。
** ** 到了windows3.0和windows3.1时代,加入了大量的图形界面,单纯的从界面上看已经和现代的windows操作系统非常接近,并成为当时兼容机的标配,代表图形操作系统进入成熟发展期。而从发展的历程上看,DOS这类非图形界面向图形界面的变迁过程中,window视窗的概念被孕育和发展,窗口的管理系统或者说框架,即window\_manager框架,也成为了现代图形操作系统中核心模块。
** ** 本章小结:
1. **window\_manager是现代图形操作系统的核心模块。**
2. **window\_manager模块迄今已有40年左右的发展史,虽然随着技术的进步,但是窗口管理的核心概念如最初的窗口大小控制,布局控制,窗口属性等一直沿用至今,了解下历史可以让我们学习该模块更好的举一反三。**
## window\_manager模块在整个系统中做什么
** ** (我们现在学习的模块都率属于OpenHarmony开源操作系统,系统及框架均以OpenHarmony为准)
** **我们想去了解一个模块,需要了解这个模块在整个系统的位置,这个模块的功能有哪些,紧密关联的模块有哪些,给其他模块提供什么样的支撑,以及自身的功能集的实现需要其他模块为自己提供怎样的支持等等等等。
** **针对以上的问题,我们一个个来探究:
```
1. window_manager模块在整个系统中的位置:
```
** **![systemPos]
![systemPos.png](data/attachment/forum/202310/30/094004rstxx3m333ntr3nj.png "systemPos.png")
** **window\_manager模块又称为窗口管理子系统,它与graphic子系统共同组成了OpenHarmony三大核心子系统中的图形子系统。
** **图形子系统:
** **graphic子系统:
** **提供了图形接口能力,提供图形的 NDK(native development kit,原生开发包)能力,包括:WebGL、Native Drawing的绘制能力、OpenGL 指令级的绘制能力支撑等。
** **窗口管理子系统:
** 提供窗口管理和Display管理的基础能力,是系统图形界面显示所需的基础子系统。**
2. **窗口管理子系统的具体功能:**
** ** 按照官网的描述,窗口管理子系统
** ** 窗口管理:窗口提供管理窗口的一些基础能力,包括对当前窗口的创建、销毁、各属性设置,以及对各窗口间的管理调度。
** **DisPlay:屏幕属性提供管理显示设备的一些基础能力,包括获取默认显示设备的信息,获取所有显示设备的信息以及监听显示设备的插拔行为。
3. **与其他模块的关系**
** **窗口管理子系统接口对开发者不可见,主要是给其他几个模块调用,如AAFWK(原能力子系统)、多媒体、相机等。
** **对于应用开发者来说,接触最多的是ability,而ability页面内容由窗口进行展示,也就是ability必然会使用该模块的接口去创建window对象。
** **对于上层UI框架,其向图形子系统申请窗口,并使用2D图像绘制引擎skia向窗口中绘入画面;而窗口附着在ability上,ability由元能力子系统AAFWK进行管理;
** **window\_manager框架本身则提供创建、销毁窗口的接口,以及窗口的显示、隐藏、切换、缩放等动作等。
** **对于下层驱动程序,图形子系统图像的最终显示由驱动决定。
## window\_manager自身的结构:
![WindowManager]
![WindowManager.png](data/attachment/forum/202310/30/094116nx9c0r1dezdrcdge.png "WindowManager.png")
**从window\_manager结构可以很清晰的看出window\_manager模块分成窗口管理和Display两块,均使用Client/Server模式,各自细分模块的功能如下:**
* **Window Manager Client**
**应用进程窗口管理接口层,提供窗口对对象抽象和窗口管理接口,对接原能力和UI框架。**
* **Display Manager Client**
**应用进程Display管理接口层,提供Display信息抽象和Display管理接口。**
* **Window Manager Server**
**窗口管理服务,提供窗口布局、Z序控制、窗口树结构、窗口拖拽、窗口快照等能力,并提供窗口布局和焦点窗口给多模输入**
**窗口布局:重叠,平铺等**
**Z序控制:窗口可以重叠的前提就要求有Z序控制,Z序可以理解为在传统的以X,Y为坐标的屏幕中的Z轴的序列,来表示窗口在屏幕这个平面堆叠的顺序。**
**窗口树结构:WindowRoot,WindowNode就是管理窗口树结构的模块。**
**窗口拖拽:窗口拖拽事件处理。**
**窗口快照:这个是智能手机兴起之后流行的功能,用于多开应用窗口的时候便捷查看和切换应用或者窗口。**
**我们对比下window发布的前几个版本窗口的功能,可以看到窗口管理的主要功能这几十年来并没有多少变化,包括笔者查看的一篇写于2008年的windows系统的窗口管理的技术文章,于今也是大同小异。**
* **Display Manager Server**
**Display管理服务,提供Display信息、屏幕截图、屏幕亮灭和亮度处理控制,并处理Display与Screen映射关系**
**对其他一些概念的解释:**
** **Display,Screen,Window:
** **这三个概念很容易混淆,我们可以参考下Linux下图形管理中的定义:
** **Display :** ** 若干个屏幕(screen)以及一套输入设备(键盘和鼠标)构成一个display,display概念的关键就是有一套完整的输入输出。屏幕不一定必须是一个,可以有多个,各个屏幕可以用来显示相同的内容,也可以用来构成矩阵显示一个大屏幕的内容。** **** **Screen :** ** Screen的层次在display之下,是一个实际的Monitor或是Device。一个screen对应一个根窗口(root window),根窗口的大小与screen相同。** **** **Window :** ** Window是比screen还要小一级的概念了。Window是有树形继承关系的,每一个屏幕上都对应有一个“窗口树”,树的根就是root window,即根窗口,它没有父窗口;除此之外,所有window都有父窗口。一个窗口还可能有子窗口,但并不是必须的。
## window\_manager在OpenHarmony中的代码目录:
```
foundation/window/window_manager/
├── dm # Dislplay Manager Client实现代码
│ ├── BUILD.gn
│ ├── include
│ ├── src
│ └── test
├── dmserver # Dislplay Manager Service实现代码
│ ├── BUILD.gn
│ ├── include
│ ├── src
│ └── test
├── extension # Ability Component 窗口相关代码实现目录
│ ├── extension_connection # Ability Component 嵌入部分
│ └── window_extension # Ability Component 被嵌入部分
├── interfaces # 对外接口存放目录
│ ├── innerkits # native接口存放目录
│ └── kits # js/napi接口存放目录
├── previewer # IDE轻量模拟器窗口代码实现目录
│ ├── BUILD.gn
│ ├── include
│ ├── mock
│ └── src
├── resources # 框架使用资源文件存放目录
│ ├── BUILD.gn
│ ├── config
│ ├── dialog_ui
│ └── media
├── sa_profile # 系统服务配置文件
│ ├── 4606.xml
│ ├── 4607.xml
│ └── BUILD.gn
├── snapshot # 截屏命令行工具实现代码
│ ├── BUILD.gn
│ ├── include
│ ├── src
│ └── test
├── test # Fuzz测试和系统测试用例存放目录
│ ├── BUILD.gn
│ ├── common
│ ├── demo
│ ├── fuzztest
│ └── systemtest
├── utils # 工具类存放目录
│ ├── BUILD.gn
│ ├── include
│ ├── src
│ └── test
├── wm # Window Manager Client实现代码
│ ├── BUILD.gn
│ ├── include
│ ├── src
│ └── test
└── wmserver # Window Manager Service实现代码
├── BUILD.gn
├── include
├── src
└── test
```
**比较核心的文件夹是两个使用Client-Server模型的wm/wmserver,dm/dmserver文件夹,其中Client端提供模块对外的接口,Server端提供具体的实现。下面我们以wm/wmserver为例讲解下windowmanager的实现。**
## window\_manager的代码实现:
**小Tip:当学习一个新的模块的时候,如果不知道如何下手,可以在这个模块中选择一个具体的功能(最好是具有代表性的),找到相关的接口(流程里的任何阶段的都可以),然后去通过不断的查询调用者和接口本身的实现又调用了谁来梳理下流程,画出时序图,然后根据定义接口的头文件来确定类图。这两个图确定后,整体的代码结构和流程就会变得清晰。**
**我们先回顾下窗口管理的相关功能:**
**窗口管理:窗口提供管理窗口的一些基础能力,包括对当前窗口的创建、销毁、各属性设置,以及对各窗口间的管理调度。**
**我们很容易可以在window\_impl.h文件中找到这些基础能力的对应接口:**
**\\foundation\\window\\window\_manager\\wm\\include\\window\_impl.h**
```
WMError Create(uint32_t parentId, //创建
const std::shared_ptr<AbilityRuntime::Context>& context = nullptr);
virtual WMError Destroy() override; //销毁
//窗口的管理调度
virtual WMError Show(uint32_t reason = 0, bool withAnimation = false) override;
virtual WMError Hide(uint32_t reason = 0, bool withAnimation = false) override;
virtual WMError MoveTo(int32_t x, int32_t y) override;
virtual WMError Resize(uint32_t width, uint32_t height) override;
virtual WMError SetKeepScreenOn(bool keepScreenOn) override;
virtual bool IsKeepScreenOn() const override;
virtual WMError SetTurnScreenOn(bool turnScreenOn) override;
virtual bool IsTurnScreenOn() const override;
virtual WMError SetBackgroundColor(const std::string& color) override;
virtual WMError SetTransparent(bool isTransparent) override;
virtual bool IsTransparent() const override;
virtual WMError SetBrightness(float brightness) override;
virtual float GetBrightness() const override;
virtual WMError SetCallingWindow(uint32_t windowId) override;
virtual void SetPrivacyMode(bool isPrivacyMode) override;
virtual bool IsPrivacyMode() const override;
virtual void SetSystemPrivacyMode(bool isSystemPrivacyMode) override;
virtual void DisableAppWindowDecor() override;
virtual WMError BindDialogTarget(sptr<IRemoteObject> targetToken) override;
virtual void SetSnapshotSkip(bool isSkip) override;
// window effect 属性设置
virtual WMError SetCornerRadius(float cornerRadius) override;
virtual WMError SetShadowRadius(float radius) override;
virtual WMError SetShadowColor(std::string color) override;
virtual void SetShadowOffsetX(float offsetX) override;
virtual void SetShadowOffsetY(float offsetY) override;
virtual WMError SetBlur(float radius) override;
virtual WMError SetBackdropBlur(float radius) override;
virtual WMError SetBackdropBlurStyle(WindowBlurStyle blurStyle) override;
```
**我们再以窗口的创建函数为例梳理下流程:**
**Create函数的实现:**
**\\foundation\\window\\window\_manager\\wm\\src\\window\_impl.cpp**
```
WMError WindowImpl::Create(uint32_t parentId, const std::shared_ptr<AbilityRuntime::Context>& context)
{
//此处省略若干代码
WMError ret = SingletonContainer::Get<WindowAdapter>().CreateWindow(windowAgent, property_, surfaceNode_,
windowId, token);
RecordLifeCycleExceptionEvent(LifeCycleEvent::CREATE_EVENT, ret);
//此处省略若干代码
return ret;
}
```
**可以再代码里面看到调用了WindowAdapter中的CreateWindow函数,而本身WindowImpl::Create的调用者可以通过IDE工具搜到:**
**\\foundation\\window\\window\_manager\\wm\\src\\window.cpp**
```
sptr<Window> Window::Create(const std::string& windowName, sptr<WindowOption>& option,
const std::shared_ptr<OHOS::AbilityRuntime::Context>& context)
{
//此处省略若干代码
WMError error = windowImpl->Create(option->GetParentId(), context);//调用windowImpl->Create
if (error != WMError::WM_OK) {
return nullptr;
}
return windowImpl;
}
```
**通过不断的寻找调用者和调用的接口,可以获得一个时序图(图1):**
![WM时序图]
![WM时序图.png](data/attachment/forum/202310/30/094344g6e33zzfm3r3ab63.png "WM时序图.png")
** **图1 开机动画窗口创建时序图
**说明:**
1. **这个是开机动画创建窗口流程时序图。**
2. **rs\_surface\_node.cpp这个类负责窗口的绘制,属于graphic模块的内容,和window\_manager模块的关系就是window\_manager的窗口最终还是要依赖graphic模块去进行绘制的。**
3. **window\_adapter.cpp与window\_manager\_service.cpp之间使用IPC进行跨进程通讯,详细的IPC实现说明会在下篇文章介绍。**
**代码的文件结构清晰了,大致的代码流程也熟悉了,可以绘制类图了(现在很多工具可以自动绘制类图,但是建议自己亲手绘制下,对理清代码结构和思路很有帮助)**
![WM类图1]
![WM类图1.png](data/attachment/forum/202310/30/094403qv103271vu9bn718.png "WM类图1.png")![WM类图2]![WM类图2.png](data/attachment/forum/202310/30/094415omtuwqbm0ul3q5u1.png "WM类图2.png")
** **图2 windowmanager类图
**说明:**
1. **window\_manager\_service是窗口管理的服务端类,窗口管理功能的具体实现是在服务端实现。**
2. **window\_manager\_service和window\_adapter属于不同线程,他们通过IPC进行通讯。**
3. **窗口管理服务,提供窗口布局、Z序控制、窗口树结构、窗口拖拽、窗口快照等能力,并提供窗口布局和焦点窗口给多模输入,这些功能在各个类的中的对应:**
**窗口布局:WindowLayOutPolicy**
**Z序控制:WindowZorderPolicy**
**窗口树结构:WindowRoot**
**窗口拖拽:DragController**
**窗口快照:SnapshotController**
**提供多模输入:InputWindowMonitor**
**具体的代码实现有兴趣的同学可以自行研究,更有兴趣的同学可以考虑绘制下DisplayManager的时序图和类图。**
## OpenHarmony的IPC实现:
**从Window\_Manager的架构图可以看到,WindowManagerClient和WindowMangerServer分属不同的进程,使用客户端-服务器(Client-Server)模型,服务请求方(Client)可获取服务提供方(Server)的代理 (Proxy),并通过此代理读写数据来实现进程间的数据通信。**
**Linux中进程之间的IPC调用有:管道、共享内存、消息队列、信号量、socket、binder,OpenHarmony基于binder驱动封装了一套ipc机制(foundation\\communication\\ipc)用于实现设备内的跨进程通信。**
![IPC]
![IPC.png](data/attachment/forum/202310/30/094437vool8ynycbocllpn.png "IPC.png")
** ** ** **图3 OpenHarmony IPC机制
**我们可以看下OpenHarmony IPC机制(图2),Binder机制通常采用客户端-服务器(Client-Server)模型,服务请求方(Client)可获取服务提供方(Server)的代理 (Proxy),并通过此代理读写数据来实现进程间的数据通信。通常,系统能力(SystemAbility)Server侧会先注册到系统能力管理者(System Ability Manager,缩写SAMgr)中,SAMgr负责管理这些SA并向Client提供相关的接口(添加,查询,获取,删除等)。Client要和某个具体的SA通信,必须先从SAMgr中获取该SA的代理,然后使用代理和SA通信。**
**这里面用到了一个Proxy-Stub (代理-存根)的设计模式,Proxy将特殊性接口转换成通用性接口,Stub将通用性接口转换成特殊性接口,二者之间的数据转换通过Parcel(打包)进行的,Proxy常作为数据发送代理,通过Parcel将数据打包发送,Stub常作为数据接收桩,解包并解析Parcel Data package。**
**使用这个设计模式,基础的步骤如下:**
**A. 设计接口类,继承至 IRemoteBroker,接口方法一般设计为虚方法。**
**B. 设计 proxy 类,继承至 IRemoteProxy,并且实现 sendRequest 方法和自身虚方法。**
**C. 设计 stub 类,继承至 IRemoteStub ,并且实现 OnRemote 方法和自身虚方法。**
**我们以WindowManager模块代码为例看下IPC的实现:**
## WindowManagerIPC实现的步骤:
1. **定义IPC对外接口IWindowManager **
**定义该服务对外提供的能力集合函数与消息的枚举,统一继承IPC接口类IRemoteBroker;同时声明该IPC对外接口唯一标识符DECLARE\_INTERFACE\_DESCRIPTOR(XXX);该标识符用于IPC通信的校验等目的。**
** \\foundation\\window\\window\_manager\\wmserver\\include\\zidl\\window\_manager\_interface.h**
```
class IWindowManager : public IRemoteBroker {
public:
DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.IWindowManager");
enum class WindowManagerMessage : uint32_t {
TRANS_ID_CREATE_WINDOW,
TRANS_ID_ADD_WINDOW,
TRANS_ID_REMOVE_WINDOW,
TRANS_ID_DESTROY_WINDOW,
//此处省略代码若干行
```
---
2. **定义客户端代码WindowManagerProxy :**
**\\foundation\\window\\window\_manager\\wmserver\\include\\zidl\\window\_manager\_proxy.h**
```
class WindowManagerProxy : public IRemoteProxy<IWindowManager> {
public:
explicit WindowManagerProxy(const sptr<IRemoteObject>& impl) : IRemoteProxy<IWindowManager>(impl) {};
~WindowManagerProxy() {};
//此处省略代码若干行
```
3. **定义服务端代码WindowManagerStub **
**\\foundation\\window\\window\_manager\\wmserver\\include\\zidl\\window\_manager\_stub.h**
```
class WindowManagerStub : public IRemoteStub<IWindowManager> {
public:
WindowManagerStub() = default;
~WindowManagerStub() = default;
virtual int32_t OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply,
MessageOption &option) override;
};
```
** 此外,如果要实现IPC,还需要服务端进程注册SA,服务提供方所在进程启动后,申请SA的唯一标识,将Stub注册到SAMgr。客户端进程通过SA的标识,从SAMgr获取Proxy,通过Proxy实现与Stub的跨进程通信。**
---
4. **实现windowmanagerService的服务**
**WindowManagerService 同时继承了SystemAbility和WindowManagerStub ,其中重写SystemAbility的OnStart()和OnStop()。**
**\\foundation\\window\\window\_manager\\wmserver\\include\\window\_manager\_service.h**
```
class WindowManagerService : public SystemAbility, public WindowManagerStub {
friend class DisplayChangeListener;
friend class WindowManagerServiceHandler;
DECLARE_SYSTEM_ABILITY(WindowManagerService);
WM_DECLARE_SINGLE_INSTANCE_BASE(WindowManagerService);
public:
using Task = std::function<void()>;
void OnStart() override;
void OnStop() override;
void OnAddSystemAbility(int32_t systemAbilityId, const std::string &deviceId) override;
int Dump(int fd, const std::vector<std::u16string>& args) override;
```
---
**\\foundation\\window\\window\_manager\\wmserver\\src\\window\_manager\_service.cpp**
```
void WindowManagerService::OnStart()
{
WLOGFI("WindowManagerService::OnStart start");
if (!Init()) {//init函数里面进行publish操作,发布系统服务
return;
}
//以下省略若干行,主要为注册各种回调的内容
```
---
**//把WindowManagerService注册到LocalAbilityManager中**
```
const bool REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(&SingletonContainer::Get<WindowManagerService>());
```
**\\foundation\\systemabilitymgr\\safwk\\services\\safwk\\src\\system\_ability.cpp**
```
bool SystemAbility::MakeAndRegisterAbility(SystemAbility* systemAbility)
{
HILOGD(TAG, "registering system ability...");
return LocalAbilityManager::GetInstance().AddAbility(systemAbility);
}
```
** 以c++实现的SA必须配置相关SystemAbility的profile配置文件才会完成SA的加载注册逻辑,否则没有编写配置文件的SystemAbility不会完成注册。配置方法需要**
** 在子系统的根目录新建一个以sa\_profile为名的文件夹,然后在此文件夹中新建两个文件:一个以saId为前缀的xml文件,另外一个为BUILD.gn文件。**
**window\_manager模块的相关文件夹目录如下:**
```
├── sa_profile # 系统服务配置文件
│ ├── 4606.xml
│ ├── 4607.xml
│ └── BUILD.gn
```
** 具体的xml的字段意义以及服务如何被拉起的有兴趣的同学自行研究下,本文就暂不深入讨论。**
## 总结:
**这篇文章希望能让大家对window\_manager模块有一个总体的认知,后续各种具体功能的实现细节大家有兴趣可以自行研究。**
## 参考文献:
1. [
https://gitee.com/openharmony/window
\_window\_manager](
https://gitee.com/openharmony/window_window_manager
) 《window\_manger仓介绍》
2. [
https://www.open-open.com/news/view/185d270
](
https://www.open-open.com/news/view/185d270
) 《图说Windows演变史:1985-2012》
3. [
https://gitee.com/openharmony/window
\_window\_manager](
https://gitee.com/openharmony/window_window_manager
) 《window\_manger仓源码》
[/md]
欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/)
Powered by Discuz! X3.5