Weston 窗口管理(2)

news2025/1/13 10:32:17

窗口管理(2)

本文基于 weston 分支 10.0.2 进行描述.

五、概述

本文为窗口管理(1)的续章,更多站在开发者角度,以 weston 的代码实现讲解窗口管理(1)中所实现的部分业务场景.

六、数据结构

窗口管理(1)中曾经描述过 weston 具体的分层逻辑,如下:

请添加图片描述

再进一步可以把 WESTON_LAYER_POSITION_NORMAL 展开为 workspace,那么就可以用下图来进行描述:

请添加图片描述

weston 有关于窗口管理部分的逻辑代码实现大多数位于 desktop-shell 文件夹下,有关于此的数据描述为:

// file : desktop-shell/shell.h
struct desktop_shell {
    // ...
    struct weston_layer fullscreen_layer;    /* 全屏层     (WESTON_LAYER_POSITION_FULLSCREEN) */
    struct weston_layer panel_layer;         /* 系统桌面层  (WESTON_LAYER_POSITION_UI) 对于 weston 参考 panel-position (weston.ini) */
    struct weston_layer background_layer;    /* 背景层     (WESTON_LAYER_POSITION_BACKGROUND) */
    struct weston_layer lock_layer;          /* 锁屏层     (WESTON_LAYER_POSITION_LOCK) */
    struct weston_layer input_panel_layer;   /* 输入层     (WESTON_LAYER_POSITION_TOP_UI) 一般输入法位于此层 */
    struct weston_layer minimized_layer;     /* 最小化层   (逻辑层) */
    // ...
    struct {
        struct wl_array array;
        unsigned int current;                   /* 索引(array),指向当前工作空间 */
        unsigned int num;                       /* 工作空间的最大数量 */
        // ...
    } workspaces;
};

其中单个 worksapce 的数据结构为:

struct workspace {
    struct weston_layer layer;                      /* 普通应用层 (WESTON_LAYER_POSITION_NORMAL) */
    // ...
};

以上删除了部分与此无关的代码,从整理逻辑上来看 weston 的分层设计确实就是一个简单的二维链表(layer 中包含多个 view), 其中 normal layer 的实现比较特殊,可以看做是三维链表;除此之外并没有太多需要说明的地方.

除了上述的数据结构之外,窗口管理(1) 中还曾经说到过最终这些都会转化为一个一维只包含有 views 的链表:

请添加图片描述

支撑起右侧的数据结构为:

// file : libweston/compositor.c
struct weston_compositor {
    // ...
    struct wl_list layer_list;  /* struct weston_layer::link */
    struct wl_list view_list;   /* struct weston_view::link */
    // ...
};

从这里也可以看出来其实 weston 其实也是"业务"代码,各种上下文状态变量疯狂变更转移,实际上都是在内存里操作数据,真正干事的还是得靠系统接口;只是比较接近于系统级(虽然它也算是系统级程序,但本质来说也是一个应用层程序).

七、业务行为

(1) desktop_shellweston_compositor 数据绑定

要想实现将 layer 的二维数据结构转化为一维数据结构,而二维数据结构存放在 desktop_shell::*_layer 中,一维数据结构则是存放在 weston_compositor::view_list 中;所以需要将 desktop_shellweston_compositor 部分数据结构进行绑定.

首先是先需要提供 desktop_shell 中的 layer 注册至 weston_compositor, 这主要是以下两个接口实现的:

void weston_layer_set_position(struct weston_layer *layer, enum weston_layer_position position)
{
    struct weston_layer *below;

    wl_list_remove(&layer->link);

    /* 根据 position 将其按序添加入 compositor */
    layer->position = position;
    wl_list_for_each_reverse(below, &layer->compositor->layer_list, link) {
        if (below->position >= layer->position) {
            wl_list_insert(&below->link, &layer->link);
            return;
        }
    }
    wl_list_insert(&layer->compositor->layer_list, &layer->link);
}

void weston_layer_unset_position(struct weston_layer *layer)
{
    // Hint : layer->link 实际指向到了 weston_compositor::layer_list, 注意 weston_compositor 中的注释
    // See also : 有兴趣也可以了解一下Linux内核实现 `container_of` (百度),然后在看看 wayland CBB 的具体实现
    wl_list_remove(&layer->link);
    wl_list_init(&layer->link);
}

以上代码实际上非常简单,但是 weston 是基于 wayland 开发了,使用了 wayland 提供的 CBB 基础组件库,所以需要理解这个库的成本,在这里展开.

weston_layer_set_position 中出现了描述 layer 优先级的枚举 weston_layer_position, 其定义如下:

// file : include/libweston/libweston.h
/**
 * Higher value means higher in the stack.
 *
 * These values are based on well-known concepts in a classic desktop
 * environment. Third-party modules based on libweston are encouraged to use
 * them to integrate better with other projects.
 *
 * A fully integrated environment can use any value, based on these or not,
 * at their discretion.
 */
enum weston_layer_position {
    /*
        * Special value to make the layer invisible and still rendered.
        * This is used by compositors wanting e.g. minimized surfaces to still
        * receive frame callbacks.
        */
    WESTON_LAYER_POSITION_HIDDEN     = 0x00000000,

    /*
        * There should always be a background layer with a surface covering
        * the visible area.
        *
        * If the compositor handles the background itself, it should use
        * BACKGROUND.
        *
        * If the compositor supports runtime-loadable modules to set the
        * background, it should put a solid color surface at (BACKGROUND - 1)
        * and modules must use BACKGROUND.
        */
    WESTON_LAYER_POSITION_BACKGROUND = 0x00000002,

    /* For "desktop widgets" and applications like conky. */
    WESTON_LAYER_POSITION_BOTTOM_UI  = 0x30000000,

    /* For regular applications, only one layer should have this value
        * to ensure proper stacking control. */
    WESTON_LAYER_POSITION_NORMAL     = 0x50000000,

    /* For desktop UI, like panels. */
    WESTON_LAYER_POSITION_UI         = 0x80000000,

    /* For fullscreen applications that should cover UI. */
    WESTON_LAYER_POSITION_FULLSCREEN = 0xb0000000,

    /* For special UI like on-screen keyboard that fullscreen applications
        * will need. */
    WESTON_LAYER_POSITION_TOP_UI     = 0xe0000000,

    /* For the lock surface. */
    WESTON_LAYER_POSITION_LOCK       = 0xffff0000,

    /* Values reserved for libweston internal usage */
    WESTON_LAYER_POSITION_CURSOR     = 0xfffffffe,
    WESTON_LAYER_POSITION_FADE       = 0xffffffff,
};

基于以上的代码片段可知, weston_compositor::layer_listlayer 的排序是有顺序的,并且按照 weston_layer_position 的规则;其次 weston_compositor::layer_list 中的数据只是引用,其实都是来自于 desktop_shell.

然后 desktop_shell 会将 layer 插入 weston_compositor 中去,大致分为初始化以及运行时,截取部分初始化代码如下:

// file : desktop-shell/shell.c
int wet_shell_init(struct weston_compositor *ec, int *argc, char *argv[])
{
    // ...
    weston_layer_init(&shell->fullscreen_layer, ec);
    weston_layer_init(&shell->panel_layer, ec);
    weston_layer_init(&shell->background_layer, ec);
    weston_layer_init(&shell->lock_layer, ec);
    weston_layer_init(&shell->input_panel_layer, ec);

    weston_layer_set_position(&shell->fullscreen_layer,
                    WESTON_LAYER_POSITION_FULLSCREEN);
    weston_layer_set_position(&shell->panel_layer,
                    WESTON_LAYER_POSITION_UI);
    weston_layer_set_position(&shell->background_layer,
                    WESTON_LAYER_POSITION_BACKGROUND);
    // ...
    // 工作空间初始化 (normal layer)
    for (i = 0; i < shell->workspaces.num; i++) {
        pws = wl_array_add(&shell->workspaces.array, sizeof *pws);
        if (pws == NULL)
            return -1;

        *pws = workspace_create(shell);
        if (*pws == NULL)
            return -1;
    }
    activate_workspace(shell, 0);
    // ...

(2) layer 平面化

有了第一点 desktop_shellweston_compositor 数据绑定 的描述,这一步就非常简单了,就操作一下链表就完事了:

void weston_compositor_build_view_list(struct weston_compositor *compositor, struct weston_output *output)
{
    struct weston_view *view, *tmp;
    struct weston_layer *layer;

    // ...
    // (1) 清空 compositor::view_list
    wl_list_for_each_safe(view, tmp, &compositor->view_list, link)
        wl_list_init(&view->link);
    wl_list_init(&compositor->view_list);
    // (2) 遍历 compositor::layer_list, 并将其中的 view 插入到 compositor::view_list
    wl_list_for_each(layer, &compositor->layer_list, link) {
        wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
            view_list_add(compositor, view, output);
        }
    }
    // ...
}

经过了 weston_compositor_build_view_list 就实现了 layer 的平面化 (逻辑意义上的).

请添加图片描述

Tips : weston_compositor_build_view_list 何时调用? 每次 UI 刷新都调用,不使用缓存,从而确保图层关系正确刷新.

(3) 切换工作空间

快捷键 WIN + Fx

// file : desktop-shell/shell.c
void update_workspace(struct desktop_shell *shell, unsigned int index, struct workspace *from /* 原来的工作空间 */, struct workspace *to /* 现在的工作空间 */)
{
    shell->workspaces.current = index;
    weston_layer_set_position(&to->layer, WESTON_LAYER_POSITION_NORMAL);  // 添加至 weston_compositor::layer_list
    weston_layer_unset_position(&from->layer);                            // 从 weston_compositor::layer_list 中移除
}

(4) NORMAL layer 工作区域计算

// file :esktop-shell/shell.c
void
get_output_work_area(struct desktop_shell *shell,
            struct weston_output *output, /* 存储屏幕宽高 */
            pixman_rectangle32_t *area)
{
    int32_t panel_width = 0, panel_height = 0;

    if (!output) {
        area->x = 0;
        area->y = 0;
        area->width = 0;
        area->height = 0;

        return;
    }

    area->x = output->x;
    area->y = output->y;

    // 获取任务栏宽高
    get_output_panel_size(shell, output, &panel_width, &panel_height);

    switch (shell->panel_position) /* 任务栏其实有上下左右四个位置,默认为上 */ {
    case WESTON_DESKTOP_SHELL_PANEL_POSITION_TOP:
    default:
        area->y += panel_height;
        /* fallthrough */
    case WESTON_DESKTOP_SHELL_PANEL_POSITION_BOTTOM:
        area->width = output->width;
        area->height = output->height - panel_height;
        break;
    case WESTON_DESKTOP_SHELL_PANEL_POSITION_LEFT:
        area->x += panel_width;
        /* fallthrough */
    case WESTON_DESKTOP_SHELL_PANEL_POSITION_RIGHT:
        area->width = output->width - panel_width;
        area->height = output->height;
        break;
    }
}

(5) 最大化实现

void
shell_configure_fullscreen(struct shell_surface *shsurf)
{
    // ...
    // 将 weston view 从之前绑定的 layer 删除(如果存在的话),一般来说应该是处于某个 workspace 中的 normal layer
    weston_layer_entry_remove(&shsurf->view->layer_link);
    // 并将其转移到 fullscreen layer
    weston_layer_entry_insert(&shsurf->shell->fullscreen_layer.view_list,
                    &shsurf->view->layer_link);
    // ...
}

(6) 最小化实现

void set_minimized(struct weston_desktop_surface *desktop_surface, struct weston_surface *surface)
{
    // ...
    // 从 workspace layer 中移除
    weston_layer_entry_remove(&view->layer_link);
    // 将其缓存至 desktop_shell 中的 minimized_layer
    weston_layer_entry_insert(&shsurf->shell->minimized_layer.view_list, &view->layer_link);
    // 并将输入设备的焦点从其移除 (不在本章讨论范围之内)
    drop_focus_state(shsurf->shell, current_ws, view->surface);
    surface_keyboard_focus_lost(surface);
    // ...
}

八、结语

实际上本章只是初步讲解了一下有关于窗口管理相关的一些数据结构,并利用这些数据结构描述了一些具体场景;实际上的业务场景还将更加复杂,例如在本章最后一个最小化实现的例子中出现了焦点的概念,以及view还是套娃的结构,具有 sub view,同时 view 还要跟 surface 扯上关系,各种各样的;如果全部描述清楚工作量是相当大的,并且没有什么太大的价值;本文的职责实际就是一个引子而已,减低入门门槛罢了.

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

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

相关文章

关于grpc的第一次尝试

自己瞎琢磨的&#xff0c;有错勿怪。 一、rpc理解 微服务会在不同的端口使用不同的语言提供相异的服务&#xff0c;端口之间的通信就使用rpc。这边的grpc的“g”我原先意味是golang&#xff0c;后来发现是google。 在golang关于rpc的官方包中&#xff0c;rpc主要有使用http/…

(7)Qt中的自定义槽(函数)

目录 槽函数定义的规则 关联信号与槽错误的示例 类成员函数函数做槽函数使用 静态类成员函数做槽函数使用 全局函数做槽函数使用 lambda表达式做槽函数使用 使用lambda表达式的注意事项 注意事项一&#xff1a; 注意事项二&#xff1a; 槽函数定义的规则 1.槽函数返回值…

多图解析KMP算法原理

KMP是什么 KMP是一种字符串匹配算法&#xff0c;能够判断字符串s2&#xff0c;是否为字符串s1的子串 例如&#xff1a;s1 "abd123def"&#xff0c;s2 "123"&#xff0c;KMP会返回4&#xff0c;代表s2是s1的子串&#xff0c;第一个匹配的下标为3 假设s…

线程池及源码分析

目录 1 java构建线程的方式 2 线程池的7个参数 3 线程池属性标识&线程池的状态 3.1 核心属性 3.2 线程池的状态 4 线程池的执行流程 5 添加工作线程的流程 6 Worker的封装&后续任务的处理 1 java构建线程的方式 一般就3~4种&#xff1a; 继承Thread&#xff…

迎接新年,暂且用Python绘制几个中国结吧

前言 今天就来分享几个用python绘制的图案吧 马上就要迎来新年了 就绘制了几个中国结&#xff0c;嘿嘿 话不多说&#xff0c;直接展示一下代码和效果图吧 更多学习资料与源码点击文章末尾名片领取 1. 效果图&#xff1a; 代码展示 import turtle turtle.screensize(600,…

GPDB插件安装工具之gppkg

gppkg命令gppkg是一个python3编写的打包脚本&#xff0c;在整个集群中安装.gppkg格式的Greenplum数据库扩展&#xff08;例如PL/Java、PL/R和MADlib&#xff09;及其依赖项&#xff0c;位于/usr/local/cloudberry-db/bin/gppkg(自己安装的gpdb目录)&#xff0c;安装到$GPHOME里…

1个寒假能学多少网络安全知识?

现在可以看到很多标题都声称三个月内就可以转行网络安全领域&#xff0c;并且成为月入15K的网络工程师。那么&#xff0c;这个寒假的时间能学多少网络安全知识&#xff1f;是否能入门网络安全工程师呢&#xff1f; 答案是肯定的。 虽然网络完全知识是一门广泛的学科&#xff…

ccc-sklearn-13-朴素贝叶斯(1)

朴素贝叶斯 一种直接衡量标签和特征之间概率关系的有监督学习算法&#xff0c;专注分类的算法&#xff0c;基于概率论和数理统计的贝叶斯理论。在计算的过程中&#xff0c;假设特征之间条件独立&#xff0c;不进行建模&#xff0c;采用后验估计。 sklearn中的朴素贝叶斯 类含…

1-选择题练手

1.采用递归方式对顺序表进行快速排序&#xff0c;下列关于递归次数的叙述中&#xff0c;正确的是 A.每次划分后&#xff0c;先处理较长的分区可以减少递归次数 B.递归次数与初始数据的排列次序无关 C.每次划分后&#xff0c;先处理较短的分区可以减少递归次数 D.递归次数与…

DaVinci:键 - 外部蒙版

调色页面&#xff1a;键Color&#xff1a;Key在调色页面&#xff0c;可以轻松地从媒体池将某个片段拖至节点面板中&#xff0c;以作为外部蒙版。或者&#xff0c;在节点上右击选择“添加蒙版” Add Matte。若无附加&#xff0c;则可以选择本节点片段的明度信息作为外部蒙版。当…

hbase2.x orphan regions on filesystem(region丢失)问题修复

问题描述&#xff1a;orphan regions on filesystem 可以通过主master web页面的HBCK Report查看 也可以通过hbck2工具查看 # 查看指定表 hbase hbck -j $HBASE_HOME/lib/hbase-hbck2-1.3.0-SNAPSHOT.jar addFsRegionsMissingInMeta default:tableName # 查看命名空间下所有…

Yolov5+TensorRT-生成dll-python/c++调用dll

YOlov5-6.0TensorRTdllpython/c调用简介1.项目环境2.TensorRT验证1.在tensorrtx-yolov5-v6.0\yolov5目录下新建build目录2.编写CMake.txt,根据自己目录更改2&#xff08;OpenCV_DIR&#xff09;、3&#xff08;TRT_DIR&#xff09;、10&#xff08;Dirent_INCLUDE_DIRS&#xf…

LabVIEW网络服务器何使用,有哪些不同

LabVIEW网络服务器何使用&#xff0c;有哪些不同NI有几款不同的Web服务器&#xff0c;可使用不同的产品并覆盖不同的用例。它们具有非常相似的名称&#xff0c;可以互换使用&#xff0c;但每个都提供不同的功能。应用程序Web服务器描述&#xff1a;NI应用Web服务器加载使用LabV…

企业微信商户号是什么?如何开通?

企业微信作为一款优秀的移动办公工具&#xff0c;与微信全方位打通&#xff0c;既可以与客户沟通交流&#xff0c;也可以在达成交易后直接进行对公收款&#xff0c;但是前提是要开通企业微信商户号。前言企业微信和微信都出自腾讯&#xff0c;而且企业微信全方位连接微信&#…

C#,图像二值化(16)——全局阈值的力矩保持算法(Moment-proserving Thresholding)及其源代码

1、力矩保持法 提出了一种基于矩保持原理的自动阈值选择方法。以这样的方式确定地计算阈值&#xff0c;即在输出画面中保留输入画面的时刻。实验结果表明&#xff0c;该方法可以将给定的图像阈值化为有意义的灰度级。该方法描述了全局阈值&#xff0c;但也适用于局部阈值。 A…

企业微信开发——企业内部自建应用开发(第二篇)---JS_SDK配置

企业微信如果想要使用企业微信的JS_SDK来实现拍照、定位等等功能&#xff0c;就需要预先在使用到的页面进行配置&#xff0c;当然你可以做全局配置。对于JS_SDK的配置设计前端和后端的统一配置。下面我来说明下具体的步骤。特别说明&#xff1a;1、企业微信有的接口需要配置wx.…

shader基础入门(1)

本文基于unity免费公开课“Hi Shader以及网络公开资料等书写”遵循开源协议。 MeshFilter网格过滤器 从海量资源中挑选适合的Mesh将他交给MeshRender MeshRenderer 网格渲染器 负责把MeshFilter丢过来的Mesh&#xff0c;绘制显示到我们的场景中 Material 材质球 Material…

多线程之死锁

目录&#xff1a; 1.什么是死锁&#xff1f; 2.可重入与不可重入 3.发生死锁的三个典型情况 4.发生死锁的四个必要条件 5.如何破除死锁&#xff1f; 1.什么是死锁&#xff1f; 谈到死锁&#xff0c;程序猿们都心存忌惮&#xff0c;因为程序一旦出现死锁&#xff0c;就会导…

深度学习训练营之鸟类识别

深度学习训练营之鸟类识别原文链接环境介绍前置工作设置GPU导入数据并进行查找数据处理可视化数据配置数据集残差网络的介绍构建残差网络模型训练开始编译结果可视化训练样本和测试样本预测原文链接 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&am…

机器学习:如何解决类别不平衡问题

类别不平衡是一个常见问题&#xff0c;其中数据集中示例的分布是倾斜的或有偏差的。 1. 简介 类别不平衡是机器学习中的一个常见问题&#xff0c;尤其是在二元分类领域。当训练数据集的类分布不均时会发生这种情况&#xff0c;从而导致训练模型存在潜在偏差。不平衡分类问题的示…