將 Rust 與 Slint 帶入越獄後的 Kindle

對於許多開發者來說,重新利用舊硬體的衝動是一種難以抗拒的渴望。無論是將被遺忘的平板電腦變成智慧家庭儀表板,還是將專用的電子閱讀器變成床頭時鐘,目標往往不在於最終產品的實用性,而是在於過程中的挑戰。

最近,一個專案展示了如何透過利用 Rust 和 Slint GUI 框架,從第 7 代 Kindle Paperwhite 的限制性生態系統中解脫出來。這項努力將一個鎖定的電子墨水屏設備轉變為一個可程式化的畫布,為任何想要在舊有的 ARMv7 硬體上執行現代、具備記憶體安全性的程式碼的人提供了藍圖。

交叉編譯的挑戰

在像 Kindle 這樣低功耗的設備上執行 Rust,面臨著首要障礙:編譯工具鏈。由於資源有限,嘗試直接在設備上進行編譯是行不通的。

解決方案是交叉編譯,目標設定為 armv7-unknown-linux-musleabihf

雖然有幾種方法可以實現這一點,但 cargo-zigbuild 已成為此特定任務的高效工具。因為 Zig 編譯器附帶了所有支援架構的 musl libc 源碼和標頭檔,並包含了自己的連結器,因此它可以作為一個完整的交叉編譯工具鏈。該過程可以簡化為三個基本步驟:

  1. 安裝 Zig。
  2. 安裝 cargo-zigbuild
  3. 執行 cargo zigbuild --release --target armv7-unknown-linux-musleabihf

社群中的其他開發者也注意到使用 rust-lld 和特定的 .cargo/config.toml 設定來建立自包含的二進位檔,也能取得類似的成功,儘管這種方法可能會限制 C 依賴項的使用。

建立設備存取權

一旦二進位檔編譯完成,就必須進行部署和執行。雖然在越獄過程中可以使用 KUAL (Kindle Unified Application Launcher) 等工具,但它們對於開發來說並不充足,因為它們無法提供用於除錯的 stdout 存取權。

為了達成這個目標,可以使用 USBNetwork 工具透過 USB 或 Wi-Fi 設定 SSH 存取。這允許傳輸公鑰,並能夠在 shell 中執行應用程式,從而提供迭代程式碼所需的即時回饋。

在電子墨水屏上實作 GUI

一個「Hello World」應用程式可以證明工具鏈是有效的,但 Kindle 的真正潛力在於其螢幕。為了建立視覺介面,選擇了 Slint GUI 函式庫,因為它支援 ARMv7 並具有輕量級的軟體渲染器。

渲染至 Framebuffer

遵循 Linux 的哲學「萬物皆檔案」,Kindle 的顯示器可以透過 /dev/fb0 的 framebuffer 進行存取。透過在 Slint 中實作 LineBufferProvider,應用程式可以逐行取得光柵化視覺輸出,將其轉換為灰階,並直接寫入記憶體映射的 framebuffer。

然而,寫入 framebuffer 並不足夠;電子墨水屏需要一個明確的觸發訊號來刷新物理顯示器。這部分透過使用 libc crate 和 ioctl() (input/output control) 來通知驅動程式刷新螢幕的「髒區域」(dirty region)。

處理觸控輸入

輸入處理與輸出處理類似。觸控控制器被暴露為 /dev/input/event1 的一個檔案。透過讀取此檔案,應用程式可以接收遵循 Linux 核心的 multi-touch protocol type B 的數據流。

該協定規定的事件為一系列座標 (X 和 Y) 與追蹤 ID,並以 SYNC_REPORT 結束。將其與 Slint 整合的邏輯涉及:

  • PointerPressed: 由觸控按下後的第一次同步報告來觸發。

  • PointerMoved: 由隨後的同步報告來觸發。

  • PointerReleased: 當追蹤 ID 為 -1 時觸發,表示手指已抬起。

結論與社群影響

透過將此邏輯提取到一個獨立的 crate,slint-backend-kindle,為 Kindle 建立 GUI 的過程變得民主化了。最初只是想做一個床頭時鐘的簡單願望,演變成了一個功能齊全的 GUI 後端,讓其他開發者可以嘗試電子墨水屏儀表板和自定義應用程式。

正如社群所指出的,電子墨水屏的「復古感」使得這些專案變得獨特且令人滿足。無論是建立一個自託管的圖書庫伺服器,還是是在 RISC-V 上建立一個可攜式音訊播放器,現代語言如 Rust 和 Zig 與舊有硬體的結合,繼續是個技術探索的技術沃土。

Sources