LVGL多显示器:明确屏幕、输入设备与资源归属
创建控件之前,先列出显示归属
双屏设备把本应给操作员的对话框显示在顾客屏幕,随后一个面板的触摸又改动另一个面板的控件。这类问题通常来自隐式默认值和不完整的所有权映射。本文采用LVGL 9.2,并按v9.2.2核对接口及默认显示器行为,关注资源归属,不展开Linux后端选择或通用线程安全设计。
为显示器A、B分别列出lv_display_t句柄、分辨率、颜色格式、物理传输、绘图缓冲区、活动屏幕、输入设备、焦点组和电源状态。使用稳定的业务名称,不要只叫“第一个”和“第二个”。初始化顺序可能变化,但不应因此悄悄改变对话框或触摸事件的目的地。

默认显示器是可变上下文
9.2显示概览介绍了每显示器屏幕和默认显示器概念。v9.2.2实际实现创建另一显示器时,会临时选中它以建立内部屏幕,随后恢复原默认值;只有尚无默认显示器时,首个实例才成为默认。应用不能把“最后创建者”当作可靠路由策略。
使用lv_obj_create(NULL)建立根屏幕时,应明确通过lv_display_set_default()选择归属,然后恢复或记录默认上下文。普通控件更适合直接使用已经属于目标屏幕的显式父对象。共享组件构造函数接收parent参数,不要在内部随意调用lv_screen_active()。读取指定显示器活动根对象时,采用固定版本接口中的lv_display_get_screen_active(disp)。
屏幕归属与屏幕加载不是一回事
屏幕是关联某个显示器的根对象,不是可以随意加载到任意面板的通用页面。在v9.2.2中,lv_screen_load()通过加载实现沿着传入屏幕的所属显示器操作。因此应确认屏幕创建时归属正确,不能指望加载前临时切换默认值,就修复已经错误的关联。
同一个业务视图要在两块屏幕显示,可以共享底层模型,但分别创建两棵具有各自父对象的控件树。这样也能适应不同分辨率、文字大小及交互规则。像素镜像是另一种设计:一份渲染输出复制到多个传输目标。不要混用独立显示器与简单镜像的假设,必须说明谁负责渲染,以及每个输出端何时结束读取像素。
输入与焦点要显式绑定
使用lv_indev_set_display()把指针输入设备关联到目标显示器,v9.2.2输入接口也提供读取关联关系的函数。触摸校准、旋转和坐标转换必须对应目标面板的分辨率及方向。I2C读值正确,不代表界面事件正确,因为坐标可能被解释到另一显示器空间。
键盘和编码器还需要明确焦点组策略。把设备分配到目标组,并只加入它应操作的控件。显示关联与组成员解决相关但不同的路由问题。共享键盘可以切换目标,但切换时应同步更新焦点所有权,并处理旧交互状态。尤其要测试按住按键或触摸时切换目标,避免释放事件意外激活另一块屏幕。
缓冲区与完成回调保持句柄一致
每个显示器都需要经过审查的缓冲区和flush配置。9.2显示接口把显示器句柄传入flush回调,应将这个身份保留到异步传输结束,并在适当时机对同一个显示器调用lv_display_flush_ready()。如果第二块屏幕在第一笔传输结束前开始工作,全局“当前显示器”变量就可能指错对象。
独立缓冲区是较容易证明的所有权模型。共享缓冲区或总线则需要跨显示器串行化,并保证所有消费者结束前不复用像素。不同分辨率或颜色格式,可能具有不同大小及对齐要求。逐个计算绘图缓冲区和临时渲染资源,不能只把一个猜测的帧缓冲大小乘二。关闭一块面板时,也不能误停另一块的完成处理和输入服务。
空闲与删除按显示器分别处理
空闲时间按显示器跟踪。文档指出一个特殊情况:lv_display_get_inactive_time(NULL)返回多个显示器中的最短空闲时间,不是只查询默认对象。针对一块屏幕实施休眠策略时,应显式传入对应句柄。看起来相似的NULL约定不一定一致,需要读具体接口,而不是推广一条全局规则。
删除或替换显示器前,停止外部生产源,处理完待完成传输,再退役引用它的输入关联和回调。屏幕对象也要按生命周期管理,包括不能直接删除当前已加载屏幕的限制。删除接口不是通用物理热拔插协议,应用驱动及输入寿命必须保证迟到完成事件不再使用已移除句柄。
用不对称配置验证
建议故意采用不同分辨率,并显示明显的A、B标记,因为两块完全相同的面板可能掩盖映射错误。通过共享组件构造函数打开对话框,改变默认显示器,确认使用显式父对象的组件仍落在预期位置。只旋转或休眠一块面板,同时让另一块继续更新数据。
分别注入触摸和编码器输入,再在手势未结束时切换目标。延迟一块屏幕的flush完成,让另一块继续刷新,全程断言屏幕归属、输入关联、回调身份和缓冲区寿命。本文未执行双屏模拟器或板级测试。验收目标是一张能够经受初始化变化、独立电源状态和异步完成的所有权映射,而不只是两块屏幕碰巧都能亮。