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

eepy-launcher: improve main page, implement app updates

+69 -27
+13 -1
eepy-launcher/src/main.rs
··· 7 7 extern crate panic_halt; 8 8 9 9 use core::arch::asm; 10 + use core::mem::offset_of; 10 11 use core::sync::atomic::Ordering; 11 12 use usb_device::bus::UsbBusAllocator; 12 13 use eepy_gui::draw_target::EpdDrawTarget; ··· 16 17 use eepy_sys::usb::{self, UsbBus}; 17 18 use usb_device::prelude::*; 18 19 use usbd_serial::SerialPort; 19 - use eepy_sys::eepy_app; 20 + use eepy_sys::{eepy_app, flash}; 21 + use eepy_sys::header::{slot_ptr, ProgramSlotHeader}; 20 22 use crate::serial::{HOST_APP, NEEDS_REFRESH, NEEDS_REFRESH_PROGRAMS}; 21 23 use crate::ui::MainGui; 22 24 23 25 static mut USB: Option<UsbBusAllocator<UsbBus>> = None; 24 26 static mut USB_DEVICE: Option<UsbDevice<UsbBus>> = None; 25 27 static mut USB_SERIAL: Option<SerialPort<UsbBus>> = None; 28 + 29 + pub(crate) unsafe fn delete_program(slot: u8) { 30 + let ptr = unsafe { slot_ptr(slot) }; 31 + let mut buf = [0u8; 256]; 32 + unsafe { buf.copy_from_slice(core::slice::from_raw_parts(ptr, 256)) }; 33 + let offset = offset_of!(ProgramSlotHeader, len); 34 + buf[offset..offset + 4].copy_from_slice(&0usize.to_ne_bytes()); 35 + 36 + unsafe { flash::program(slot as u32 * 512 * 1024, &buf) }; 37 + } 26 38 27 39 #[eepy_app(name = "Launcher")] 28 40 fn main() {
+19 -3
eepy-launcher/src/serial.rs
··· 5 5 use usbd_serial::SerialPort; 6 6 use eepy_serial::{Response, SerialCommand}; 7 7 use eepy_sys::flash::erase_and_program; 8 - use eepy_sys::header::{slot, slot_ptr}; 8 + use eepy_sys::header::{slot, slot_ptr, Programs}; 9 9 use eepy_sys::image::refresh; 10 - use eepy_sys::IMAGE_BYTES; 10 + use eepy_sys::{header, IMAGE_BYTES}; 11 11 use eepy_sys::input::{next_event, set_touch_enabled}; 12 12 use eepy_sys::misc::{debug, info, trace}; 13 13 use eepy_sys::usb::UsbBus; 14 - use crate::{USB_DEVICE, USB_SERIAL}; 14 + use crate::{delete_program, USB_DEVICE, USB_SERIAL}; 15 15 use crate::ui::flashing::draw_flashing_ui; 16 16 17 17 #[derive(Copy, Clone, Debug)] ··· 225 225 buf[0..4].copy_from_slice(&(erase_cycles + 1).to_ne_bytes()); 226 226 227 227 unsafe { write_flash(&buf[0..4096], slot, 0) }; 228 + 229 + let this_header = unsafe { header::slot(slot) }; 230 + let this_name = unsafe { core::slice::from_raw_parts((*this_header).name_ptr, (*this_header).name_len) }; 231 + 232 + // If there is an old program with the same name, delete it 233 + Programs::new() 234 + .filter_map(|prog| unsafe { 235 + let name = core::slice::from_raw_parts((*prog).name_ptr, (*prog).name_len); 236 + if (*prog).slot() != slot && name == this_name { 237 + Some((*prog).slot()) 238 + } else { 239 + None 240 + } 241 + }) 242 + .for_each(|slot| unsafe { delete_program(slot) }); 243 + 228 244 PROG_SLOT.store(0, Ordering::Relaxed); 229 245 230 246 NEEDS_REFRESH_PROGRAMS.store(true, Ordering::Relaxed);
+11 -10
eepy-launcher/src/ui/page/app_info.rs
··· 1 - use core::mem::offset_of; 2 1 use eepy_gui::element::button::Button; 3 2 use embedded_graphics::prelude::*; 4 3 use embedded_graphics::text::Text; 5 4 use eepy_gui::draw_target::EpdDrawTarget; 6 5 use eepy_gui::element::{Gui, DEFAULT_TEXT_STYLE}; 7 - use eepy_sys::flash; 8 - use eepy_sys::header::{slot, slot_ptr, ProgramSlotHeader}; 6 + use eepy_sys::header::slot; 9 7 use eepy_sys::input_common::Event; 8 + use crate::delete_program; 10 9 use crate::ui::MainGui; 11 10 use crate::ui::page::main::MainPage; 12 11 ··· 66 65 } 67 66 68 67 fn tick(&mut self, target: &mut EpdDrawTarget, ev: Event) -> Self::Output { 68 + let mut refresh = false; 69 + 69 70 let b = self.back_button.tick(target, ev); 70 71 if b.clicked { 71 72 return Some(MainGui::MainPage(MainPage::new())); 72 73 } 74 + refresh |= b.needs_refresh; 73 75 74 76 let d = self.delete_button.tick(target, ev); 75 77 if d.clicked { 76 - let ptr = unsafe { slot_ptr(self.slot) }; 77 - let mut buf = [0u8; 256]; 78 - unsafe { buf.copy_from_slice(core::slice::from_raw_parts(ptr, 256)) }; 79 - let offset = offset_of!(ProgramSlotHeader, len); 80 - buf[offset..offset + 4].copy_from_slice(&[0; 4]); 81 - 82 - unsafe { flash::program(self.slot as u32 * 512 * 1024, &buf) }; 78 + unsafe { delete_program(self.slot) }; 83 79 return Some(MainGui::MainPage(MainPage::new())); 80 + } 81 + refresh |= d.needs_refresh; 82 + 83 + if refresh { 84 + target.refresh(true); 84 85 } 85 86 86 87 None
+22 -9
eepy-launcher/src/ui/page/main.rs
··· 16 16 } 17 17 18 18 impl MainPage { 19 + fn app_button(x: usize, y: usize, label: &'static str) -> Button<'static> { 20 + let x_coord = 10 + 115 * x as i32; 21 + let y_coord = 8 + 25 * y as i32; 22 + Button::with_default_style( 23 + Rectangle::new(Point::new(x_coord, y_coord), Size::new(105, 20)), 24 + label, 25 + false, 26 + ) 27 + } 28 + 19 29 pub(crate) fn refresh_buttons(&mut self) { 20 30 let mut programs = Programs::new(); 21 31 22 32 for y in 0..16 { 23 33 for x in 0..2 { 34 + // Reserve the first space for the scratchpad 35 + if x == 0 && y == 0 { 36 + continue; 37 + } 38 + 24 39 if let Some(prog) = programs.next() { 25 40 let bi = y * 2 + x; 26 - let x_coord = if x == 0 { 10 } else { 125 }; 27 - let y_coord = 35 + 23 * y as i32; 28 - let button = Button::with_default_style( 29 - Rectangle::new(Point::new(x_coord, y_coord), Size::new(105, 20)), 30 - unsafe { (*prog).name().unwrap() }, 31 - false, 32 - ); 41 + let button = Self::app_button(x, y, unsafe { (*prog).name().unwrap_or("<invalid>") }); 33 42 let slot_num = unsafe { (&*prog).slot() }; 34 43 self.app_buttons[bi] = Some((button, slot_num)) 35 44 } ··· 39 48 40 49 pub(crate) fn new() -> Self { 41 50 let mut res = Self { 42 - scratchpad_button: Button::with_default_style_auto_sized(Point::new(10, 10), "Scratchpad", true), 51 + scratchpad_button: Button::with_default_style( 52 + Rectangle::new(Point::new(10, 8), Size::new(105, 20)), 53 + "Scratchpad", 54 + true 55 + ), 43 56 app_buttons: [const { None }; 32], 44 57 }; 45 58 res.refresh_buttons(); ··· 69 82 draw_target.refresh(true); 70 83 } 71 84 72 - for b in &mut self.app_buttons { 85 + for b in &mut self.app_buttons[1..] { 73 86 if let Some((button, s)) = b { 74 87 let response = button.tick(draw_target, ev); 75 88
+4 -4
eepy-sys/src/header.rs
··· 128 128 return false; 129 129 } 130 130 131 - if self.name().is_err() { 131 + if unsafe { self.name() }.is_err() { 132 132 #[cfg(feature = "defmt")] 133 133 warn!("Program name is not valid UTF-8"); 134 134 return false; 135 135 } 136 136 137 - if self.version().is_err() { 137 + if unsafe { self.version() }.is_err() { 138 138 #[cfg(feature = "defmt")] 139 139 warn!("Program version string is not valid UTF-8"); 140 140 return false; ··· 187 187 crc == self.crc 188 188 } 189 189 190 - pub fn name(&self) -> Result<&str, Utf8Error> { 190 + pub unsafe fn name(&self) -> Result<&str, Utf8Error> { 191 191 unsafe { 192 192 core::str::from_utf8(core::slice::from_raw_parts(self.name_ptr, self.name_len)) 193 193 } 194 194 } 195 195 196 - pub fn version(&self) -> Result<&str, Utf8Error> { 196 + pub unsafe fn version(&self) -> Result<&str, Utf8Error> { 197 197 unsafe { 198 198 core::str::from_utf8(core::slice::from_raw_parts(self.version_ptr, self.version_len)) 199 199 }