Raspberry Pi 5 で slint を動かす

# はじめに

この記事は [Slint Advent Calendar 2024](https://qiita.com/advent-calendar/2024/slint) ７日目の記事になります。

昨日は @hermit4 さんによる [Slint言語入門(1) 概要](https://qiita.com/hermit4/items/81cd36d65f33178085e6) でした。

.slint の基礎がまとまっていて必見ですね。

---

本日は、先月 EdgeTech+ 2024 において [合同会社シグナルスロット](https://signal-slot.co.jp/) ブースで展示したデモの中から、選挙の投票端末のデモを動かした時の作業をまとめた記事になります。

![votingmachine.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13867/52990000-fa5c-bd8d-c22c-a31bfda0f268.gif)

# ハードウェア

- [Raspberry Pi 5 / 4GB](https://www.switch-science.com/products/9249)  
- [ラズパイ5に最適なACアダプター 5.1V/5.0A USB-PD Type-Cコネクタ出力](https://www.switch-science.com/products/9345)  
- [7.9インチタッチスクリーン液晶（Raspberry Pi用）](https://www.switch-science.com/products/8317)
- [タッチ液晶](https://www.amazon.co.jp/gp/product/B0CL9K1S18/)  
- SD カード(128GB くらい)

# OS

- [Raspberry Pi OS (bookworm) 64 bit](https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-64-bit)

## 初期設定

```console
$ sudo apt update  
$ sudo apt upgrade  
```

- デフォルトの GUI を起動しないようにする
- 日本語のロケールを使えるようにする

```console
$ sudo raspi-config
```

- 1 System Options  
  - S5 Boot / Auto Login  
    - B1 Console  
- 5 Localisation Options  
  - L1 Locale  
    - \[\*\] ja\_JP.UTF-8 UTF-8  
    - ja\_JP.UTF-8

```console
$ sudo systemctl disable cups  
$ sudo systemctl disable bluetooth
```

### スワップファイルの設定

Rust のビルド（リンク時）にメモリをたくさん使うのでスワップを増やしておきます。
8GB 版ではこの作業は必要ありませんでしたが、4GB 版では必要でした。

```console
$ sudo vi /etc/dphys-swapfile  
```

```text
CONF\_SWAPSIZE=2000
```

```console
sudo reboot  
```

## フォント

```console
$ wget https://github.com/adobe-fonts/source-han-sans/releases/download/2.004R/SourceHanSansJ.zip  
$ unzip SourceHanSansJ.zip  
$ mkdir -p ~/.fonts  
$ mv OTF/Japanese/*.otf ~/.fonts/  
$ fc-cache -v -f ~/.fonts  
$ rm -rf SourceHanSansJ.zip OTF/
```

# ソフトウェア

## Rust

[https://www.rust-lang.org/ja/tools/install](https://www.rust-lang.org/ja/tools/install)

```conslle
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh  
$ . "$HOME/.cargo/env"
```

## Slint

[https://github.com/slint-ui/slint/blob/master/docs/building.md](https://github.com/slint-ui/slint/blob/master/docs/building.md)


### 依存パッケージのインストール

```console
$ sudo apt install libssl-dev  
$ sudo apt install libinput-dev libfontconfig-dev libgbm-dev libxkbcommon-dev libudev-dev libseat-dev git
```

### slint のソースコードをクローン

```console
$ git clone https://github.com/slint-ui/slint
$ cd slint  
$ git show
commit 10de1e664be2d1328b1e5e780897af497d78c077 (HEAD -> master, origin/master, origin/HEAD)
Author: Tobias Hunger <tobias.hunger@slint.dev>
Date:   Tue Oct 22 14:02:16 2024 +0200

    live-preview: Default to "native" as expected (if that is available!)

diff --git a/tools/lsp/preview/ui.rs b/tools/lsp/preview/ui.rs
index e7f5b07c4..330af889b 100644
--- a/tools/lsp/preview/ui.rs
+++ b/tools/lsp/preview/ui.rs
@@ -35,7 +35,12 @@ pub fn create_ui(style: String, experimental: bool) -> Result<PreviewUi, Platfor
     let style = if known_styles.contains(&style.as_str()) {
         style
     } else {
-        known_styles.first().map(|s| s.to_string()).unwrap_or_default()
+        known_styles
+            .iter()
+            .find(|x| **x == "native")
+            .or_else(|| known_styles.first())
+            .map(|s| s.to_string())
+            .unwrap_or_default()
     };

     let style_model = Rc::new({
```

EdgeTech+ の準備をはじめた１ヶ月半前時点の master で試しています。

X11 や Wayland ではなく、linuxkms で表示をするために *雑に* 以下の変更をしました。

（[meta-slint](https://github.com/slint-ui/meta-slint/blob/21acd61d2c32030d8eeaaed942ab7b192fad4064/recipes-slint/slint/slint-cpp-v2.inc#L39) ではもう少しいい方法をとっている気がする）

```diff
diff --git a/Cargo.toml b/Cargo.toml
index 4422c7751..28dc9f002 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -70,28 +70,11 @@ members = [
 default-members = [
     'api/rs/build',
     'api/rs/slint',
-    'examples/gallery',
-    'examples/memory',
-    'examples/printerdemo_old/rust',
-    'examples/printerdemo/rust',
-    'examples/slide_puzzle',
-    'examples/todo/rust',
-    'examples/virtual_keyboard/rust',
-    'examples/carousel/rust',
-    'examples/energy-monitor',
-    'internal/backends/winit',
-    'internal/backends/qt',
-    'internal/backends/selector',
+    'internal/backends/linuxkms',
     'internal/compiler',
     'internal/core',
     'internal/interpreter',
-    'tests/doctests',
-    'tests/driver/interpreter',
-    'tests/driver/rust',
-    'tests/screenshots',
     'tools/compiler',
-    'tools/figma_import',
-    'tools/lsp',
     'tools/viewer',
 ]

diff --git a/internal/backends/linuxkms/Cargo.toml b/internal/backends/linuxkms/Cargo.toml
index f0486d085..e31beaf95 100644
--- a/internal/backends/linuxkms/Cargo.toml
+++ b/internal/backends/linuxkms/Cargo.toml
@@ -23,8 +23,7 @@ renderer-femtovg = ["i-slint-renderer-femtovg", "drm", "gbm", "glutin", "raw-win
 renderer-software = ["i-slint-core/software-renderer-systemfonts", "drm", "dep:bytemuck", "dep:memmap2"]
 libseat = ["dep:libseat"]

-#default = ["renderer-skia", "renderer-femtovg"]
-default = []
+default = ["renderer-skia", "renderer-femtovg", "renderer-skia-opengl"]

 [dependencies]
 i-slint-core = { workspace = true, features = ["default", "image-decoders", "svg"] }
diff --git a/internal/backends/selector/Cargo.toml b/internal/backends/selector/Cargo.toml
index 38528ae24..95f493306 100644
--- a/internal/backends/selector/Cargo.toml
+++ b/internal/backends/selector/Cargo.toml
@@ -38,7 +38,7 @@ raw-window-handle-06 = ["i-slint-core/raw-window-handle-06", "i-slint-backend-wi
 system-testing = ["i-slint-backend-testing/system-testing"]

 # note that default enable the i-slint-backend-qt, but not its enable feature
-default = ["i-slint-backend-qt", "backend-winit"]
+default = ["i-slint-backend-linuxkms"]

 [dependencies]
 cfg-if = "1"
```

### slint のビルド

.slint ファイルを表示するのに必要な slint-viewer をビルドします。

```console
$ export SLINT_BACKEND=linuxkms-skia-opengl  
$ cargo build --release --bin slint-viewer
```

### Demo

```console
$ git clone https://github.com/signal-slot/votingmachine.git --branch v1.0.0
$ /home/pi/slint/target/release/slint-viewer ./votingmachine/slint/main.slint
```

![votingmachine.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13867/52990000-fa5c-bd8d-c22c-a31bfda0f268.gif)

# 自動起動

```console
$ sudo vi /etc/systemd/system/votingmachine.service
```

```text
[Unit]
Description="Slint Service"
After=getty@tty1.service

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/carpark/slint/
ExecStart=/home/pi/slint/target/release/slint-viewer /home/pi/carpark/slint/main.slint
Environment=SLINT_BACKEND=linuxkms-skia-opengl
Restart=always

[Install]
WantedBy=multi-user.target
```

```console
$ sudo systemctl daemon-reload  
$ sudo systemctl enable votingmachine.service
```

# おわりに

今回は Raspberry Pi OS 上に Rust をインストールし、Slint をビルドし、.slint ファイルを表示しました。

シンプルな UI なので、X11 も Wayland も使わずに KMS でフルスクリーンで表示しているため描画の負荷が低い設計になっています。

とりあえず試して、こうやったら動いた、というレベルなので、細かいところが怪しいので、それは今後調べることにします。

明日は @hermit4 さんの [Slint言語入門(2) 構文について](https://qiita.com/hermit4/items/3128daed1d0605e05ff9)です。お楽しみにー