lupyuen.org/articles/porting-druid-rust-widgets-to-pinetime-smart-watch.html

1148 lines
No EOL
78 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="/_static/css/banner-styles.css?v=bsmaklHF" />
<link rel="stylesheet" type="text/css" href="/_static/css/iconochive.css?v=qtvMKcIJ" />
<link rel="canonical" href="https://lupyuen.org/articles/porting-druid-rust-widgets-to-pinetime-smart-watch.html" />
<!-- End Wayback Rewrite JS Include -->
<title data-rh="true">Porting [druid] Rust Widgets to PineTime Smart Watch</title>
<meta data-rh="true" charset="utf-8" />
<meta data-rh="true" name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
<meta data-rh="true" name="theme-color" content="#000000" />
<meta data-rh="true" property="og:type" content="article" />
<meta data-rh="true" property="article:published_time" content="2020-01-02T10:06:01.676Z" />
<meta data-rh="true" name="title" content="Porting [druid] Rust Widgets to PineTime Smart Watch" />
<meta data-rh="true" property="og:title" content="Porting [druid] Rust Widgets to PineTime Smart Watch" />
<meta data-rh="true" property="twitter:title" content="Porting [druid] Rust Widgets to PineTime Smart Watch" />
<meta data-rh="true" name="description"
content="A button that responds to our tapping and increments a counter… Thats what we shall accomplish today on the PineTime Smart Watch… In [druid], UI controls are known as Widgets. Our app contains two…" />
<meta data-rh="true" property="og:description"
content="A button that responds to our tapping and increments a counter… Thats what we shall accomplish today on the PineTime Smart Watch…" />
<meta data-rh="true" property="twitter:description"
content="A button that responds to our tapping and increments a counter… Thats what we shall accomplish today on the PineTime Smart Watch…" />
<meta data-rh="true" name="twitter:card" content="summary_large_image" />
<meta data-rh="true" name="twitter:creator" content="@MisterTechBlog" />
<meta data-rh="true" name="author" content="Lup Yuen Lee 李立源" />
<meta data-rh="true" name="robots" content="index,follow" />
<meta data-rh="true" name="referrer" content="unsafe-url" />
<meta data-rh="true" name="twitter:label1" value="Reading time" />
<meta data-rh="true" name="twitter:data1" value="12 min read" />
<meta property="og:image"
content="https://lupyuen.github.io/images/legacy/g1.png">
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
<link rel="stylesheet" type="text/css" href="../normalize.css">
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
<link rel="stylesheet" type="text/css" href="../prism.css">
<script src="../storage.js"></script><noscript>
<link rel="stylesheet" href="../noscript.css"></noscript>
<link rel="shortcut icon" href="../favicon.ico">
<style type="text/css">
#crate-search {
background-image: url("../down-arrow.svg");
}
a {
color: #77d;
}
</style>
<!-- End scripts/rustdoc-header.html -->
</head>
<body>
<div id="root">
<div class="a b c">
<article>
<section class="cj ck cl cm ak cn ce n co"></section><span class="r"></span>
<div>
<div class="cp u cq cr cs ct"></div>
<section class="cu cv cw cx cy">
<div class="cz ak">
<p><img src="https://lupyuen.github.io/images/legacy/g1.png" /></p>
<p><em>PineTime Smart Watch running a [druid] app with
a Label Widget and a Button Widget</em></p>
</div>
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<div>
<div id="d2a3" class="ea eb ec bk ed b ee ef eg eh ei ej ek el em en eo">
<h1 class="ed b ee ep eg eq ei er ek es em et ec">Porting [druid] Rust Widgets to PineTime Smart
Watch</h1>
</div>
<div class="eu">
<div class="n ev ew ex ey">
<div class="o n">
<div><a rel="noopener"
href="https://lupyuen.github.io">
<div class="dd ez fa">
<div class="bs n fb o p cp fc fd fe ff fg ct"></div>
</div>
</a></div>
<div class="fi ak r">
<div class="n">
<div style="flex:1"><span class="bj b bk bl bm bn r ec q">
<div class="fj n o fk"><span class="bj dy ds bl di fl fm fn fo fp ec"><a
class="at au av aw ax ay az ba bb bc fq bf bg bh bi" rel="noopener"
href="https://lupyuen.github.io">Lup
Yuen Lee 李立源</a></span>
<div class="fr r ar h">
</div>
</div>
</span></div>
</div><span class="bj b bk bl bm bn r bo bp"><span class="bj dy ds bl di fl fm fn fo fp bo">
<div><a class="at au av aw ax ay az ba bb bc fq bf bg bh bi" rel="noopener"
href="https://lupyuen.github.io">
14 Dec 2019</a> <!-- -->·
<!-- -->
<!-- -->12
<!-- --> min read
</div>
</span></span>
</div>
</div>
</div>
</div>
</div>
<p>
UPDATE: This code in this article has been archived in the <a href="https://github.com/lupyuen/pinetime-rust-mynewt/tree/pre-lvgl">pre-lvgl</a> branch of pinetime-rust-mynewt.
The pinetime-rust-mynewt firmware has been revamped to support <a href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/watchface">Rust Watch Faces on LVGL</a>.
<a href="https://lupyuen.github.io/pinetime-rust-mynewt/articles/watchface">Check out the updates </a>
</p>
<p id="410b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">A button that responds to our
tapping and increments a counter… Thats what we shall accomplish today on the <a
class="at cg hd he hf hg" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">PineTime
Smart Watch</a></p>
</div>
</div>
<div class="cz ak">
<div class="dl r dd">
<div class="hm r"><iframe
src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F7nXJdW_GkEw%3Ffeature%3Doembed&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D7nXJdW_GkEw&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F7nXJdW_GkEw%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube"
allowfullscreen="" frameborder="0" height="480" width="854" title="PineTime [druid] touch widgets"
class="cp t u dh ak" scrolling="auto"></iframe></div>
</div>
</div>
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<p id="fe15" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Watch App</a> we see in the video
was created with the [<a
href="https://crates.io/crates/druid"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">druid</a>] Crate in Rust.</p>
<p id="7843" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In [druid], UI controls are known
as Widgets. Our app contains two Widgets: A Label Widget that displays the counter, and a Button
Widget that increments the counter.</p>
<p id="183d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Lemme explain the code in the <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">[druid] app</a></p>
<p><script src="https://gist.github.com/lupyuen/cf8de0355513e5a450a975839e7b7870.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="274d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">When the app is launched, we
create a new window, passing it the <code class="dm ho hp hq hr b">ui_builder</code> function that
will return a list of Widgets and their layouts. (More about <code
class="dm ho hp hq hr b">ui_builder</code> later.)</p>
<p><script src="https://gist.github.com/lupyuen/2e042fc196fb9cb7fe28ff836d08336c.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="bcf5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here we set the Application State
to <code class="dm ho hp hq hr b">0</code>. <em class="hs">Whats this Application State?</em></p>
<p id="dadc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Remember the counter value thats
incremented every time we tap the button? This counter value is stored in the [druid] <strong
class="gr ht">Application State</strong>. Widgets will interact with the Application State: Our
Button Widget updates the state and our Label Widget displays the state.</p>
<p id="8005" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Thats why [druid] is known as a
<a href="https://crates.io/crates/druid"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Data-Oriented Rust UI</a>… [druid]
Widgets are bound to data values in the Application State.</p>
<p><script src="https://gist.github.com/lupyuen/63e343144093f02599dc75f6b76798b8.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="e28f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Recall that <code
class="dm ho hp hq hr b">main_window</code> contains a list of Widgets and their layouts. Well pass
this to the AppLauncher to launch the app window. The app shall be launched with <code
class="dm ho hp hq hr b">data</code>, the Application State, set to 0. If the launch fails, well
stop with an error <code class="dm ho hp hq hr b">launch failed</code>.</p>
<p id="b14e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Now lets look at <code
class="dm ho hp hq hr b">ui_builder</code> and how it creates the Widgets…</p>
<p><script src="https://gist.github.com/lupyuen/b1829f1ec252fb6a45fbdd2c5d5bb23b.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="c0e1" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">First we create a <code
class="dm ho hp hq hr b">text</code> string that contains the counter value. How shall we get the
value of the <code class="dm ho hp hq hr b">text</code> string? Through this Rust Closure…</p>
<p><script src="https://gist.github.com/lupyuen/a15ffce536a92befa5bc5088b3774de5.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="1e7e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Whats a Rust
Closure?</em> Think of it as a Rust Function without a name. The Closure above is equivalent to this
verbose Rust Function…</p>
<p><script src="https://gist.github.com/lupyuen/eaf6512835189ceb785a44304268ffe9.js"></script></p>
<p id="a14a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">So its simpler to use the
Closure form. The Closure simply returns the value of the counter in <code
class="dm ho hp hq hr b">data</code> (the Application State) as the value of the <code
class="dm ho hp hq hr b">text</code> string. The <code class="dm ho hp hq hr b">into</code> part
converts the returned value from number to string.</p>
<p><script src="https://gist.github.com/lupyuen/2be64d6b3cc1cc2d661059f7480e1608.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="e7af" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Now that we have the counter
value stored in our <code class="dm ho hp hq hr b">text</code> string, lets display it with a Label
Widget.</p>
<p><script src="https://gist.github.com/lupyuen/234494d01b9520199db07daf21b70d02.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="61ee" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here we create a button labelled
increment that calls this Rust Closure whenever the button is tapped…</p>
<p><script src="https://gist.github.com/lupyuen/fcfab489865c453417cd6180c925fc60.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="881c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This Closure is equivalent to the
Rust Function…</p>
<p><script src="https://gist.github.com/lupyuen/8a067757c1816fa101b2a2d60b6057b8.js"></script></p>
<p id="1a92" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
class="dm ho hp hq hr b">data</code> contains our Application State, which is our counter value. The
Closure simply increments our counter value by 1, every time the button is tapped.</p>
<p id="e00b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Hey Presto!</em>
Thats the magic behind our Data-Oriented UI… The button increments the counter in our Application
State, the label displays the value of the counter in our Application State!</p>
<p id="d6f2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">[druid] completes the magic act
by wiring up the Widgets to the Application State… When our Application State changes (upon tapping
the button), [druid] automagically refreshes our Label Widget to show the new counter value.</p>
<p id="e398" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Lets tell [druid] how to layout
our Label and Button Widgets for display…</p>
<p><script src="https://gist.github.com/lupyuen/42f63217ec8547d3040d587f23eefb41.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="a402" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Well display the Label and
Button Widgets in a single column <code class="dm ho hp hq hr b">col</code>. The Label Widget goes on
top, centered horizontally, with a padding of 5 pixels.</p>
<p><script src="https://gist.github.com/lupyuen/3e10975902d1a13ba41858c8b87f5f82.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="ff7e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The Button Widget appears next in
<code class="dm ho hp hq hr b">col</code>. Also with a padding of 5 pixels.</p>
<p id="73d3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Note that the second argument to
<code class="dm ho hp hq hr b">add_child</code> is the same for both the label and the button: <code
class="dm ho hp hq hr b">1.0</code>. This means that the label and button shall occupy equal
vertical spacing, i.e. the label shall fill the top half of the screen, the button shall fill the
bottom half. This is similar to the <code
class="dm ho hp hq hr b"><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">flexbox</a></code><a
href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"> concept in CSS</a>.</p>
<p><script src="https://gist.github.com/lupyuen/4451cacfd5a46a1cb89bbd9e2441c578.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
<p id="f5f0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">And lastly, we return the <code
class="dm ho hp hq hr b">col</code> to the caller. To recap: <code
class="dm ho hp hq hr b">col</code> contains the Label and Button Widgets, the Closures for
displaying and incrementing the Application State counter, and the layout of the Widgets.</p>
<p id="bc8a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Thats the clever simplicity of
[druid] apps. [druid] apps have a Declarative UI like <a
href="https://developer.apple.com/tutorials/swiftui/creating-a-watchos-app"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">SwiftUI</a> and <a
href="https://flutterbyexample.com/reusable-custom-card-widget"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Flutter</a>… Without the legacy
baggage of iOS and Android. Thats why I think [druid] is perfect for building Watch Apps for
PineTime!</p>
<p id="d5de" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Heres the complete [druid] Watch
App for PineTime…</p>
<p><script src="https://gist.github.com/lupyuen/55496eb6c8b78a6b036b13508a1d79be.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/hello.rs</a>
</em></p>
</div>
</div>
</section>
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
<section class="cu cv cw cx cy">
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<h1 id="d632" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Powering [druid] with Type
Inference</h1>
<p id="6c5c" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">The above Rust source code for
the watch app looks so trivial (compared with SwiftUI and Flutter)… <em class="hs">Surely something
must be missing in that app?</em></p>
<p id="6eb3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Oh yes, plenty is missing from
the app… <em class="hs">But the Rust Compiler performs </em><a
href="https://doc.rust-lang.org/stable/rust-by-example/types/inference.html"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><em class="hs">Type
Inference</em></a><em class="hs"> and fills in all the missing bits for us!</em> Heres how it
looks with the missing bits filled in…</p>
<p><script src="https://gist.github.com/lupyuen/fe85063d052c0544e35e7fd18cc0b27d.js"></script></p>
<p id="bbdc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Note that the Widgets have been
expanded as <code class="dm ho hp hq hr b">Label&lt;u32&gt;</code> and <code
class="dm ho hp hq hr b">Button&lt;u32&gt;</code>. Thats because <code
class="dm ho hp hq hr b">Label</code> and <code class="dm ho hp hq hr b">Button</code> are actually
Generic Types in Rust.</p>
<p id="4334" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Quick recap of Generic Types:
<code class="dm ho hp hq hr b">Vec</code> is a Generic Type that represents a vector (array) of
values. When we write <code class="dm ho hp hq hr b">Vec&lt;i32&gt;</code>, it refers to a vector of
integer values (<code class="dm ho hp hq hr b">i32</code>).</p>
<p id="deee" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Thus <code
class="dm ho hp hq hr b">Label&lt;u32&gt;</code> and <code
class="dm ho hp hq hr b">Button&lt;u32&gt;</code> are Generic Widgets that are backed by <code
class="dm ho hp hq hr b">u32</code>, our Application State that contains the counter. <em
class="hs">Reminds us that [druid] is really a Data-Oriented UI!</em></p>
<p id="b70b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Some types (like <code
class="dm ho hp hq hr b">WindowDesc</code>) are Generic Types with two parameters. So this line of
code in our app…</p>
<pre
class="hh hi hj hk hl iu iv iw"><span id="132f" class="ix ie ec bk hr b ds iy iz r ja">let main_window = WindowDesc::new(ui_builder);</span></pre>
<p id="ffe2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Actually expands to this
complicated mess…</p>
<pre
class="hh hi hj hk hl iu iv iw"><span id="b0db" class="ix ie ec bk hr b ds iy iz r ja">let main_window = WindowDesc::&lt;u32,Flex&lt;u32&gt;&gt;::new(ui_builder);</span></pre>
<p id="be4c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">What a bloody
clever compiler!</em> The Rust Compiler makes it easy for us to write Watch Apps. Under the hood,
things are a lot more complicated with the Generic Types. Read on to learn how [druid] Widgets are
implemented on PineTime…</p>
</div>
</div>
</section>
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
<section class="cu cv cw cx cy">
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<h1 id="55ec" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Downsizing [druid] from Desktop
to Embedded</h1>
<p id="d91d" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">[druid] is a GUI toolkit that
produces desktop apps for Windows, macOS and Linux. It doesnt support Embedded Platforms like
PineTime. So I had to change some features to make [druid] work on PineTime. <em class="hs">(Only the
[druid] implementation was changed, not the [druid] APIs… The same [druid] desktop application code
can be compiled for PineTime without any code changes! Compare the above [druid] code for PineTime
with </em><a
href="https://github.com/xi-editor/druid/blob/master/druid/examples/hello.rs"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><em class="hs">the original
version</em></a><em class="hs">)</em></p>
<p id="011a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">On Embedded Rust we declare our
programs as <code
class="dm ho hp hq hr b">#![<a href="https://rust-embedded.github.io/book/intro/no-std.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">no_std</a>]</code>.
This means that our embedded programs will call the <a
href="https://doc.rust-lang.org/core/index.html"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr ht">Rust Core
Library</strong></a>, instead of the usual <a
href="https://doc.rust-lang.org/std/index.html"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr ht">Rust
Standard Library</strong></a> which has many more features. Any code in [druid] that uses the
following features from the Rust Standard Library will <em class="hs">NOT</em> compile on PineTime…
</p>
<ol class="">
<li id="5e50" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd"><code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/vec/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Vec</a></code>
vectors, because it uses <strong class="gr ht">Heap Memory</strong> to support resizable vectors.
And Heap Memory is not available on embedded platforms (by default).</li>
<li id="9320" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/string/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">String</a></code>
(though <code class="dm ho hp hq hr b">&amp;str</code> is supported), because it also uses <strong
class="gr ht">Heap Memory</strong>.</li>
<li id="0f06" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/fmt/" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">format</a>!</code>,
because internally it uses a <code class="dm ho hp hq hr b">String</code> to write formatted
strings.</li>
<li id="8d8e" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/boxed/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Box</a></code>,
<code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/cell/struct.RefCell.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">RefCell</a></code>,
<code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/sync/struct.Arc.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Arc</a></code>
(Atomically Reference Counted) and <code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/borrow/enum.Cow.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Cow</a></code>
(Clone On Write), because they use <strong class="gr ht">Heap Memory</strong> as well.</li>
</ol>
<p id="5d5b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Why isnt Heap
Memory available on Embedded Rust?</em> Embedded platforms like PineTime often have little RAM.
(Only 64 KB RAM for PineTimes Nordic nRF52832 microcontroller) When RAM is limited, we prefer to
budget our memory requirements in advance… Just to be sure that our Smart Watch doesnt allocate too
many Widgets at runtime and crash.</p>
<p id="a867" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Hence all space for Widgets
should be preallocated in Static Memory before the Watch App starts. <em class="hs">(There is a way to
implement Heap Memory on embedded platforms, </em><a
href="https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><em class="hs">check this for
details</em></a><em class="hs">)</em></p>
<p id="579c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">So many features
are missing from Embedded Rust… Is it still possible to run [druid] apps on PineTime?</em> Yes, I
used some tricks to make a quick (and somewhat dirty) port of [druid] to PineTime… Which you can also
use for porting Rust desktop libraries to embedded platforms! Read on to learn more…</p>
</div>
</div>
</section>
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
<section class="cu cv cw cx cy">
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<h1 id="24ff" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Porting Vectors, Strings and
format! to Embedded</h1>
<p id="810e" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">The <code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/vec/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Vec</a></code>
and <code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/string/index.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">String</a></code>
types are used in many Rust libraries. On embedded platforms we cant allow vectors and strings to be
resized on demand… But we can create vectors and strings that have a maximum size.</p>
<p id="c009" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Heres the quickest (and
dirtiest) way to use the [<a
href="https://crates.io/crates/heapless"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">heapless</a>] crate to replace
standard types <code class="dm ho hp hq hr b">Vec</code> and <code
class="dm ho hp hq hr b">String</code> by <a
href="https://docs.rs/heapless/0.5.1/heapless/struct.Vec.html"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">vectors</a> and <a
href="https://docs.rs/heapless/0.5.1/heapless/struct.String.html"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">strings</a> with fixed size
limits…</p>
<p><script src="https://gist.github.com/lupyuen/9a0430d44a7df8c082ad71c0bcad1e89.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/localization.rs#L87-L91"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/localization.rs#L87-L91</a>
</em></p>
<p id="7ce2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">(Yep told you this
would be dirty… Should never override standard Rust types!)</em></p>
<p id="6289" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Most of the surrounding code that
calls <code class="dm ho hp hq hr b">Vec</code> and <code class="dm ho hp hq hr b">String</code>
should compile with this simple modification. (We may need to replace <code
class="dm ho hp hq hr b">&amp;str</code> by <code class="dm ho hp hq hr b">String</code>) Watch out
for the extra copies of vectors and strings that our program will now be copying… Keep them small!</p>
<p id="5036" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Once the code works on our
embedded gadget, <em class="hs">Do The Right Thing</em> and rename <code
class="dm ho hp hq hr b">Vec</code> and <code class="dm ho hp hq hr b">String</code> to something
sensible, like <code class="dm ho hp hq hr b">FixedVec</code> and <code
class="dm ho hp hq hr b">FixedString</code>.</p>
<p id="22c0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Theres a similar trick for
porting <code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/std/macro.format.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">format</a>!</code>
to embedded platforms. Lets say we are converting a number v to a text string…</p>
<pre
class="hh hi hj hk hl iu iv iw"><span id="ffb5" class="ix ie ec bk hr b ds iy iz r ja">format!(&quot;{}&quot;, v)</span></pre>
<p id="55ff" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This fails to compile because
<code class="dm ho hp hq hr b">format!</code> uses an internal <code
class="dm ho hp hq hr b">String</code> to <a
href="https://doc.rust-lang.org/src/alloc/fmt.rs.html#561"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">store the formatted result</a>.
The [heapless] solution uses the <code
class="dm ho hp hq hr b"><a href="https://doc.rust-lang.org/core/macro.write.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">write</a>!</code>
macro like this…</p>
<p><script src="https://gist.github.com/lupyuen/3601e17aba48b9e6eda4e77d7edfcfdc.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/argvalue.rs#L63-L66"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/argvalue.rs#L63-L66</a>
</em></p>
</div>
</div>
</section>
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
<section class="cu cv cw cx cy">
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<h1 id="43cb" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Boxing up Widgets and Windows
</h1>
<p id="a7fe" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">Without Heap Memory, [druid]
cant use <code class="dm ho hp hq hr b">Box</code> to allocate Widgets on the heap…</p>
<p><script src="https://gist.github.com/lupyuen/33ae6027e8aa716636c5d6756ebdfcd1.js"></script></p>
<p><em>Original [druid] code that allocates Widgets
on the heap. From <a
href="https://github.com/xi-editor/druid/blob/master/druid/src/widget/flex.rs#L103-L151"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/xi-editor/druid/blob/master/druid/src/widget/flex.rs#L103-L151</a>
</em></p>
<p id="660d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Why does [druid]
use </em><code class="dm ho hp hq hr b"><em class="hs">Box</em></code><em class="hs"> Widgets?</em>
Because [druid] has Container Widgets (<code class="dm ho hp hq hr b">Flex</code> / <code
class="dm ho hp hq hr b">Row</code> / <code class="dm ho hp hq hr b">Column</code>) that contain
references to Child Widgets, like we see above.</p>
<p id="ec69" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In our embedded version, [druid]
uses a custom <code
class="dm ho hp hq hr b"><a href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">WidgetBox</a></code>
type that pretends to be a <code class="dm ho hp hq hr b">Widget</code> in a <code
class="dm ho hp hq hr b">Box</code></p>
<p><script src="https://gist.github.com/lupyuen/9d83be6ad4b20e808e2646df97ed796c.js"></script></p>
<p><em>Modified [druid] code that uses static
WidgetBox. From <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/flex.rs#L126-L151"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/flex.rs#L126-L151</a>
</em></p>
<p id="b906" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Heres a crude implementation of
<code class="dm ho hp hq hr b">WidgetBox</code>… Instead of the heap, <code
class="dm ho hp hq hr b">WidgetBox</code> stores Widgets in an array in Static Memory. <code
class="dm ho hp hq hr b">WidgetBox</code> then returns the array index (Widget ID) at which the
Widget is stored….</p>
<p><script src="https://gist.github.com/lupyuen/d76314005e47a91be45caea2274c5638.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs</a>
</em></p>
<p id="bda2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
class="dm ho hp hq hr b">WidgetBox</code> implements the Traits (features) of a Widget by forwarding
the function calls to the Widget stored in Static Memory…</p>
<p><script src="https://gist.github.com/lupyuen/5c725d5f288fb6b9e9793885673c9b4b.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs</a>
</em></p>
<p id="dc64" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Storing Widgets statically can be
tricky… Our Widgets are Generic Types that depend on the data type of the Application State. Earlier
we have seen that the Rust Compiler expands our Watch App with labels and buttons as <code
class="dm ho hp hq hr b">Label&lt;u32&gt;</code> and <code
class="dm ho hp hq hr b">Button&lt;u32&gt;</code></p>
<p id="645a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The solution is to use a <a
href="https://users.rust-lang.org/t/how-to-use-specialization-features/6023"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr ht">Specialised
Trait</strong></a> like this…</p>
<p><script src="https://gist.github.com/lupyuen/a4946daf650c85e16708d9028522ec9b.js"></script></p>
<p><em>From <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/widget/widgetbox.rs</a>
</em></p>
<p id="2c55" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Note the pattern for coding a
Specialised Trait…</p>
<ol class="">
<li id="7bdc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd">Declare the Trait
across all types: <br /><code
class="dm ho hp hq hr b">trait GlobalWidgets&lt;<strong class="gr ht">D</strong>&gt; { fn f(arg: <strong class="gr ht">D</strong>); ...</code>
</li>
<li id="ba7f" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd">Implement the Default
Trait: <br /><code
class="dm ho hp hq hr b">impl&lt;<strong class="gr ht">D</strong>&gt; GlobalWidgets&lt;<strong class="gr ht">D</strong>&gt; { <strong class="gr ht">default</strong> fn f(arg: <strong class="gr ht">D</strong>) { ...</code>
</li>
<li id="68c7" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd">Implement the
Specialised Trait for each specific type like <code class="dm ho hp hq hr b">u32</code>: <br /><code
class="dm ho hp hq hr b">impl GlobalWidgets&lt;<strong class="gr ht">u32</strong>&gt; { fn f(arg: <strong class="gr ht">u32</strong>) { ...</code>
</li>
</ol>
<p id="7b92" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The same trick is used to port
[druid] Windows to PineTime. Whenever we need to <code class="dm ho hp hq hr b">Box</code> up a
[druid] <code class="dm ho hp hq hr b">Window</code>, we use a <code
class="dm ho hp hq hr b"><a href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/windowbox.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">WindowBox</a></code>
instead. Windows and Window Handlers are now <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/win_handler.rs#L52-L264"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">stored in a static array</a> just
like Widgets.</p>
</div>
</div>
</section>
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
<section class="cu cv cw cx cy">
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<p><img src="https://lupyuen.github.io/images/legacy/g2.png" /></p>
<p><em>[druid] Dependencies</em></p>
<h1 id="8d2e" class="id ie ec bk bj if ee jl eg jm ii jn ik jo im jp io">Connect [druid] to PineTimes
Display and Touch Controllers</h1>
<p id="3c20" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">Now that [druid] compiles
successfully on PineTime, lets connect our fork of [druid] to the PineTime display and touch
hardware…</p>
<div class="jq jr js jt ju jv"><a
href="https://github.com/lupyuen/druid-embedded"
rel="noopener nofollow">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">lupyuen/druid-embedded</div>
</h2>
</div>
<div class="ku r">
<div class="kv r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
<p id="818d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">[druid] calls the [<a
href="https://crates.io/crates/piet"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">piet</a>] crate to render graphics
and text. <a class="at cg hd he hf hg" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">In
our first PineTime article</a>, we have created a Rust display driver based on the [<a
href="https://crates.io/crates/embedded-graphics"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">embedded-graphics</a>] and [<a
href="https://crates.io/crates/st7735-lcd"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">st7735-lcd</a>] crates.</p>
<p id="357a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Hence the solution: Adapt [piet]
to render graphics and text with [embedded-graphics]. Heres the resulting fork…</p>
<div class="jq jr js jt ju jv"><a
href="https://github.com/lupyuen/piet-embedded/tree/master/piet-embedded-graphics"
rel="noopener nofollow">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">lupyuen/piet-embedded</div>
</h2>
</div>
<div class="ku r">
<div class="lc r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
<p id="b850" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Heres a peek of [piet] adapted
for [embedded-graphics]: Rendering a Bezier path with [embedded-graphics] primitives (so that we can
have rounded buttons)…</p>
<p><script src="https://gist.github.com/lupyuen/17fc071db37d5b1d496b9c8ae7f10126.js"></script></p>
<p><em>Rendering Widgets with [embedded-graphics].
From <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/context.rs#L67-L199"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/context.rs#L67-L199</a>
</em></p>
<p id="c523" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The display driver from our first
PineTime article <code class="dm ho hp hq hr b">display.rs</code> has been <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/display.rs"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">relocated into the [piet]
library</a>.</p>
<blockquote class="ld le lf">
<p id="1b65" class="gp gq ec hs gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="bk">💎</em><strong
class="gr ht">Advanced Topic</strong>: The [embedded-graphics] version of [piet] make take a few
seconds to <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/context.rs#L87-L96"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">blank the screen</a> or <a
href="https://github.com/lupyuen/piet-embedded/blob/master/piet-embedded-graphics/src/context.rs#L124-L147"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">fill a region with colour</a>
(e.g. for buttons). I may enhance the display driver [st7735-lcd] to use SPI with DMA for such
operations.</p>
</blockquote>
<p id="6826" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">What about
touch?</em> <a class="at cg hd he hf hg" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">In
our second PineTime article</a>, we have created a Rust Touch Controller Driver. <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/touch_sensor.rs#L81-L101"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">This Touch Controller Driver has
been wired up</a> to send touch events to [druid]. [druid] doesnt support touch events yet, so we
simulate the mouse down + mouse up events instead…</p>
<p><script src="https://gist.github.com/lupyuen/0c57c85af177dbe9ceed957e37f08db2.js"></script></p>
<p><em>Handling Touchable Widgets. From <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/win_handler.rs#L62-L90"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://github.com/lupyuen/druid-embedded/blob/master/druid/src/win_handler.rs#L62-L90</a>
</em></p>
<blockquote class="ld le lf">
<p id="f7d9" class="gp gq ec hs gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="bk">💎</em><strong
class="gr ht">Advanced Topic</strong>: If you check the video demo, most taps of the button
respond within 1 second, but some taps on the button dont seem to increment the counter, and some
taps appear to increment the counter twice. Thats because the Touch Controller Driver has not been
implemented completely.</p>
<p id="0202" class="gp gq ec hs gr b gs gt gu gv gw gx gy gz ha hb hc"><a class="at cg hd he hf hg"
target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">The
touchscreen controller supports multitouch</a>, so the controller tracks each finger and reports
whether each finger is a press (down) or release (up) action. The current Touch Controller Driver <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/rust/app/src/touch_sensor.rs#L81-L101"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">interprets only the first finger
detected</a>, and always assume its a press (down) action not a release (up) action.</p>
</blockquote>
</div>
</div>
</section>
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
<section class="cu cv cw cx cy">
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<h1 id="11b4" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Other Features Downsized in
[druid] and [kurbo]</h1>
<p id="088b" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">The above fork of [druid] has
some features commented out…</p>
<ol class="">
<li id="76d5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd"><strong
class="gr ht">Localization</strong>: Our [druid] app uses a <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/localization.rs"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Localized String</a> <code
class="dm ho hp hq hr b">hello-counter</code>… This actually refers to a <a
href="https://github.com/xi-editor/druid/blob/master/druid/resources/i18n/en-US/builtin.ftl"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Resource File</a> that specifies
the displayed text as <code class="dm ho hp hq hr b">Current value is &lt;count&gt;</code>. This is
probably overkill for a proof-of-concept, so it has been stubbed out and replaced by <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/argvalue.rs"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">a simpler version</a> that
displays only the first argument.</li>
<li id="e48f" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><strong
class="gr ht">Themes and Environments</strong>: [druid] supports UI themes and configurable
environment settings. For embedded platforms they should probably be implemented as compile-time
constants. So Themes and Environments have been <a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/env.rs#L40-L83"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">stubbed out for now</a>.</li>
<li id="52ec" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/event.rs#L127-L137"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong
class="gr ht">Menus</strong></a><strong class="gr ht"> and </strong><a
href="https://github.com/lupyuen/druid-embedded/blob/master/druid/src/event.rs#L82-L94"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong
class="gr ht">Keyboards</strong></a>: Since they are not necessary for Watch Apps</li>
</ol>
<p id="fdaa" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">[druid] and [piet] use the [<a
href="https://crates.io/crates/kurbo"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">kurbo</a>] crate for computing
Bezier curves, which are necessary for drawing rounded buttons. [kurbo] has been downsized to work
with PineTime and <code class="dm ho hp hq hr b">#![no_std]</code></p>
<ol class="">
<li id="0d47" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd"><strong
class="gr ht">Bezier paths</strong> are limited to <a
href="https://github.com/lupyuen/kurbo-embedded/blob/master/src/bezpath.rs#L15-L20"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">16 points</a></li>
<li id="6be4" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><strong
class="gr ht">Double-precision floating-point (</strong><code
class="dm ho hp hq hr b"><strong class="gr ht">f64</strong></code><strong class="gr ht">) math
functions</strong> seem to be missing from the Rust Core Library, so we are using the [<a
href="https://crates.io/crates/libm"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">libm</a>] crate instead. We
could switch to single-precision floating-point (f32) and use [<a
href="https://crates.io/crates/micromath"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">micromath</a>] instead, because
its optimised for embedded platforms.</li>
</ol>
<p id="62bc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here is my fork of [kurbo] for
PineTime…</p>
<div class="jq jr js jt ju jv"><a
href="https://github.com/lupyuen/kurbo-embedded"
rel="noopener nofollow">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">lupyuen/kurbo-embedded</div>
</h2>
</div>
<div class="ku r">
<div class="lg r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
</div>
</div>
</section>
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
<section class="cu cv cw cx cy">
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<h1 id="5b74" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">PineTime ROM Usage for [druid]
and Friends</h1>
<p id="6f6a" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">[druid] + [piet] + [kurbo] +
[embedded-graphics] + [st7735-lcd] + Rust Core Library + Mynewt OS… <em class="hs">Will this long and
exciting Conga Line even run on PineTimes Nordic nRF52832 microcontroller with </em><strong
class="gr ht"><em class="hs">512 KB ROM</em></strong><em class="hs"> and </em><strong
class="gr ht"><em class="hs">64 KB RAM</em></strong><em class="hs">?</em></p>
<p><img src="https://lupyuen.github.io/images/legacy/g3.png" /></p>
<p id="890e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">Yes it does!</em>
Total ROM used is only <strong class="gr ht">237 KB</strong>, less than half of the available ROM
space!</p>
<p id="4c74" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">And this was built in <strong
class="gr ht">Debug Mode</strong>, not optimised Release Mode! <em class="hs">(Which is hard to
debug and learn)</em></p>
<p id="647d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">What about ROM
space for firmware upgrades?</em> Well we have an additional <strong class="gr ht">4 MB SPI
Flash</strong> available on PineTime!</p>
<p id="7e01" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Total RAM used is <strong
class="gr ht">46 KB</strong>, which includes 32 KB of stack space <em class="hs">(Probably too
much)</em></p>
<p id="11e1" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hs">So a functional
Rust Widget UI for Watch Apps looks mighty feasible on PineTime!</em></p>
<p id="9b62" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I think it also helps when I
replaced the <code class="dm ho hp hq hr b">Box</code> references in [druid] by <code
class="dm ho hp hq hr b">WidgetBox</code> and <code class="dm ho hp hq hr b">WindowBox</code>… The
Rust Compiler does an incredible job of removing dead code when everything is statically defined.</p>
<p id="6dae" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">We can analyse the ROM bloat by
loading this <a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/pinetime/logs/my_sensor_app.elf.map#L32538"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Linker Output Map</a> into a
Memory Map Google Sheet thats <a class="at cg hd he hf hg" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/stm32-blue-pill-analyse-and-optimise-your-ram-and-rom">explained
in this article</a></p>
</div>
</div>
<div class="cz ak">
<p><img src="https://lupyuen.github.io/images/legacy/g4.png" /></p>
<p><em>Functions that occupy the most ROM space, sorted
by ROM size (in bytes). From <a
href="https://docs.google.com/spreadsheets/d/1Lb217jqZGM7NlnOVCxpaLAI0CQcOTPnbMpksDSPVyiw/edit#gid=381366828&amp;fvid=1643056349"
class="at cg hd he hf hg" target="_blank"
rel="noopener nofollow">https://docs.google.com/spreadsheets/d/1Lb217jqZGM7NlnOVCxpaLAI0CQcOTPnbMpksDSPVyiw/edit#gid=381366828&amp;fvid=1643056349</a>
</em></p>
</div>
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<p id="d9b5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><a
href="https://docs.google.com/spreadsheets/d/1Lb217jqZGM7NlnOVCxpaLAI0CQcOTPnbMpksDSPVyiw/edit#gid=381366828"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">The Google Sheet is here</a>. It
shows the functions that occupy the most ROM space, sorted by ROM size (in bytes). Some observations…
</p>
<ol class="">
<li id="1731" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc jb jc jd">[<a
href="https://crates.io/crates/libm"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">libm</a>], the floating-point
math library, seems to be taking the most ROM space. We could replace this by [<a
href="https://crates.io/crates/micromath"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">micromath</a>], a smaller math
library for embedded platforms</li>
<li id="49b1" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd"><code
class="dm ho hp hq hr b">core::num::flt2dec::strategy</code> needs closer study. And why <code
class="dm ho hp hq hr b">dragon</code> and his friend <code class="dm ho hp hq hr b">grisu</code>
are hiding in my watch!</li>
<li id="7d8a" class="gp gq ec bk gr b gs je gu jf gw jg gy jh ha ji hc jb jc jd">[druid] seems to take
a fair amount of ROM space. Which is expected since [druid] was designed for desktop use. The ROM
usage will probably drop drastically once we enable size optimisation in the Rust Release Build.
</li>
</ol>
</div>
</div>
</section>
<hr class="hu dy hv hw hx dv hy hz ia ib ic" />
<section class="cu cv cw cx cy">
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<h1 id="f64c" class="id ie ec bk bj if ee ig eg ih ii ij ik il im in io">Whats Next?</h1>
<p id="974e" class="gp gq ec bk gr b gs ip gu iq gw ir gy is ha it hc">Here are the other articles in
the PineTime series…</p>
<div class="jq jr js jt ju jv"><a rel="noopener"
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">Sneak Peek of PineTime Smart Watch… And why its perfect for
teaching IoT</div>
</h2>
</div>
<div class="ku r">
<div class="lw r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
<div class="jq jr js jt ju jv"><a rel="noopener"
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">Building a Rust Driver for PineTimes Touch Controller</div>
</h2>
</div>
<div class="ku r">
<div class="lx r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
<div class="jq jr js jt ju jv"><a rel="noopener"
href="https://lupyuen.github.io/articles/optimising-pinetimes-display-driver-with-rust-and-mynewt">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">Optimising PineTimes Display Driver with Rust and Mynewt</div>
</h2>
</div>
<div class="ku r">
<div class="ly r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
<p id="26f3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I hope the maintainers of
[druid], [piet], [kurbo], [embedded-graphics], [st7735-lcd] will consider incorporating some of the
tweaks I have covered in this article. <em class="hs">Together we can do so much more when the Rust
Ecosystem embraces Embedded Platforms!</em></p>
<p id="161c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">And I hope you agree with me that
this is clearly the better way to code Watch Apps. Theres more work to be done to flesh this out
fully for PineTime… Lemme know if youre keen to help!</p>
<p id="c82c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The source code in this article
may be found in the <code class="dm ho hp hq hr b">pinetime</code> branch of this repository…</p>
<div class="jq jr js jt ju jv"><a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/pinetime"
rel="noopener nofollow">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">lupyuen/stm32bluepill-mynewt-sensor</div>
</h2>
</div>
<div class="ku r">
<div class="lz r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
<p id="9eb7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The macOS build files for
[druid], [piet], [kurbo], Embedded Rust and Mynewt OS for PineTime are available here…</p>
<div class="jq jr js jt ju jv"><a
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/releases/tag/v13.0.0"
rel="noopener nofollow">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">lupyuen/stm32bluepill-mynewt-sensor</div>
</h2>
</div>
<div class="ku r">
<div class="ma r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
<p id="3eb4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Ill be packaging a proper
release when PineTime is available to the general public. Meanwhile if you have trouble building the
source files (<a class="at cg hd he hf hg" target="_blank" rel="noopener"
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">as
explained in the first article</a>), please drop me a note! The PineTime build, flash and debug
steps on Visual Studio Code look like this…</p>
</div>
</div>
<div class="cz ak">
<div class="dl r dd">
<div class="mb r"><iframe
src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FAMVYXOSPjIg%3Ffeature%3Doembed&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DAMVYXOSPjIg&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FAMVYXOSPjIg%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube"
allowfullscreen="" frameborder="0" height="300" width="400"
title="Rust and Mynewt OS on PineTime Smart Watch" class="cp t u dh ak" scrolling="auto"></iframe>
</div>
</div>
<p><em>PineTime build, flash and debug steps on Visual
Studio Code</em></p>
</div>
<div class="n p">
<div class="ac ae af ag ah dz aj ak">
<p id="b9e4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">UPDATE: <a
href="https://store.pine64.org/?product=pinetime-dev-kit"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">PineTime is now available for
purchase by general public</a>! Im creating a <a
href="https://github.com/lupyuen/pinetime-rust-mynewt"
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">new repository here</a> that will
have a prebuilt firmware image for PineTime. Ill also include instructions for flashing the firmware
to your PineTime with a Raspberry Pi.</p>
<div class="jq jr js jt ju jv"><a
href="https://github.com/lupyuen/pinetime-rust-mynewt"
rel="noopener nofollow">
<section class="jy cl cm ak ce n ar jz ka kb kc kd ke kf kg kh ki kj kk kl km kn">
<div class="ko n co p kp kq">
<h2 class="bj if kr bl ec">
<div class="di jw fm fn jx fp">lupyuen/pinetime-rust-mynewt</div>
</h2>
</div>
<div class="ku r">
<div class="mc r kw kx ky ku kz la lb"></div>
</div>
</section>
</a></div>
</div>
</div>
</section>
</div>
</article>
</div>
</div>
<ul>
<li>
<p><a href="https://github.com/sponsors/lupyuen">Sponsor me a coffee</a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io">Check out my articles</a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/rss.xml">RSS Feed</a></p>
</li>
</ul>
</body>
</html>
<!--
FILE ARCHIVED ON 04:48:08 Jan 09, 2020 AND RETRIEVED FROM THE
INTERNET ARCHIVE ON 10:32:54 Feb 21, 2021.
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
SECTION 108(a)(3)).
-->
<!--
playback timings (ms):
exclusion.robots.policy: 0.385
captures_list: 230.53
exclusion.robots: 0.399
RedisCDXSource: 0.705
PetaboxLoader3.resolve: 218.895 (4)
esindex: 0.012
CDXLines.iter: 27.074 (3)
LoadShardBlock: 198.076 (3)
PetaboxLoader3.datanode: 63.453 (4)
load_resource: 135.773
-->