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

Configure Feed

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

eepy-serial-host: new crate eepytool: use eepy-serial-host eepy-launcher: handle serial MaybeRefresh

+264 -116
+1
.idea/epd_firmware.iml
··· 17 17 <sourceFolder url="file://$MODULE_DIR$/eepy-launcher/src" isTestSource="false" /> 18 18 <sourceFolder url="file://$MODULE_DIR$/eepytool/src" isTestSource="false" /> 19 19 <sourceFolder url="file://$MODULE_DIR$/eepy-derive/src" isTestSource="false" /> 20 + <sourceFolder url="file://$MODULE_DIR$/eepy-serial-host/src" isTestSource="false" /> 20 21 <excludeFolder url="file://$MODULE_DIR$/fw16_epd_bsp/target" /> 21 22 <excludeFolder url="file://$MODULE_DIR$/target" /> 22 23 <excludeFolder url="file://$MODULE_DIR$/fw16-epd-bsp/target" />
+1
Cargo.toml
··· 6 6 "eepy", 7 7 "eepy-sys", 8 8 "eepy-serial", 9 + "eepy-serial-host", 9 10 "eepy-gui", 10 11 "eepy-example-app", 11 12 "elf2epb",
+27 -18
eepy-launcher/src/serial.rs
··· 7 7 use eepy_sys::flash::erase_and_program; 8 8 use eepy_sys::header::{slot, slot_ptr, Programs}; 9 9 use eepy_sys::image::refresh; 10 - use eepy_sys::{header, IMAGE_BYTES}; 10 + use eepy_sys::{header, image, 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; ··· 20 20 21 21 ReceivingImage { 22 22 fast_refresh: bool, 23 + maybe_refresh: bool, 23 24 index: usize, 24 25 }, 25 26 ··· 96 97 } 97 98 98 99 if HOST_APP.load(Ordering::Relaxed) { 99 - match SerialCommand::try_from(cmd_buf[0]) { 100 - Ok(SerialCommand::RefreshNormal) => *state = SerialState::ReceivingImage { fast_refresh: false, index: 0 }, 101 - Ok(SerialCommand::RefreshFast) => *state = SerialState::ReceivingImage { fast_refresh: true, index: 0 }, 102 - Ok(SerialCommand::ExitHostApp) => { 100 + match SerialCommand::from_repr(cmd_buf[0]) { 101 + Some(SerialCommand::RefreshNormal) => *state = SerialState::ReceivingImage { fast_refresh: false, maybe_refresh: false, index: 0 }, 102 + Some(SerialCommand::RefreshFast) => *state = SerialState::ReceivingImage { fast_refresh: true, maybe_refresh: false, index: 0 }, 103 + Some(SerialCommand::MaybeRefreshNormal) => *state = SerialState::ReceivingImage { fast_refresh: false, maybe_refresh: true, index: 0 }, 104 + Some(SerialCommand::MaybeRefreshFast) => *state = SerialState::ReceivingImage { fast_refresh: true, maybe_refresh: true, index: 0 }, 105 + Some(SerialCommand::ExitHostApp) => { 103 106 set_touch_enabled(true); 104 107 HOST_APP.store(false, Ordering::Relaxed); 105 108 NEEDS_REFRESH.store(true, Ordering::Relaxed); 106 109 write_all(serial, &[Response::Ack as u8]); 107 110 }, 108 - Ok(SerialCommand::NextEvent) => { 111 + Some(SerialCommand::NextEvent) => { 109 112 write_all(serial, &[Response::Ack as u8]); 110 113 write_all(serial, &postcard::to_vec::<_, 32>(&next_event()).unwrap()); 111 114 }, 112 - Ok(SerialCommand::EnableTouch) => { 115 + Some(SerialCommand::EnableTouch) => { 113 116 set_touch_enabled(true); 114 117 write_all(serial, &[Response::Ack as u8]); 115 118 }, 116 - Ok(SerialCommand::DisableTouch) => { 119 + Some(SerialCommand::DisableTouch) => { 117 120 set_touch_enabled(false); 118 121 write_all(serial, &[Response::Ack as u8]); 119 122 }, 120 - Ok(SerialCommand::EnterHostApp | SerialCommand::GetProgramSlot | SerialCommand::UploadProgram) => { 123 + Some(SerialCommand::EnterHostApp | SerialCommand::GetProgramSlot | SerialCommand::UploadProgram) => { 121 124 write_all(serial, &[Response::IncorrectMode as u8]); 122 125 } 123 - Err(_) => write_all(serial, &[Response::UnknownCommand as u8]), 126 + None => write_all(serial, &[Response::UnknownCommand as u8]), 124 127 } 125 128 } else { 126 - match SerialCommand::try_from(cmd_buf[0]) { 127 - Ok(SerialCommand::GetProgramSlot) => { 129 + match SerialCommand::from_repr(cmd_buf[0]) { 130 + Some(SerialCommand::GetProgramSlot) => { 128 131 if let Some(slot) = best_slot() { 129 132 write_all(serial, &[Response::Ack as u8, slot]); 130 133 PROG_SLOT.store(slot, Ordering::Relaxed); ··· 132 135 write_all(serial, &[Response::ProgramSlotsFull as u8]); 133 136 } 134 137 }, 135 - Ok(SerialCommand::UploadProgram) => { 138 + Some(SerialCommand::UploadProgram) => { 136 139 if PROG_SLOT.load(Ordering::Relaxed) == 0 { 137 140 write_all(serial, &[Response::NoProgramSlot as u8]); 138 141 } else { ··· 141 144 write_all(serial, &[Response::Ack as u8]); 142 145 } 143 146 }, 144 - Ok(SerialCommand::EnterHostApp) => { 147 + Some(SerialCommand::EnterHostApp) => { 145 148 HOST_APP.store(true, Ordering::Relaxed); 146 149 refresh(&[0u8; IMAGE_BYTES], false); 147 150 set_touch_enabled(false); 148 151 write_all(serial, &[Response::Ack as u8]); 149 152 }, 150 - Ok( 153 + Some( 151 154 SerialCommand::RefreshNormal 152 155 | SerialCommand::RefreshFast 156 + | SerialCommand::MaybeRefreshNormal 157 + | SerialCommand::MaybeRefreshFast 153 158 | SerialCommand::ExitHostApp 154 159 | SerialCommand::NextEvent 155 160 | SerialCommand::DisableTouch 156 161 | SerialCommand::EnableTouch 157 162 ) => write_all(serial, &[Response::IncorrectMode as u8]), 158 - Err(_) => write_all(serial, &[Response::UnknownCommand as u8]), 163 + None => write_all(serial, &[Response::UnknownCommand as u8]), 159 164 } 160 165 } 161 166 } 162 167 } 163 168 164 - SerialState::ReceivingImage { fast_refresh, index } => { 169 + SerialState::ReceivingImage { fast_refresh, maybe_refresh, index } => { 165 170 if let Ok(count) = serial.read(&mut buf[*index..]) { 166 171 *index += count; 167 172 if *index == IMAGE_BYTES { 168 - refresh(buf, *fast_refresh); 173 + if *maybe_refresh { 174 + image::maybe_refresh(buf, *fast_refresh); 175 + } else { 176 + image::refresh(buf, *fast_refresh); 177 + } 169 178 write_all(serial, &[Response::Ack as u8]); 170 179 *state = SerialState::ReadyForCommand; 171 180 }
+9
eepy-serial-host/Cargo.toml
··· 1 + [package] 2 + name = "eepy-serial-host" 3 + version = "0.1.0" 4 + edition = "2021" 5 + 6 + [dependencies] 7 + eepy-serial = { path = "../eepy-serial" } 8 + serialport.workspace = true 9 + postcard.workspace = true
+16
eepy-serial-host/src/image.rs
··· 1 + use eepy_serial::SerialCommand; 2 + use crate::{Error, HostApp, Serial}; 3 + 4 + pub const IMAGE_BYTES: usize = (240 * 416) / 8; 5 + 6 + impl Serial<HostApp> { 7 + pub fn refresh(&mut self, fast: bool, image: &[u8; IMAGE_BYTES]) -> Result<(), Error> { 8 + let cmd = if fast { SerialCommand::RefreshFast } else { SerialCommand::RefreshNormal }; 9 + self.write(cmd, image) 10 + } 11 + 12 + pub fn maybe_refresh(&mut self, fast: bool, image: &[u8; IMAGE_BYTES]) -> Result<(), Error> { 13 + let cmd = if fast { SerialCommand::MaybeRefreshFast } else { SerialCommand::MaybeRefreshNormal }; 14 + self.write(cmd, image) 15 + } 16 + }
+16
eepy-serial-host/src/input.rs
··· 1 + use eepy_serial::{Event, SerialCommand}; 2 + use crate::{Error, HostApp, Serial}; 3 + 4 + impl Serial<HostApp> { 5 + pub fn set_touch_enabled(&mut self, enabled: bool) -> Result<(), Error> { 6 + let cmd = if enabled { SerialCommand::EnableTouch } else { SerialCommand::DisableTouch }; 7 + self.write(cmd, &[]) 8 + } 9 + 10 + pub fn next_event(&mut self) -> Result<Option<Event>, Error> { 11 + self.write(SerialCommand::NextEvent, &[])?; 12 + let mut event_buf = [0u8; 32]; 13 + self.port.read(&mut event_buf)?; 14 + Ok(postcard::from_bytes(&event_buf)?) 15 + } 16 + }
+126
eepy-serial-host/src/lib.rs
··· 1 + pub mod image; 2 + pub mod input; 3 + pub mod program_upload; 4 + 5 + use std::fmt::{Display, Formatter}; 6 + use std::io; 7 + use std::io::{Read, Write}; 8 + use std::marker::PhantomData; 9 + use std::time::Duration; 10 + use serialport::SerialPort; 11 + use eepy_serial::{Response, SerialCommand, SerialError}; 12 + 13 + #[derive(Debug)] 14 + pub enum Error { 15 + InvalidResponse, 16 + DeserializeFailed(postcard::Error), 17 + Serial(SerialError), 18 + Io(io::Error), 19 + } 20 + 21 + impl From<SerialError> for Error { 22 + fn from(value: SerialError) -> Self { 23 + Self::Serial(value) 24 + } 25 + } 26 + 27 + impl From<io::Error> for Error { 28 + fn from(value: io::Error) -> Self { 29 + Self::Io(value) 30 + } 31 + } 32 + 33 + impl From<postcard::Error> for Error { 34 + fn from(value: postcard::Error) -> Self { 35 + Self::DeserializeFailed(value) 36 + } 37 + } 38 + 39 + impl Display for Error { 40 + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 41 + match self { 42 + Self::InvalidResponse => write!(f, "received invalid response"), 43 + Self::DeserializeFailed(e) => e.fmt(f), 44 + Self::Serial(e) => e.fmt(f), 45 + Self::Io(e) => e.fmt(f), 46 + } 47 + } 48 + } 49 + 50 + impl std::error::Error for Error {} 51 + 52 + pub trait SerialState {} 53 + pub trait CanEnter<S: SerialState>: SerialState {} 54 + 55 + pub enum Unknown {} 56 + impl SerialState for Unknown {} 57 + impl CanEnter<Normal> for Unknown {} 58 + impl CanEnter<HostApp> for Unknown {} 59 + 60 + pub enum Normal {} 61 + impl SerialState for Normal {} 62 + impl CanEnter<HostApp> for Normal {} 63 + 64 + pub enum HostApp {} 65 + impl SerialState for HostApp {} 66 + impl CanEnter<Normal> for HostApp {} 67 + 68 + pub struct Serial<S: SerialState> { 69 + port: Box<dyn SerialPort>, 70 + _marker: PhantomData<S>, 71 + } 72 + 73 + impl<T: SerialState> Serial<T> { 74 + pub(crate) fn write(&mut self, command: SerialCommand, data: &[u8]) -> Result<(), Error> { 75 + self.port.write_all(&[command as u8])?; 76 + self.port.write_all(data)?; 77 + let mut response_buf = [0u8]; 78 + self.port.read_exact(&mut response_buf)?; 79 + Response::from_repr(response_buf[0]) 80 + .map(|resp| Result::<(), SerialError>::from(resp).map_err(|e| Error::Serial(e))) 81 + .ok_or(Error::InvalidResponse)? 82 + } 83 + } 84 + 85 + impl Serial<Unknown> { 86 + pub fn new(port: &str) -> Result<Self, serialport::Error> { 87 + let port = serialport::new(port, 0) 88 + .timeout(Duration::from_secs(60)) 89 + .open()?; 90 + 91 + Ok(Self { 92 + port, 93 + _marker: PhantomData::default(), 94 + }) 95 + } 96 + } 97 + 98 + impl<T: CanEnter<Normal>> Serial<T> { 99 + pub fn normal(mut self) -> Result<Serial<Normal>, Error> { 100 + let res = self.write(SerialCommand::ExitHostApp, &[]); 101 + match res { 102 + Ok(()) | Err(Error::Serial(SerialError::IncorrectMode)) => { 103 + Ok(Serial { 104 + port: self.port, 105 + _marker: PhantomData::default(), 106 + }) 107 + }, 108 + Err(e) => Err(e), 109 + } 110 + } 111 + } 112 + 113 + impl<T: CanEnter<HostApp>> Serial<T> { 114 + pub fn host_app(mut self) -> Result<Serial<HostApp>, Error> { 115 + let res = self.write(SerialCommand::EnterHostApp, &[]); 116 + match res { 117 + Ok(()) | Err(Error::Serial(SerialError::IncorrectMode)) => { 118 + Ok(Serial { 119 + port: self.port, 120 + _marker: PhantomData::default(), 121 + }) 122 + }, 123 + Err(e) => Err(e), 124 + } 125 + } 126 + }
+16
eepy-serial-host/src/program_upload.rs
··· 1 + use std::io::Read; 2 + use eepy_serial::SerialCommand; 3 + use crate::{Error, Normal, Serial}; 4 + 5 + impl Serial<Normal> { 6 + pub fn get_slot(&mut self) -> Result<u8, Error> { 7 + self.write(SerialCommand::GetProgramSlot, &[])?; 8 + let mut slot_n = [0u8]; 9 + self.port.read_exact(&mut slot_n)?; 10 + Ok(slot_n[0]) 11 + } 12 + 13 + pub fn upload(&mut self, program: &[u8]) -> Result<(), Error> { 14 + self.write(SerialCommand::UploadProgram, &program) 15 + } 16 + }
+2 -1
eepy-serial/Cargo.toml
··· 4 4 edition = "2021" 5 5 6 6 [dependencies] 7 - eepy-sys = { path = "../eepy-sys" } 7 + eepy-sys = { path = "../eepy-sys" } 8 + strum.workspace = true
+12 -45
eepy-serial/src/lib.rs
··· 5 5 use core::fmt::{Display, Formatter}; 6 6 7 7 #[repr(u8)] 8 - #[derive(Copy, Clone, Debug, Eq, PartialEq)] 8 + #[derive(Copy, Clone, Debug, Eq, PartialEq, strum::FromRepr)] 9 9 pub enum SerialCommand { 10 10 /// Refresh the screen. Must be followed by exactly 12480 bytes of image data. 11 11 RefreshNormal = 0, 12 12 /// Fast refresh the screen. Must be followed by exactly 12480 bytes of image data. 13 13 RefreshFast = 1, 14 14 15 + MaybeRefreshNormal = 2, 16 + MaybeRefreshFast = 3, 17 + 15 18 /// Enter Host App mode. In this mode, events will not be processed by the builtin UI and can 16 19 /// instead be retrieved by host programs using the [SerialCommand::NextEvent] command. This 17 20 /// mode should be used by all host programs writing images to the display. 18 - EnterHostApp = 2, 21 + EnterHostApp = 4, 19 22 /// Exit Host App mode (see [SerialCommand::EnterHostApp]). 20 - ExitHostApp = 3, 23 + ExitHostApp = 5, 21 24 22 25 /// Get the next event. Only works in Host App mode. 23 - NextEvent = 4, 26 + NextEvent = 6, 24 27 25 28 /// Disable touch. Touch events will no longer be added to the event queue. Only works in Host 26 29 /// App mode. 27 - DisableTouch = 5, 30 + DisableTouch = 7, 28 31 /// Enable touch. 29 - EnableTouch = 6, 32 + EnableTouch = 8, 30 33 31 34 /// Get the program slot that will be used to store the next uploaded program. This command is 32 35 /// only available when not in Host App mode. 33 - GetProgramSlot = 7, 36 + GetProgramSlot = 9, 34 37 /// Upload a program. The program will be stored in the slot returned by the previous 35 38 /// GetProgramSlot call. The program uploaded must be linked correctly for the 36 39 /// slot. Only available when not in Host App mode. 37 - UploadProgram = 8, 38 - } 39 - 40 - impl TryFrom<u8> for SerialCommand { 41 - type Error = (); 42 - 43 - fn try_from(value: u8) -> Result<Self, Self::Error> { 44 - match value { 45 - x if x == SerialCommand::RefreshNormal as u8 => Ok(SerialCommand::RefreshNormal), 46 - x if x == SerialCommand::RefreshFast as u8 => Ok(SerialCommand::RefreshFast), 47 - x if x == SerialCommand::EnterHostApp as u8 => Ok(SerialCommand::EnterHostApp), 48 - x if x == SerialCommand::ExitHostApp as u8 => Ok(SerialCommand::ExitHostApp), 49 - x if x == SerialCommand::NextEvent as u8 => Ok(SerialCommand::NextEvent), 50 - x if x == SerialCommand::DisableTouch as u8 => Ok(SerialCommand::DisableTouch), 51 - x if x == SerialCommand::EnableTouch as u8 => Ok(SerialCommand::EnableTouch), 52 - x if x == SerialCommand::GetProgramSlot as u8 => Ok(SerialCommand::GetProgramSlot), 53 - x if x == SerialCommand::UploadProgram as u8 => Ok(SerialCommand::UploadProgram), 54 - 55 - _ => Err(()), 56 - } 57 - } 40 + UploadProgram = 10, 58 41 } 59 42 60 43 #[repr(u8)] 61 - #[derive(Copy, Clone, Debug, Eq, PartialEq)] 44 + #[derive(Copy, Clone, Debug, Eq, PartialEq, strum::FromRepr)] 62 45 pub enum Response { 63 46 UnknownCommand = 0x00, 64 47 IncorrectMode = 0x01, ··· 87 70 Response::ProgramSlotsFull => write!(f, "No program slot is available"), 88 71 Response::NoProgramSlot => write!(f, "Call GetProgramSlot before UploadProgram"), 89 72 Response::Ack => write!(f, "Success"), 90 - } 91 - } 92 - } 93 - 94 - impl TryFrom<u8> for Response { 95 - type Error = (); 96 - 97 - fn try_from(value: u8) -> Result<Self, Self::Error> { 98 - match value { 99 - x if x == Response::UnknownCommand as u8 => Ok(Response::UnknownCommand), 100 - x if x == Response::IncorrectMode as u8 => Ok(Response::IncorrectMode), 101 - x if x == Response::ProgramSlotsFull as u8 => Ok(Response::ProgramSlotsFull), 102 - x if x == Response::NoProgramSlot as u8 => Ok(Response::NoProgramSlot), 103 - x if x == Response::Ack as u8 => Ok(Response::Ack), 104 - 105 - _ => Err(()), 106 73 } 107 74 } 108 75 }
+2 -3
eepytool/Cargo.toml
··· 4 4 edition = "2021" 5 5 6 6 [dependencies] 7 - eepy-serial = { path = "../eepy-serial" } 8 - postcard.workspace = true 7 + eepy-serial-host = { path = "../eepy-serial-host" } 8 + color-eyre = "0.6.3" 9 9 clap.workspace = true 10 - serialport.workspace = true 11 10 tar.workspace = true 12 11 zstd.workspace = true
+36 -49
eepytool/src/main.rs
··· 1 1 use std::fs::File; 2 - use std::io::{Read, Write}; 2 + use std::io::Read; 3 3 use std::path::PathBuf; 4 - use std::time::Duration; 5 4 use clap::{Parser, Subcommand}; 6 - use serialport::SerialPort; 7 5 use tar::Archive; 8 - use eepy_serial::{Event, Response, SerialCommand, SerialError}; 6 + use eepy_serial_host::{Normal, Serial}; 7 + use eepy_serial_host::image::IMAGE_BYTES; 9 8 10 9 #[derive(Parser, Debug)] 11 10 #[command(version, about, long_about = None)] ··· 25 24 Refresh { 26 25 #[arg(long, action)] 27 26 fast: bool, 27 + #[arg(long, action)] 28 + maybe: bool, 28 29 #[arg(short, long)] 29 30 image: PathBuf, 30 31 }, ··· 40 41 41 42 use Subcommands::*; 42 43 43 - fn write(serial: &mut Box<dyn SerialPort>, command: SerialCommand, data: &[u8]) -> Result<(), SerialError> { 44 - serial.write_all(&[command as u8]).unwrap(); 45 - serial.write_all(data).unwrap(); 46 - let mut response_buf = [0u8]; 47 - serial.read_exact(&mut response_buf).unwrap(); 48 - Response::try_from(response_buf[0]).unwrap().into() 49 - } 44 + fn upload_program(serial: &mut Serial<Normal>, path: PathBuf) -> color_eyre::Result<()> { 45 + let slot_n = serial.get_slot()?; 50 46 51 - fn next_event(serial: &mut Box<dyn SerialPort>) -> Result<Option<Event>, SerialError> { 52 - write(serial, SerialCommand::NextEvent, &[])?; 53 - let mut event_buf = [0u8; 32]; 54 - serial.read(&mut event_buf).unwrap(); 55 - Ok(postcard::from_bytes(&event_buf).unwrap()) 56 - } 57 - 58 - fn upload_program(serial: &mut Box<dyn SerialPort>, path: PathBuf) -> Result<(), SerialError> { 59 - write(serial, SerialCommand::GetProgramSlot, &[])?; 60 - let mut slot_n = [0u8]; 61 - serial.read_exact(&mut slot_n).unwrap(); 62 - let slot_n = slot_n[0]; 63 - 64 - let file = File::open(path).unwrap(); 65 - let zstd_reader = zstd::stream::read::Decoder::new(file).unwrap(); 47 + let file = File::open(path)?; 48 + let zstd_reader = zstd::stream::read::Decoder::new(file)?; 66 49 let mut tar = Archive::new(zstd_reader); 67 - for file in tar.entries().unwrap() { 68 - let mut file = file.unwrap(); 69 - if file.path().unwrap().to_str().unwrap().ends_with(&format!(".s{slot_n:02}.epb")) { 70 - println!("Uploading {}", file.path().unwrap().to_str().unwrap()); 50 + for file in tar.entries()? { 51 + let mut file = file?; 52 + if file.path()?.to_str().unwrap().ends_with(&format!(".s{slot_n:02}.epb")) { 53 + println!("Uploading {}", file.path()?.to_str().unwrap()); 71 54 let mut buf = vec![0u8; file.size() as usize]; 72 - file.read_exact(&mut buf).unwrap(); 73 - write(serial, SerialCommand::UploadProgram, &buf)?; 55 + file.read_exact(&mut buf)?; 56 + serial.upload(&buf)?; 74 57 return Ok(()); 75 58 } 76 59 } ··· 78 61 panic!("App package did not contain binary for slot {slot_n}"); 79 62 } 80 63 81 - fn main() { 64 + fn main() -> color_eyre::Result<()> { 82 65 let args = Args::parse(); 83 66 84 - // Baud rate setting doesn't matter for pure USB serial so use 0 85 - let mut port = serialport::new(&args.serial_port, 0) 86 - .timeout(Duration::from_secs(60)) 87 - .open() 88 - .expect(&format!("Failed to open serial port {}", args.serial_port)); 67 + let port = Serial::new(&args.serial_port)?; 89 68 90 69 match args.command { 91 - EnterHostApp => write(&mut port, SerialCommand::EnterHostApp, &[]).unwrap(), 92 - ExitHostApp => write(&mut port, SerialCommand::ExitHostApp, &[]).unwrap(), 93 - Refresh { fast, image } => { 94 - let data = std::fs::read(image).unwrap(); 95 - let cmd = if fast { SerialCommand::RefreshFast } else { SerialCommand::RefreshNormal }; 96 - write(&mut port, cmd, &data).unwrap(); 70 + EnterHostApp => { port.host_app()?; }, 71 + ExitHostApp => { port.normal()?; }, 72 + Refresh { fast, maybe, image } => { 73 + let mut buf = [0u8; IMAGE_BYTES]; 74 + let mut file = File::open(image)?; 75 + file.read_exact(&mut buf)?; 76 + 77 + if maybe { 78 + port.host_app()?.maybe_refresh(fast, &buf)?; 79 + } else { 80 + port.host_app()?.refresh(fast, &buf)?; 81 + } 97 82 }, 98 - DisableTouch => write(&mut port, SerialCommand::DisableTouch, &[]).unwrap(), 99 - EnableTouch => write(&mut port, SerialCommand::EnableTouch, &[]).unwrap(), 100 - NextEvent => println!("{:?}", next_event(&mut port).unwrap()), 101 - UploadProgram { package } => upload_program(&mut port, package).unwrap(), 102 - }; 83 + DisableTouch => port.host_app()?.set_touch_enabled(false)?, 84 + EnableTouch => port.host_app()?.set_touch_enabled(true)?, 85 + NextEvent => println!("{:?}", port.host_app()?.next_event()?), 86 + UploadProgram { package } => upload_program(&mut port.normal()?, package)?, 87 + } 88 + 89 + Ok(()) 103 90 }