firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
1#![no_std]
2#![no_main]
3
4extern crate panic_halt;
5
6use core::arch::asm;
7use embedded_graphics::geometry::AnchorPoint;
8use embedded_graphics::pixelcolor::BinaryColor;
9use embedded_graphics::prelude::*;
10use embedded_graphics::primitives::{Circle, Line, PrimitiveStyle, Rectangle};
11use eepy_gui::draw_target::EpdDrawTarget;
12use eepy_gui::element::button::Button;
13use eepy_gui::element::Gui;
14use eepy_gui::element::slider::Slider;
15use eepy_sys::exec::exec;
16use eepy_sys::image::RefreshBlockMode;
17use eepy_sys::input::{has_event, next_event, set_touch_enabled, Event, TouchEventType};
18use eepy_sys::header::{ProgramSlotHeader, Programs};
19
20#[link_section = ".header"]
21#[used]
22static HEADER: ProgramSlotHeader = ProgramSlotHeader::partial(
23 "Launcher",
24 env!("CARGO_PKG_VERSION"),
25 entry,
26);
27
28enum Page {
29 MainPage,
30 ScratchpadPage,
31}
32
33struct MainPage {
34 scratchpad_button: Button<'static>,
35 app_buttons: [Option<(Button<'static>, u8)>; 32],
36}
37
38impl MainPage {
39 fn new() -> Self {
40 let mut buttons = [const { None }; 32];
41 let mut programs = Programs::new();
42
43 for y in 0..16 {
44 for x in 0..2 {
45 if let Some(prog) = programs.next() {
46 let bi = y * 2 + x;
47 let x_coord = if x == 0 { 10 } else { 125 };
48 let y_coord = 35 + 23 * y as i32;
49 let button = Button::with_default_style(
50 Rectangle::new(Point::new(x_coord, y_coord), Size::new(105, 20)),
51 unsafe { (*prog).name().unwrap() },
52 false,
53 );
54 let slot_num = unsafe { (&*prog).slot() };
55 buttons[bi] = Some((button, slot_num))
56 }
57 }
58 }
59
60 Self {
61 scratchpad_button: Button::with_default_style_auto_sized(Point::new(10, 10), "Scratchpad", true),
62 app_buttons: buttons,
63 }
64 }
65}
66
67impl Gui for MainPage {
68 type Output = Option<Page>;
69
70 fn draw_init(&self, draw_target: &mut EpdDrawTarget) {
71 self.scratchpad_button.draw_init(draw_target);
72 for b in &self.app_buttons {
73 if let Some((button, _)) = b {
74 button.draw_init(draw_target);
75 }
76 }
77 }
78
79 fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output {
80 let mut needs_refresh = false;
81
82 let s = self.scratchpad_button.tick(draw_target, ev);
83 if s.clicked {
84 return Some(Page::ScratchpadPage);
85 } else if s.needs_refresh {
86 draw_target.refresh(true, RefreshBlockMode::BlockAcknowledge);
87 }
88
89 for b in &mut self.app_buttons {
90 if let Some((button, s)) = b {
91 let response = button.tick(draw_target, ev);
92
93 if response.clicked {
94 exec(*s);
95 }
96
97 needs_refresh |= response.needs_refresh;
98 }
99 }
100
101 if needs_refresh {
102 draw_target.refresh(true, RefreshBlockMode::NonBlocking);
103 }
104
105 None
106 }
107}
108
109struct ScratchpadPage {
110 exit_button: Button<'static>,
111 clear_button: Button<'static>,
112 toggle_button: Button<'static>,
113 slider: Slider,
114 eraser: bool,
115 prev_pos: Option<Point>,
116}
117
118impl ScratchpadPage {
119 fn new() -> Self {
120 let exit_button = Button::with_default_style_auto_sized(Point::new(10, 416 - 10 - 20), "Exit", true);
121
122 let next_pos = exit_button
123 .bounding_box()
124 .translate(Point::new(10, 0))
125 .anchor_point(AnchorPoint::TopRight);
126 let clear_button = Button::with_default_style_auto_sized(next_pos, "Clear", true);
127
128 let next_pos = clear_button
129 .bounding_box()
130 .translate(Point::new(10, 0))
131 .anchor_point(AnchorPoint::TopRight);
132 let toggle_button = Button::with_default_style_auto_sized(next_pos, "Eraser", true);
133
134 let slider = Slider::with_default_style(Point::new(20, 20), 100, 1, 10, 2);
135
136 Self {
137 exit_button,
138 clear_button,
139 toggle_button,
140 slider,
141 eraser: false,
142 prev_pos: None,
143 }
144 }
145}
146
147impl Gui for ScratchpadPage {
148 type Output = Option<Page>;
149
150 fn draw_init(&self, draw_target: &mut EpdDrawTarget) {
151 self.exit_button.draw_init(draw_target);
152 self.clear_button.draw_init(draw_target);
153 self.toggle_button.draw_init(draw_target);
154 self.slider.draw_init(draw_target);
155 }
156
157 fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output {
158 let mut refresh: Option<RefreshBlockMode> = None;
159 let mut handle_drawing: bool = true;
160
161 let e = self.exit_button.tick(draw_target, ev);
162 if e.clicked {
163 return Some(Page::MainPage);
164 }
165 if e.needs_refresh {
166 refresh = Some(RefreshBlockMode::BlockAcknowledge);
167 }
168
169 let c = self.clear_button.tick(draw_target, ev);
170 if c.clicked {
171 draw_target.clear(BinaryColor::Off).unwrap();
172 self.draw_init(draw_target);
173 draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge);
174 return None;
175 }
176 if c.needs_refresh {
177 refresh = Some(RefreshBlockMode::BlockAcknowledge);
178 }
179
180 let t = self.toggle_button.tick(draw_target, ev);
181 if t.clicked {
182 self.eraser = !self.eraser;
183 self.toggle_button.label = if self.eraser { "Pen" } else { "Eraser" };
184 refresh = Some(RefreshBlockMode::NonBlocking);
185 }
186 if t.needs_refresh {
187 refresh = Some(RefreshBlockMode::BlockAcknowledge);
188 }
189
190 if self.slider.tick(draw_target, ev) {
191 refresh = Some(RefreshBlockMode::NonBlocking);
192 handle_drawing = false;
193 }
194
195 if let Event::Touch(ev) = ev {
196 if handle_drawing && matches!(ev.ev_type, TouchEventType::Move | TouchEventType::Up) {
197 if let Some(prev) = self.prev_pos {
198 let style = PrimitiveStyle::with_stroke(BinaryColor::from(!self.eraser), self.slider.value as u32);
199 Line::new(prev, ev.eg_point())
200 .into_styled(style)
201 .draw(draw_target)
202 .unwrap();
203 // Draw a circle at each end of the line
204 let circle_style = PrimitiveStyle::with_fill(BinaryColor::from(!self.eraser));
205 Circle::with_center(prev, self.slider.value as u32 - 1)
206 .into_styled(circle_style)
207 .draw(draw_target)
208 .unwrap();
209 Circle::with_center(ev.eg_point(), self.slider.value as u32 - 1)
210 .into_styled(circle_style)
211 .draw(draw_target)
212 .unwrap();
213
214 self.draw_init(draw_target);
215
216 if refresh.is_none() {
217 refresh = Some(RefreshBlockMode::NonBlocking);
218 }
219 }
220 }
221
222 if matches!(ev.ev_type, TouchEventType::Down | TouchEventType::Move) {
223 self.prev_pos = Some(ev.eg_point());
224 }
225 }
226
227 if let Some(mode) = refresh {
228 draw_target.refresh(true, mode);
229 }
230
231 None
232 }
233}
234
235struct MainGui {
236 current_page: Page,
237 main_page: MainPage,
238 scratchpad_page: ScratchpadPage,
239}
240
241impl MainGui {
242 fn new() -> Self {
243 Self {
244 current_page: Page::MainPage,
245 main_page: MainPage::new(),
246 scratchpad_page: ScratchpadPage::new(),
247 }
248 }
249
250 fn get_current_page(&self) -> &dyn Gui<Output = Option<Page>> {
251 match self.current_page {
252 Page::MainPage => &self.main_page,
253 Page::ScratchpadPage => &self.scratchpad_page,
254 }
255 }
256
257 fn get_current_page_mut(&mut self) -> &mut dyn Gui<Output = Option<Page>> {
258 match self.current_page {
259 Page::MainPage => &mut self.main_page,
260 Page::ScratchpadPage => &mut self.scratchpad_page,
261 }
262 }
263}
264
265impl Gui for MainGui {
266 type Output = ();
267
268 fn draw_init(&self, draw_target: &mut EpdDrawTarget) {
269 self.get_current_page().draw_init(draw_target);
270 }
271
272 fn tick(&mut self, draw_target: &mut EpdDrawTarget, ev: Event) -> Self::Output {
273 if let Some(page) = self.get_current_page_mut().tick(draw_target, ev) {
274 self.current_page = page;
275 draw_target.clear(BinaryColor::Off).unwrap();
276 self.draw_init(draw_target);
277 draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge);
278 }
279 }
280}
281
282
283#[no_mangle]
284pub extern "C" fn entry() {
285 let mut draw_target = EpdDrawTarget::new();
286 set_touch_enabled(true);
287 let mut gui = MainGui::new();
288 gui.draw_init(&mut draw_target);
289 draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge);
290
291 loop {
292 while let Some(ev) = next_event() {
293 gui.tick(&mut draw_target, ev);
294 }
295
296 if !has_event() {
297 // has_event() is a syscall. The SVCall exception is a WFE wakeup event, so we need two
298 // WFEs so we don't immediately wake up.
299 unsafe { asm!("wfe", "wfe") };
300 }
301 }
302}