diff --git a/Cargo.toml b/Cargo.toml index 7e4e139..5f7eb9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,3 +37,7 @@ path = "examples/rust/multi_zone.rs" [[example]] name = "error_handling" path = "examples/rust/error_handling.rs" + +[[example]] +name = "standby" +path = "examples/rust/standby.rs" diff --git a/examples/c/Makefile b/examples/c/Makefile index eeccaf4..9174a14 100644 --- a/examples/c/Makefile +++ b/examples/c/Makefile @@ -4,7 +4,7 @@ LIB_DIR = ../../target/debug LIB = $(LIB_DIR)/libglider_api.so LDFLAGS = -Wl,-rpath,$(abspath $(LIB_DIR)) -TARGETS = getting_started redraw mode_guide multi_zone error_handling +TARGETS = getting_started redraw mode_guide multi_zone error_handling standby all: $(TARGETS) diff --git a/examples/c/standby.c b/examples/c/standby.c new file mode 100644 index 0000000..345012c --- /dev/null +++ b/examples/c/standby.c @@ -0,0 +1,43 @@ +/* + * Standby — put the display into low-power standby and wake it again. + * + * The display blanks the panel and stops updating while in standby. + * Call glider_exit_standby() to resume normal operation. + * + * make standby && ./standby + */ + +#include +#include +#include "glider-api.h" + +#define STANDBY_SECS 3 + +int main(void) { + Display *display = glider_open(); + if (!display) { + fprintf(stderr, "Could not connect to display.\n"); + return 1; + } + + printf("Entering standby...\n"); + if (glider_enter_standby(display) != SUCCESS) { + fprintf(stderr, "enter_standby failed.\n"); + glider_close(display); + return 1; + } + printf("Display is in standby. Waiting %d seconds...\n", STANDBY_SECS); + + sleep(STANDBY_SECS); + + printf("Exiting standby...\n"); + if (glider_exit_standby(display) != SUCCESS) { + fprintf(stderr, "exit_standby failed.\n"); + glider_close(display); + return 1; + } + printf("Done — display resumed.\n"); + + glider_close(display); + return 0; +} diff --git a/examples/python/standby.py b/examples/python/standby.py new file mode 100644 index 0000000..bdf13e1 --- /dev/null +++ b/examples/python/standby.py @@ -0,0 +1,33 @@ +""" +Standby — put the display into low-power standby and wake it again. + +The display blanks the panel and stops updating while in standby. +Call exit_standby() to resume normal operation. + +Run with a Glider connected to a device running firmware v??? or later. + + python examples/python/standby.py +""" + +import time +from glider_api import Display, DisplayConfig + +STANDBY_SECS = 3 + +config = DisplayConfig.glider_standard() + +try: + display = Display.new_with_config(config) +except TypeError as e: + print(f"Could not connect: {e}") + raise SystemExit(1) + +print("Entering standby...") +display.enter_standby() +print(f"Display is in standby. Waiting {STANDBY_SECS} seconds...") + +time.sleep(STANDBY_SECS) + +print("Exiting standby...") +display.exit_standby() +print("Done — display resumed.") diff --git a/examples/rust/standby.rs b/examples/rust/standby.rs new file mode 100644 index 0000000..c27fe5f --- /dev/null +++ b/examples/rust/standby.rs @@ -0,0 +1,35 @@ +//! Standby — put the display into low-power standby and wake it again. +//! +//! The display blanks the panel and stops updating while in standby. +//! Call `exit_standby` to resume normal operation. +//! +//! ``` +//! cargo run --example standby +//! ``` + +use std::{thread, time::Duration}; + +use glider_api::{Display, DisplayConfig}; + +const STANDBY_SECS: u64 = 3; + +fn main() -> Result<(), Box> { + let config = DisplayConfig::glider_standard(); + + let display = Display::new_with_config(&config).map_err(|e| { + eprintln!("Could not connect: {e}"); + e + })?; + + println!("Entering standby..."); + display.enter_standby()?; + println!("Display is in standby. Waiting {STANDBY_SECS} seconds..."); + + thread::sleep(Duration::from_secs(STANDBY_SECS)); + + println!("Exiting standby..."); + display.exit_standby()?; + println!("Done — display resumed."); + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index ac1456c..f856dae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,6 +102,8 @@ pub enum Mode { } const REPORT_ID_CONTROL: u8 = 5; +const USBCMD_POWERDOWN: u8 = 0x01; +const USBCMD_POWERUP: u8 = 0x02; const USBCMD_REDRAW: u8 = 0x04; const USBCMD_SETMODE: u8 = 0x05; @@ -294,6 +296,37 @@ impl Display { device.0.read_timeout(&mut response, 200).to_py_err()?; parse_response(&response) } + + /// Put the display into standby, blanking the panel and reducing power draw. + /// + /// The display stops updating until [`Display::exit_standby`] is called. + /// Call [`Display::exit_standby`] to resume normal operation. + /// + /// Raises `TypeError` on USB communication errors or if the firmware + /// rejects the command. + pub fn enter_standby(&self) -> PyResult<()> { + let buf = build_display_packet(USBCMD_POWERDOWN, 0x0000, &Rect::new(0, 0, 0, 0)); + let device = self.device.lock().unwrap(); + device.0.write(&buf).to_py_err()?; + + let mut response: [u8; 16] = [0; 16]; + device.0.read_timeout(&mut response, 200).to_py_err()?; + parse_response(&response) + } + + /// Wake the display from standby and resume normal operation. + /// + /// Raises `TypeError` on USB communication errors or if the firmware + /// rejects the command. + pub fn exit_standby(&self) -> PyResult<()> { + let buf = build_display_packet(USBCMD_POWERUP, 0x0000, &Rect::new(0, 0, 0, 0)); + let device = self.device.lock().unwrap(); + device.0.write(&buf).to_py_err()?; + + let mut response: [u8; 16] = [0; 16]; + device.0.read_timeout(&mut response, 200).to_py_err()?; + parse_response(&response) + } } fn build_display_packet(cmd: u8, param: u16, area: &Rect) -> BytesMut { @@ -389,6 +422,28 @@ pub extern "C" fn glider_redraw(d: *mut Display, area: Rect) -> Response { unsafe { &*d }.redraw(&area).into() } +/// Put the display into standby, blanking the panel and reducing power draw. +/// +/// Returns `SUCCESS` (85) on success or `FAILURE` (0) on error. +#[no_mangle] +pub extern "C" fn glider_enter_standby(d: *mut Display) -> Response { + if d.is_null() { + return Response::Failure; + } + unsafe { &*d }.enter_standby().into() +} + +/// Wake the display from standby and resume normal operation. +/// +/// Returns `SUCCESS` (85) on success or `FAILURE` (0) on error. +#[no_mangle] +pub extern "C" fn glider_exit_standby(d: *mut Display) -> Response { + if d.is_null() { + return Response::Failure; + } + unsafe { &*d }.exit_standby().into() +} + #[pymodule] fn glider_api(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?;