← 返回信息流
AI 资讯Hacker News·7 天前

在越狱Kindle上运行Rust(及Slint)

原标题:Rust (and Slint) on a Jailbroken Kindle

速览

本文介绍了如何在越狱的Kindle设备上编译和运行Rust程序。通过结合Slint图形界面库,作者成功实现了在Kindle上构建原生图形应用。这一实践证明了Rust在资源受限嵌入式设备上的强大潜力,为Kindle用户提供了扩展功能的新技术路径。

AI 深度解读

在越狱 Kindle 上运行 Rust 与 Slint:从“Hello World”到电子墨水屏仪表盘

背景

作者最近对其第七代 Kindle Paperwhite 进行了越狱操作。虽然表面上看,动机似乎是想要摆脱亚马逊日益收紧的控制,但更实际的需求是希望将这台设备作为床头柜上的时钟使用。

在寻找解决方案时,作者发现了一个现有的项目,并通过对代码进行简单调整成功实现了时钟功能。然而,既然已经打开了这扇门,作者开始思考是否可以在 Kindle 上运行 Rust 语言,从而开发更具实用价值的功能。鉴于作者近期重新开始了 Home Assistant 和智能家居设备的折腾,一个用于展示部分功能的仪表盘(Dashboard)成为了一个有趣的潜在项目。

正如 Pete Cordell 所言:“告诉程序员 X 已经有库可用,就像告诉词曲作家爱已经有歌可唱一样。”作者决定不依赖现成的方案,而是亲自探索在资源受限的 Kindle 上构建 Rust GUI 应用的技术路径。

核心内容

1. 跨平台编译 Rust 环境搭建

Kindle 基于 ARM 架构,且使用 musl libc。作者指出,直接在 Kindle 这种低功耗设备上配置 Rust 编译工具链是极其困难且不可行的。因此,跨平台编译(Cross-compilation)是必经之路。

作者推荐使用 cargo-zigbuild 作为跨平台编译工具。Zig 编译器内置了所有支持架构的 musl libc 源代码和头文件,并拥有自己的链接器。这意味着 zig cc 可以作为任何 musl 目标的完整跨平台编译工具链。

具体的编译步骤如下:

  1. 安装 Zig。
  2. 安装 cargo-zigbuild
  3. 执行命令:cargo zigbuild --release --target armv7-unknown-linux-musleabihf

2. 获取 Kindle 的 Shell 访问权限

编译好的二进制文件需要传输到 Kindle 并运行。虽然可以通过越狱时安装的 KUAL 运行程序,但为了查看 stdout 以验证应用是否正常工作,作者需要 SSH 访问权限。

作者使用了 USBNetwork 工具,该工具允许通过 USB 或 Wi-Fi 设置设备的 SSH 访问。为了方便连接,作者在 SSH 配置文件中添加了条目并复制了公钥。需要注意的是,标准的 ssh-copy-id 命令在 Kindle 上不起作用,作者必须手动将 .pub 文件添加到 Kindle 上的 /mnt/us/usbnet/etc/authorized_keys 文件中。

3. 图形界面选择:Slint

虽然 Rust 生态中出现了许多成熟的 GUI 库,但作者仅熟悉 Slint,因此选择了它。基于之前在树莓派(Raspberry Pi)上运行 Slint 的经验,作者确认 ARMv7 平台是原生支持的。接下来的挑战在于如何将输出连接到电子墨水屏(e-ink),以及如何接收触摸屏的输入。

4. 实现屏幕输出:Framebuffer 操作

Slint 支持多种渲染器和后端,其中包括一个轻量级的软件渲染器,几乎适用于任何平台。作者通过实现 LineBufferProvider 接口中的 process_line() 函数,逐行获取光栅化的视觉输出。

处理流程如下:

  1. 将每一行输出转换为灰度图像。
  2. 将其写入帧缓冲区(framebuffer)。在 Kindle 上,帧缓冲区表现为一个内存映射的文件 /dev/fb0。这体现了 Linux “一切皆文件” 的设计哲学。
  3. 通知驱动刷新显示。由于电子墨水屏的特性,必须通过 libc crate 调用 ioctl()(输入/输出控制)来刷新屏幕。Slint 内部提供了需要刷新的脏区域(dirty region),将其传递给 ioctl 即可完成刷新。

5. 实现触控输入:事件流解析

屏幕显示只是拼图的一半,另一半是让触摸屏与 Slint 通信。同样遵循“一切皆文件”的原则,触摸控制器在系统中表现为 /dev/input/event1

作者直接对该文件进行 read() 操作。内核将事件结构体直接写入缓冲区,无需解析复杂协议,只需匹配内存布局即可。每个事件包含时间戳、事件类型、代码和值。

Kindle 使用 Linux 内核的多点触控协议 Type B。事件以流的形式到达,例如:“X 坐标现在是...”、“Y 坐标现在是...”、“跟踪 ID 现在是...”,最后是一个 SYNC_REPORT 事件,表示该批次数据结束。

逻辑处理如下:

  • 随着事件到来,累积最新的 X、Y 坐标和跟踪 ID。
  • 遇到 SYNC_REPORT 时,决定如何向 Slint 分发事件。
  • 如果跟踪 ID 为 -1,表示手指抬起,触发 PointerReleased
  • 否则,触摸后的第一个同步事件触发 PointerPressed,后续事件触发 PointerMoved
  • Slint 负责处理剩余的 UI 逻辑。

6. 成果与后续

经过大量的调试(解决无输出、屏幕不刷新、双重刷新闪烁、触控无响应或重复响应等问题),作者最终实现了一个带有计数器和增加按钮的简单应用。

作者将针对 Kindle 的后端代码提取为一个独立的 crate,并发布到了 crates.io 上。目前,Slint 的 Kindle 后端已基本可用(尽管可能需要针对不同版本的 Kindle 进行微调)。接下来的工作将是绘制“猫头鹰的其余部分”,即构建完整的仪表盘界面,但这将是未来的另一个项目。

关键要点

  • 跨平台编译是关键:在 Kindle 这种资源受限设备上,直接在设备上进行 Rust 编译是不现实的,必须使用 cargo-zigbuild 等工具在主机上进行针对 armv7-unknown-linux-musleabihf 的交叉编译。
  • SSH 访问的特殊配置:Kindle 的 USBNetwork 工具不支持标准的 ssh-copy-id,需要手动将公钥追加到 /mnt/us/usbnet/etc/authorized_keys
  • Slint 的轻量级优势:Slint 的软件渲染器非常适合嵌入式场景,通过实现 LineBufferProvider 可以灵活地将渲染结果写入 Linux 帧缓冲区。
  • Linux 哲学的应用
    • 输出:利用 /dev/fb0 内存映射文件直接写入屏幕像素数据。
    • 输入:通过读取 /dev/input/event1 文件获取内核生成的原始触控事件结构体。
  • 多点触控协议处理:Kindle 使用 Type B 多点触控协议,开发者需要手动累积 X/Y 坐标和跟踪 ID,并在收到 SYNC_REPORT 时转换为 Slint 的指针事件(Press/Move/Release)。
  • 开源贡献:作者将通用的 Kindle 后端逻辑封装为独立的 Rust crate 并发布至 crates.io,为其他开发者提供了基础组件。

意义与影响

  • 拓展老旧硬件的生命周期:该项目展示了如何通过软件手段(越狱、交叉编译、底层驱动交互)赋予老旧电子设备(如 Kindle Paperwhite)新的功能,使其从单纯的阅读器转变为智能家居仪表盘或时钟。
  • Rust 在嵌入式 GUI 领域的潜力:证明了 Rust 及其新兴 GUI 框架 Slint 可以在资源极其有限的 ARM 嵌入式设备上运行,并处理复杂的图形渲染和输入事件。这对于希望使用 Rust 进行嵌入式开发的开发者具有参考价值。
  • Linux 底层机制的直观应用:文章详细展示了如何利用 Linux 的 /dev/fb0/dev/input 机制,为那些希望深入理解 Linux 图形栈和输入子系统的开发者提供了具体的实践案例。
  • 社区驱动的创新:尽管存在许多现成的智能家居仪表盘项目,但作者选择从零开始构建,这种“造轮子”的过程不仅解决了个人需求,还通过开源代码回馈了社区,降低了后来者的入门门槛。
查看原文 →sverre.me