firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
0

Configure Feed

Select the types of activity you want to include in your feed.

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}