首页 > 其他分享 >Slint-UI移植到任意平台-概述-Rust

Slint-UI移植到任意平台-概述-Rust

时间:2024-01-28 18:56:37浏览次数:31  
标签:mut platform window let Slint UI event Rust slint

前言

本文仅为笔者记忆,个人经验写着玩,目前1.3.2版本。

注:本文尚未完成。
注:本文尚未完成。
注:本文尚未完成。

本人目前想要移植一种贴近前端技术的GUI到裸机上,但是裸机不支持UNIX环境,所以绝大部分框架都用不了(如Flutter/skia等),最后发现Slint最合适。

Slint有三种渲染器{femtovg/OpenGL ,skia ,software },由于本文移植目标是裸机,所以采用CPU/MCU完成这个过程,选用软件渲染software

Slint官方文档

  1. Slint模板语法,类似于 HTML
    https://slint.dev/releases/1.3.2/docs/slint/
  2. MCU移植指南(重要):https://slint.dev/releases/1.3.2/docs/rust/slint/docs/mcu/
  3. MCU示例工程(SPI/IO等),主要侧重于使用Slint:
    https://github.com/slint-ui/slint-mcu-rust-template
  4. MCU移植示例,侧重于移植Slint
    https://github.com/slint-ui/slint/tree/master/examples/printerdemo_mcu

本文研究的目标

我们就研究
https://github.com/slint-ui/slint/tree/master/examples/printerdemo_mcu
及其依赖的 mcu-board-support = { path = "../mcu-board-support" }
image

首先我们去它的目录 ../mcu-board-support看看,选择我们熟悉的芯片平台来看即可,我选择stm32
image

这里传递了 StmBackend,由于Rust没有c那种先声明才能使用的规定,所以继续看下面。看看StmBackend的声明和定义。

image
image
PS:源码里的RNG我们先不管,RNG硬件随机数发生器,是STM32的片上外设硬件。

可以看到,StmBackend实现了两个行为(Rust里面叫Trait:定义共同行为; Cpp和Java叫做继承,Golang叫接口实现)

我们只需要实现这些行为,即可完成移植slint的大部分工作(即完成HAL层),剩下的底层就是纯裸机Bringup开发了。

impl slint::platform::Platform for StmBackend 实现如下:

impl slint::platform::Platform for StmBackend {
    fn create_window_adapter(
        &self,
    ) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
        let window = slint::platform::software_renderer::MinimalSoftwareWindow::new(
            slint::platform::software_renderer::RepaintBufferType::SwappedBuffers,
        );
        self.window.replace(Some(window.clone()));
        Ok(window)
    }

    fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
        let inner = &mut *self.inner.borrow_mut();

        let mut ft5336 =
            ft5336::Ft5336::new(&mut inner.touch_i2c, 0x70 >> 1, &mut inner.delay).unwrap();
        ft5336.init(&mut inner.touch_i2c);

        // Safety: The Refcell at the beginning of `run_event_loop` prevents re-entrancy and thus multiple mutable references to FB1/FB2.
        let (fb1, fb2) = unsafe { (&mut FB1, &mut FB2) };

        let mut displayed_fb: &mut [TargetPixel] = fb1;
        let mut work_fb: &mut [TargetPixel] = fb2;

        let mut last_touch = None;
        self.window
            .borrow()
            .as_ref()
            .unwrap()
            .set_size(slint::PhysicalSize::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32));
        loop {
            slint::platform::update_timers_and_animations();

            if let Some(window) = self.window.borrow().clone() {
                window.draw_if_needed(|renderer| {
                    while inner.layer.is_swap_pending() {}
                    renderer.render(work_fb, DISPLAY_WIDTH);
                    inner.scb.clean_dcache_by_slice(work_fb);
                    // Safety: the frame buffer has the right size
                    unsafe { inner.layer.swap_framebuffer(work_fb.as_ptr() as *const u8) };
                    // Swap the buffer pointer so we will work now on the second buffer
                    core::mem::swap::<&mut [_]>(&mut work_fb, &mut displayed_fb);
                });

                // handle touch event
                let touch = ft5336.detect_touch(&mut inner.touch_i2c).unwrap();
                let button = slint::platform::PointerEventButton::Left;
                let event = if touch > 0 {
                    let state = ft5336.get_touch(&mut inner.touch_i2c, 1).unwrap();
                    let position = slint::PhysicalPosition::new(state.y as i32, state.x as i32)
                        .to_logical(window.scale_factor());
                    Some(match last_touch.replace(position) {
                        Some(_) => slint::platform::WindowEvent::PointerMoved { position },
                        None => slint::platform::WindowEvent::PointerPressed { position, button },
                    })
                } else {
                    last_touch.take().map(|position| {
                        slint::platform::WindowEvent::PointerReleased { position, button }
                    })
                };

                if let Some(event) = event {
                    let is_pointer_release_event =
                        matches!(event, slint::platform::WindowEvent::PointerReleased { .. });

                    window.dispatch_event(event);

                    // removes hover state on widgets
                    if is_pointer_release_event {
                        window.dispatch_event(slint::platform::WindowEvent::PointerExited);
                    }
                }
            }

            // FIXME: cortex_m::asm::wfe();
        }
    }

    fn duration_since_start(&self) -> core::time::Duration {
        // FIXME! the timer can overflow
        let val = self.timer.counter() / 10;
        core::time::Duration::from_millis(val.into())
    }

    fn debug_log(&self, arguments: core::fmt::Arguments) {
        use alloc::string::ToString;
        defmt::println!("{=str}", arguments.to_string());
    }
}


HAL层的过程是这样的:

  1. create_window_adapter完成
  2. run_event_loop完成每一次事件处理(包括屏幕刷新/触摸输入等事件)
  • Slint上层software renderer渲染器完成渲染,通过FB1和FB2双缓冲机制将图像传递给 可触摸屏幕主控FT5336(I2C通讯),完成显示。也接收触摸屏事件,完成输入响应。
    image
  1. debug_log完成日志输出接口,其实就是print
  2. duration_since_start是运行时间,通过定时器实现即可。

Slint的官方宣言!

译文:
image

本文未完成。

看完这个STM32的,再去看其他芯片(例如ESP32)的Slint移植代码,会发现其实都差不多。

标签:mut,platform,window,let,Slint,UI,event,Rust,slint
From: https://www.cnblogs.com/yucloud/p/17993104/rust_slint_embed_baremetal

相关文章

  • OpenIM (Open-Source Instant Messaging) Mac Deployment Guide
    Thisguideprovidesstep-by-stepinstructionsfordeployingOpenIMonaMac,includingbothsourcecodeandDockerdeploymentmethods.##PreliminaryEnvironmentSetupEnsureacleanworkingenvironment:1.**CreateaNewDirectory**:Startinanewdirec......
  • Rust学习之Diesel setup报错解决
    Dieselsetup报错解决Diesel是一个安全、可扩展的RustORM和查询生成器。Diesel是Rust中与数据库交互最高效的方式,因为它对查询进行了安全且可组合的抽象。1.报错信息diesel_demoonmaster[?]via......
  • BUIR论文阅读笔记
    这个领域不熟悉,是看的第一篇论文,记录细一点Abstract单类协作过滤(OCCF)的目标是识别出与之呈正相关但尚未交互的用户-项目对,其中只有一小部分积极的用户-项目交互被观察到,对于积极和消极交互的区分建模,以往的工作在一定程度上依赖于负抽样,即将未观察到的用户项目对视为负对,因为实......
  • docker-compose up -d和docker-compose up --build的补充
    docker-composeup-d和docker-composeup--build的补充:https://blog.csdn.net/yang2330648064/article/details/131333689?ops_request_misc=&request_id=&biz_id=102&utm_term=docker-compose%20up%20-d%E7%9A%84%E5%90%AB%E4%B9%89&utm_medium=distribute.pc......
  • MySQL 8.0.26 新增参数 group_replication_view_change_uuid
    MySQL8.0.26新增参数group_replication_view_change_uuidGreatSQL[root@localhost][test]>showglobalvariableslike'group_replication_view_change_uuid';+------------------------------------+-----------+|Variable_name|V......
  • 鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之CheckboxGroup组件
    鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之CheckboxGroup组件一、操作环境操作系统: Windows10专业版、IDE:DevEcoStudio3.1、SDK:HarmonyOS3.1+编辑二、CheckboxGroup组件提供多选框组件,通常用于某选项的打开或关闭。子组件无。接口CheckboxGroup(options?:{group?:string})创......
  • layui 表格前后端总结
    @PostMapping("findAll")@ResponseBodypublicLayuifindAll(Paramparam){QueryWrapper<EduTeacher>wrapper=newQueryWrapper<>();Page<EduTeacher>page=newPage<>(param.getPage(),param.getLimit()......
  • 为什么不推荐用 UUID 作为 Mysql 的主键
    学习改变命运,技术铸就辉煌。大家好,我是銘,全栈开发程序员。UUID是什么我们先来了解一下UUID是什么?UUID是指UniversallyUniqueIdentifier,翻译为中文是通用唯一识别码,UUID的目的是让分布式系统中的所有元素都能有唯一的识别信息。如此一来,每个人都可以创建不与其它人冲突......
  • 为什么不推荐用 UUID 作为 Mysql 的主键
    学习改变命运,技术铸就辉煌。大家好,我是銘,全栈开发程序员。UUID是什么我们先来了解一下UUID是什么?UUID是指UniversallyUniqueIdentifier,翻译为中文是通用唯一识别码,UUID的目的是让分布式系统中的所有元素都能有唯一的识别信息。如此一来,每个人都可以创建不与其它人冲突的UUI......
  • 洛谷题解-P1673 [USACO05FEB] Part Acquisition S
    https://www.luogu.com.cn/problem/P1673题目描述奶牛们接到了寻找一种新型挤奶机的任务,为此它们准备依次经过N(1≤N≤5×104)N(1\leN\le5\times10^4)N(1≤N≤5×104)颗行星,在行星上进行交易。为了方便,奶牛们已经给可能出现的K(1≤K≤103)K(1\leK\le10^3)K(1≤K≤103)......