【QT】Qt ApplicationManager Compositor源码分析

news2024/10/6 20:28:49

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.0

Item {
    // 省略
    // 重点关注这段代码
    // Show windows
    Column {
        anchors.right: parent.right
        Repeater {
            model: WindowManager
            WindowItem {
                width: 600
                height: 200
                window: 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);

       // 省略
    }
#else
    if (!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 appId
        const QString xdgAppId = surface->applicationId();
        if (!xdgAppId.isEmpty()) {
            for (const auto &checkApp : apps) {
                if (checkApp->id() == xdgAppId) {
                    app = checkApp;
                    break;
                }
            }
        }
    }

// 只有开启了非安全模式,才可以接收非App侧创建的Surface
    if (!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 not
    int 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 destruction
        m_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 node
            delete 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 node
        delete 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;
#else
    qCWarning(qLcWaylandCompositor) << "Without OpenGL support only shared memory textures are supported";
    return nullptr;
#endif // QT_CONFIG(opengl)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/608912.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微机实验:第5章——存储器设计

存储器设计 将两片6116所有的存储单元都写入11H。 提示&#xff1a;6116的存储容量为2K*8b&#xff0c;片内地址为0000H-07FFH,两片一起构成F8000H-F8FFFH的内存空间。 仿真调试时可以看到&#xff1a;每片从0000H-07FFH的每个存储单元均显示11H。 CODE SEGMENTASSUME CS:C…

4-4 哈夫曼编码

博主简介&#xff1a;一个爱打游戏的计算机专业学生博主主页&#xff1a; 夏驰和徐策所属专栏&#xff1a;算法设计与分析 1.什么是哈夫曼编码&#xff1f; 哈夫曼编码&#xff08;Huffman coding&#xff09;是一种用于数据压缩的无损编码方法。它是由David A. Huffman在1952…

STM32F4_软件模拟SPI

目录 1. 硬件连接 2. SPI通讯协议 3. W25Q64 简介 4. 程序详解 4.1 main.c 4.2 SPI.c 4.3 SPI.h 4.4 W25Q128.c 4.5 W25Q128.h 4.6 OLED.c 4.7 OLED.h 4.8 OLED_Font.h 5. 实验结果 我们都知道&#xff0c;SPI 和 IIC 一样&#xff0c;都可以通过硬件方式和软件方…

JSON基础(待补充)

一、JSON初识 1.1基础认识 JSON是一种轻量级的数据交换格式&#xff0c;它基于JavaScript语言的对象表示法&#xff0c;可以在多种语言之间进行数据交换。JSON的基本数据类型有数值、字符串、布尔值、数组、对象和空值。JSON的格式简洁易读&#xff0c;也易于解析和处理。JSON…

【数据结构】由完全二叉树引申出的堆的实现

【数据结构】由完全二叉树引申出的堆的实现 一、什么是堆二、目标三、实现1、初始化工作2、堆的插入(堆的创建)2.1、向上调整建堆2.1.1、向上调整算法原理解析2.1.2、代码实现 2.2、向下调整建堆2.2.1、向下调整算法原理解析2.2.2、代码实现 2.3、“向上”和“向下”复杂度的差…

初识网络安全

目录 HTML前置基础知识 1、id和class区别&#xff1a; 2、一些常用的属性&#xff1a; 3、HTML字符编码和实体编码 4、URL介绍 网址的组成部分&#xff1a; TTL值 DNS工作原理和资源记录及其种类&#xff1a; 5、正确区分“加密”和“签名” 6、状态码 1xx &#xf…

如何安装pycharm

PyCharm是JetBrains公司推出的一款Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;可以提供高效的Python代码编写、调试和测试。以下是一些PyCharm的主要功能&#xff1a; 代码智能提示和自动补全功能&#xff1b;支持调试和测试Python代码&#xff1b;完整的Pyth…

基于Springboot+Vue的幼儿园管理系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

汽车相关知识及术语

1 汽车构造与制造流程 1.1 汽车构造 汽车可以分为四大部分 车身&#xff1a; 骨架、车身钣金件以及座椅、仪表、天窗、车外后视镜等车身附件 动力系统&#xff1a; 发动机和变速器 底盘&#xff1a; 传动系统、悬架系统、转向系统、制动系统和车轮轮胎 电气电子系统&#…

《Apollo 智能驾驶进阶课程》三、无人车自定位技术

1. 什么是无人车自定位系统 相对一个坐标系来确定无人车的位置和姿态 定位的指标要求大概分为三个部分&#xff1a;精度、鲁棒性、场景 定位精度必须控制在10厘米以内&#xff0c;才能使行驶中的自动驾驶车辆避免出现碰撞/车道偏离的情况。鲁棒性一般情况下用最大值来衡量。…

Java IO流详细教程

目录 一、IO介绍 IO流体系 字节流 字节输出流&#xff1a;FileoutputStream 字节输入流FilelnputStream 字符流 字符输入流 字符输出流 缓冲流 字节缓冲流 字符缓冲流 序列化、反序列化流 序列化/对象操作输出流 反序列化/对象操作输入流 打印流 字节打印流 字…

firewalld与iptables练习

1、禁止一个IP访问 iptables -I INPUT -s $ip -j REJECT 2、清空默认的防火墙默认规则&#xff1a; iptables -F 3、保存清空后的防火墙规则表 service iptables save 4、firewall-cmd --list-all #查看防火墙规则&#xff08;只显示/etc/firewalld/zones/public.xml中防火墙…

投票活动链接创建微信链接视频投票线上免费投票链接

近些年来&#xff0c;第三方的微信投票制作平台如雨后春笋般络绎不绝。随着手机的互联网的发展及微信开放平台各项基于手机能力的开放&#xff0c;更多人选择微信投票小程序平台&#xff0c;因为它有非常大的优势。 1.它比起微信公众号自带的投票系统、传统的H5投票系统有可以图…

从零手写操作系统之RVOS协作式多任务切换实现-03

从零手写操作系统之RVOS协作式多任务切换实现-03 任务&#xff08;task&#xff09;多任务 &#xff08;Multitask&#xff09;任务上下文&#xff08;Context&#xff09;多任务系统的分类协作式多任务 创建和初始化第 1 号任务切换到第一号任务执行协作式多任务 - 调度初始化…

字典树算法(C/C++)

目录 一、字典树算法的概念介绍 二、字典树算法的实现 三、例题 &#xff08;注&#xff1a;借鉴蓝桥杯国赛特训营&#xff09; 一、字典树算法的概念介绍 首先我们看下字典的组织方式 Trie 的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效…

实训总结-----Scrapy爬虫

1.安装指令 pip install scrapy 2.创建 scrapy 项目 任意终端 进入到目录(用于存储我们的项目) scrapy startproject 项目名 会在目录下面 创建一个以 项目名 命名的文件夹 终端也会有提示 cd 项目名 scrapy genspider example example.com 3.运行爬虫指令 scrapy craw…

ffmpeg之AVFormatContext结构体详细解释

AVFormatContext 作用 AVFormatContext主要起到了管理和存储媒体文件相关信息的作用。它是一个比较重要的结构体&#xff0c;在FFmpeg中用于表示媒体文件的格式上下文&#xff0c;其中包含了已经打开的媒体文件的详细信息&#xff0c;包括媒体文件的格式、媒体流的信息、各个媒…

【笔记】使用电脑连接树莓派 并在电脑屏幕上显示树莓派桌面(无需额外为树莓派购买显示器)

一、前言 想在树莓派上跑 yolo5&#xff0c;为了方便地看到代码的检测结果&#xff0c;需要为树莓派外接显示器&#xff0c;但是手头并没有额外的显示器&#xff0c;于是想在电脑屏幕上显示树莓派的桌面&#xff0c;对解决的过程作一些记录。 二、基本流程 树莓派系统的烧录…

c++11 标准模板(STL)(std::bitset)(三)

定义于头文件 <bitset> template< std::size_t N > class bitset; 类模板 bitset 表示一个 N 位的固定大小序列。可以用标准逻辑运算符操作位集&#xff0c;并将它与字符串和整数相互转换。 bitset 满足可复制构造 (CopyConstructible) 及可复制赋值 (CopyAssign…

【SpringMVC】请求与响应

1&#xff0c;PostMan工具的使用 1. PostMan简介 代码编写完后&#xff0c;我们要想测试&#xff0c;只需要打开浏览器直接输入地址发送请求即可。发送的是GET请求可以直接使用浏览器&#xff0c;但是如果要发送的是POST请求呢? 如果要求发送的是post请求&#xff0c;我们就…