# Populating the GUI framebuffer with Rust

**URL:** <https://dev.unawatch.com/t/populating-the-gui-framebuffer-with-rust/45>\
**Category:** General Development\
**Created:** [August 23, 2026, 3:10pm UTC](https://dev.unawatch.com/t/populating-the-gui-framebuffer-with-rust/45 "2026-08-23T15:10:43Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![shatter\_poetry](https://avatars.discourse-cdn.com/v4/letter/s/6de8d8/32.png) [@shatter\_poetry](https://dev.unawatch.com/u/shatter_poetry)\
**Post date:** [August 23, 2026, 3:10pm UTC](https://dev.unawatch.com/t/populating-the-gui-framebuffer-with-rust/45/1 "2026-08-23T15:10:43Z")

</div>

**Why bother?**

I’m on Linux, which means TouchGFX is not ideal. A Linux user can virtualize Windows and run the TouchGFX Designer in there, but from a development workflow perspective it’s _painful_. In my personal time I try to stick to Rust as much as possible, so that’s the language I’m most comfortable with and wanted to see if I could get working.

**What do I get?**

Very little, currently. Exclusively populating the screen. Reading sensors, sending them to the UI, all that stuff is the standard SDK. The entire offering of what I’ve done so far is: draw on the watch screen within an app with [embedded-graphics](https://lib.rs/crates/embedded-graphics).

**What’s involved?**

UNA _almost_ already supports this, there are some hacks to get what’s offered to actually work. Hopefully these will be upstreamed.

If you’re more than passively interested I can share the repo, but it has a number of things currently that should actively not be copied because I was taking shortcuts to get things to work. In very broad strokes:

1. Look at [una-sdk/Libs/Source/AppSystem/EntryPoint/CustomGUI/main.cpp at main · UNAWatch/una-sdk · GitHub](https://github.com/UNAWatch/una-sdk/blob/main/Libs/Source/AppSystem/EntryPoint/CustomGUI/main.cpp) , that’s the contract
2. Write a shim (in C++) to handle messages
  - C++ owns the framebuffer as well, Rust receives a raw pointer and a length

3. Make an extremely dull ABI
  - `typdef struct { ... everything you need including uint32_t frames ... } poc_gui_state`
  - `uint32_t poc_gui_screen_count(void)`
  - `void poc_gui_render(uint8_t* buf, uint16_t width, uint16_t height, uint32_t screen, const poc_gui_state* state);`

4. In Rust, implement [`DrawTarget`](https://docs.rs/embedded-graphics/latest/embedded_graphics/draw_target/trait.DrawTarget.html) over the byte slice
  - this step is glue togetther `embedded-graphics` and the framebuffer

5. Build the crate as two types (simulator too!): `"staticlib", "lib"`
6. Mash the build together so CMake runs `cargo build --release --target thumbv8m.main-none-eabihf` then gives the linker the resultant `.a`.
  - this part is unnecessarily crunchy right now, I anticipate UNA cleaning this up so custom GUIs are easier

There’s an app [here](https://kira.technicallyrural.com/data/apps/registry/rust-gui-poc/RustGuiPoc/Rust_GUI_0.2.0.uapp) you can try that does precisely this:

 ![signal-2026-08-23-11-03-08-244](https://canada1.discourse-cdn.com/flex048/uploads/unawatch/original/1X/a996cafdcea235033fb7e888f6cf4238515f6153.webp)

 ![signal-2026-08-23-11-03-08-244_002](https://canada1.discourse-cdn.com/flex048/uploads/unawatch/original/1X/c80c8d5bc2ff9819c494cf1b538096b38e6bb339.webp)

I’ll try and feed in a real sensor so it’s something a small bit closer to a “real” app, but it is _early_ days.

---

<div class="post-metadata">

**Author:** ![shatter\_poetry](https://avatars.discourse-cdn.com/v4/letter/s/6de8d8/32.png) [@shatter\_poetry](https://dev.unawatch.com/u/shatter_poetry)\
**Post date:** [August 23, 2026, 5:57pm UTC](https://dev.unawatch.com/t/populating-the-gui-framebuffer-with-rust/45/2 "2026-08-23T17:57:08Z")

</div>

I spent a little more time with this. There’s a new app [here](https://kira.technicallyrural.com/data/apps/registry/rust-gui-poc/RustGuiPoc/Rust_GUI_0.11.0.uapp) that is a HUGE improvement (/s).

- shows a “heartbeat” of the render loop with a dot around the outside
- renders the actual non-faked accelerometer data against a little bullseye
- shows off a dither

 ![signal-2026-08-23-13-50-33-889](https://canada1.discourse-cdn.com/flex048/uploads/unawatch/original/1X/bc2fcd4542ff016beb51e9463a3e83764cd4f8d8.webp)

 ![signal-2026-08-23-13-50-33-889_002](https://canada1.discourse-cdn.com/flex048/uploads/unawatch/original/1X/0221680275eb4ddda87d2172b2daecab2bb14e72.webp)

This is very slightly more real. The first screen could be a game if someone had not experienced the last ~50 years of technological advancement.
