【QT】Qt ApplicationManager Compositor源码分析

news/2024/11/15 1:30:17/

Qt ApplicationManager的Compositor功能分析

  • 根据Qt ApplicationManager官网介绍,它基于Wayland协议实现了Compositor功能。下述为官网介绍。实际上,QtApplicationManager是使用了QtWayland模块来实现Compositor的。Wayland是一套旨在替代XWindow的 Compositor标准,感兴趣的可自行了解。

To support multiple UI processes on an embedded Linux system, you need a central window compositor: a Wayland compositor is the state-of-the-art solution for this. Consequently, the application manager incorporates a compositor that is fully-compliant with the Wayland protocol, based on the QtWayland module.

  • 关于Wayland Compositor,简单来说就是Client绘制Buffer,Compositor将这些Buffer融合,最终显示到屏幕上。大概就是下图这个样子(图片摘自QtWayland官网)。
    在这里插入图片描述
  • QtApplicationManager推荐使用QML方式进行开发,可以QML编写一套Compositor服务(进程)。

这里基于QML方式的Compositor Sample,分析一下QtApplicationManager是如何实现Compositor功能。

一个QML的Compositor例子

  • 路径qtapplicationmanager\examples\applicationmanager\hello-world下提供了Hello World程序,其中system-ui.qml(启动入口)实现如下
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2019 Luxoft Sweden AB
** Copyright (C) 2018 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtApplicationManager module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/import QtQuick 2.4
import QtApplicationManager.SystemUI 2.0Item {// 省略// 重点关注这段代码// Show windowsColumn {anchors.right: parent.rightRepeater {model: WindowManagerWindowItem {width: 600height: 200window: model.window}}}
}
  • 上面的代码中,将WindowManager对象内部的一系列window对象,绑定到 WindowItem上,并设置了宽和高。因为WaylandSurface与WindowManager中的 window对象是一对一的(后面会解释),所以这段代码,就是让Surface显示出来的本质。

  • 创建WaylandSurface时WindowManager创建Window对象,具体实现如下。

// 绑定了QWaylandSurface::hasContentChanged信号,当有该信号时触发surfaceMapped信号。
// 该信号在Surface中有Buffer被Commit时触发
// 这块代码可以看出来 QtApplicationManager目前只支持 XDG和WlShell两种类型。void WaylandCompositor::createWlSurface(QWaylandSurface *surface, const QWaylandResource &resource)
{WindowSurface *windowSurface = static_cast<WindowSurface *>(surface);QWaylandWlShellSurface *ss = new QWaylandWlShellSurface(m_wlShell, windowSurface, resource);windowSurface->setShellSurface(ss);connect(windowSurface, &QWaylandSurface::hasContentChanged, this, [this, windowSurface]() {if (windowSurface->hasContent())emit this->surfaceMapped(static_cast<WindowSurface*>(windowSurface));});
}void WaylandCompositor::onXdgSurfaceCreated(QWaylandXdgSurface *xdgSurface)
{WindowSurface *windowSurface = static_cast<WindowSurface*>(xdgSurface->surface());Q_ASSERT(!windowSurface->m_wlSurface);windowSurface->m_xdgSurface = xdgSurface;connect(windowSurface, &QWaylandSurface::hasContentChanged, this, [this, windowSurface]() {if (windowSurface->hasContent())emit this->surfaceMapped(windowSurface);});emit windowSurface->xdgSurfaceChanged();
}
void WindowManager::registerCompositorView(QQuickWindow *view)
{// 省略#if defined(AM_MULTI_PROCESS)if (!ApplicationManager::instance()->isSingleProcess()) {if (!d->waylandCompositor) {d->waylandCompositor = new WaylandCompositor(view, d->waylandSocketName);for (const auto &extraSocket : d->extraWaylandSockets)d->waylandCompositor->addSocketDescriptor(extraSocket);connect(d->waylandCompositor, &QWaylandCompositor::surfaceCreated,this, &WindowManager::waylandSurfaceCreated);connect(d->waylandCompositor, &WaylandCompositor::surfaceMapped,this, &WindowManager::waylandSurfaceMapped);// 省略}
#elseif (!once)qCInfo(LogGraphics) << "WindowManager: running in single-process mode [forced at compile-time]";
#endif// 省略
}// 这里函数里面,会创建Window对象,并将Window对象与Surface对象绑定。
void WindowManager::waylandSurfaceMapped(WindowSurface *surface)
{qint64 processId = surface->processId();const auto apps = ApplicationManager::instance()->fromProcessId(processId);Application *app = nullptr;if (apps.size() == 1) {app = apps.constFirst();} else if (apps.size() > 1) {// if there is more than one app within the same process, check the XDG surface appIdconst QString xdgAppId = surface->applicationId();if (!xdgAppId.isEmpty()) {for (const auto &checkApp : apps) {if (checkApp->id() == xdgAppId) {app = checkApp;break;}}}}// 只有开启了非安全模式,才可以接收非App侧创建的Surfaceif (!app && ApplicationManager::instance()->securityChecksEnabled()) {qCCritical(LogGraphics) << "SECURITY ALERT: an unknown application with pid" << processId<< "tried to map a Wayland surface!";return;}Q_ASSERT(surface);qCDebug(LogGraphics) << "Mapping Wayland surface" << surface << "of" << d->applicationId(app, surface);// Only create a new Window if we don't have it already in the window list, as the user controls// whether windows are removed or notint index = d->findWindowByWaylandSurface(surface->surface());if (index == -1) {WaylandWindow *w = new WaylandWindow(app, surface);// 这里会将Window绑定到List上setupWindow(w);}
}
  • 为什么将Window(对应Surface)绑定到WindowItem上可以显示出Surface的内容?将Window赋值给WindowItem,实际上调用的是下面的函数。所以实际上,是Surface设置给了QWaylandQuickItem。
void WindowItem::WaylandImpl::setup(Window *window)
{Q_ASSERT(!m_waylandWindow);Q_ASSERT(window && !window->isInProcess());m_waylandWindow = static_cast<WaylandWindow*>(window);if (!m_waylandItem)createWaylandItem();m_waylandItem->setBufferLocked(false);m_waylandItem->setSurface(m_waylandWindow->surface());
}void WindowItem::WaylandImpl::createWaylandItem()
{m_waylandItem = new QWaylandQuickItem(q);connect(m_waylandItem, &QWaylandQuickItem::surfaceDestroyed, q, [this]() {// keep the buffer there to allow us to animate the window destructionm_waylandItem->setBufferLocked(true);});
}
  • QWaylandQuickItem继承QQuickItem,并实现了updatePaintNode函数。继承QQuickItem的类,可以通过实现该虚函数来绘制自己想要的内容。这段代码比较长,大体上就是从Surface(也就是View)中拿出Buffer,做成纹理节点并返回。QT底层的RenderThread会将该节点绘制到屏幕上。
QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{Q_D(QWaylandQuickItem);d->lastMatrix = data->transformNode->combinedMatrix();const bool bufferHasContent = d->view->currentBuffer().hasContent();if (d->view->isBufferLocked() && d->paintEnabled)return oldNode;if (!bufferHasContent || !d->paintEnabled || !surface()) {delete oldNode;return nullptr;}QWaylandBufferRef ref = d->view->currentBuffer();const bool invertY = ref.origin() == QWaylandSurface::OriginBottomLeft;const QRectF rect = invertY ? QRectF(0, height(), width(), -height()): QRectF(0, 0, width(), height());if (ref.isSharedMemory()
#if QT_CONFIG(opengl)|| bufferTypes[ref.bufferFormatEgl()].canProvideTexture
#endif) {
#if QT_CONFIG(opengl)if (oldNode && !d->paintByProvider) {// Need to re-create a nodedelete oldNode;oldNode = nullptr;}d->paintByProvider = true;
#endif// This case could covered by the more general path below, but this is more efficient (especially when using ShaderEffect items).QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode);if (!node) {node = new QSGSimpleTextureNode();if (smooth())node->setFiltering(QSGTexture::Linear);d->newTexture = true;}if (!d->provider)d->provider = new QWaylandSurfaceTextureProvider();if (d->newTexture) {d->newTexture = false;d->provider->setBufferRef(this, ref);node->setTexture(d->provider->texture());}d->provider->setSmooth(smooth());node->setRect(rect);qreal scale = surface()->bufferScale();QRectF source = surface()->sourceGeometry();node->setSourceRect(QRectF(source.topLeft() * scale, source.size() * scale));return node;}#if QT_CONFIG(opengl)Q_ASSERT(!d->provider);if (oldNode && d->paintByProvider) {// Need to re-create a nodedelete oldNode;oldNode = nullptr;}d->paintByProvider = false;QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);if (!node) {node = new QSGGeometryNode;d->newTexture = true;}QSGGeometry *geometry = node->geometry();QWaylandBufferMaterial *material = static_cast<QWaylandBufferMaterial *>(node->material());if (!geometry)geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);if (!material)material = new QWaylandBufferMaterial(ref.bufferFormatEgl());if (d->newTexture) {d->newTexture = false;material->setBufferRef(this, ref);}const QSize surfaceSize = ref.size() / surface()->bufferScale();const QRectF sourceGeometry = surface()->sourceGeometry();const QRectF normalizedCoordinates =sourceGeometry.isValid()? QRectF(sourceGeometry.x() / surfaceSize.width(),sourceGeometry.y() / surfaceSize.height(),sourceGeometry.width() / surfaceSize.width(),sourceGeometry.height() / surfaceSize.height()): QRectF(0, 0, 1, 1);QSGGeometry::updateTexturedRectGeometry(geometry, rect, normalizedCoordinates);node->setGeometry(geometry);node->setFlag(QSGNode::OwnsGeometry, true);node->setMaterial(material);node->setFlag(QSGNode::OwnsMaterial, true);return node;
#elseqCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";return nullptr;
#endif // QT_CONFIG(opengl)
}

http://www.ppmy.cn/news/212665.html

相关文章

【Java系列】Mybatis-Plus 使用介绍二

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 MyBatis-Plus 是 MyBatis 的增强工具&#xff0c;它简化了 MyBatis 的开发&#xff0c;并提供了许多实用的功能和工具类。下面是 MyBatis-Plus 的使用方法&#xff1a; 1 使用方法 1. 引入依赖 在 Maven…

Node服务器和常见模块

1 Node服务器开发 2 fs模块-文件系统 3 event模块-事件处理 4 认识二进制和buffer 5 Buffer的创建方式 6 Buffer的源码解析 node 的fs模块操作&#xff1a; 这里主要讲node如何进行读取文件&#xff0c;操作文件。服务器该有的操作node都有。 node的fs读取文本文件内容的d…

从0-1一起学习live555设计思想之一 基础运行环境 + 任务调度

系列文章目录 文章目录 系列文章目录前言一、基础组件总览二、UsageEnvironment三、BasicUsageEnvironment0四、BasicUsageEnvironment五、TaskScheduler六、BasicTaskScheduler0七、DelayQueue八、BasicTaskScheduler九、基础调度总结总结前言 一、基础组件总览 本篇开始分析…

iPhone 全系尺寸大全

iPhone 设备尺寸信息

oppofindx5pro参数配置

OPPOFindX5Pro背部采用了更为先进的工艺制程&#xff0c;官方称之为“一体化纳米微晶陶瓷”&#xff0c;手感相比上一代有了大幅提升&#xff0c;温润如玉&#xff0c;但手感太滑了&#xff0c;很多时候我都是双手握持&#xff0c;由于电池容量的增加&#xff0c;这款手机的厚度…

判断手机型号

- (NSString*)deviceString { // 需要#import "sys/utsname.h" struct utsname systemInfo; uname(&systemInfo); NSString *deviceString [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; if ([deviceString isEqualToString…

MySQL代码错误号大全

错误&#xff1a;1000 SQLSTATE: HY000 (ER_HASHCHK) 消息&#xff1a;hashchk 错误&#xff1a;1001 SQLSTATE: HY000 (ER_NISAMCHK) 消息&#xff1a;isamchk 错误&#xff1a;1002 SQLSTATE: HY000 (ER_NO) 消息&#xff1a;NO 错误&#xff1a;1003 SQLSTATE: HY0…

viv代码分析(一)

本博客转载于&#xff1a;http://www.cnblogs.com/tureno/articles/4445393.html 通过vivi研究bootloader有一段时间了&#xff0c;基本是在与之相关的基础方面做工作&#xff0c;还没有真正深入研究vivi。以后的学习重心就要放到研究vivi源代码上面了。我想&#xff0c;真正细…