Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
247 lines
7.3 KiB
Rust
247 lines
7.3 KiB
Rust
use std::{
|
|
fs::File,
|
|
io,
|
|
os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
|
|
path::Path,
|
|
process,
|
|
};
|
|
|
|
/// A handle represents an owned and valid Windows handle to a file-like
|
|
/// object.
|
|
///
|
|
/// When an owned handle is dropped, then the underlying raw handle is closed.
|
|
/// To get a borrowed handle, use `HandleRef`.
|
|
#[derive(Debug)]
|
|
pub struct Handle(File);
|
|
|
|
impl AsRawHandle for Handle {
|
|
fn as_raw_handle(&self) -> RawHandle {
|
|
self.0.as_raw_handle()
|
|
}
|
|
}
|
|
|
|
impl FromRawHandle for Handle {
|
|
unsafe fn from_raw_handle(handle: RawHandle) -> Handle {
|
|
Handle(File::from_raw_handle(handle))
|
|
}
|
|
}
|
|
|
|
impl IntoRawHandle for Handle {
|
|
fn into_raw_handle(self) -> RawHandle {
|
|
self.0.into_raw_handle()
|
|
}
|
|
}
|
|
|
|
impl Handle {
|
|
/// Create an owned handle to the given file.
|
|
///
|
|
/// When the returned handle is dropped, the file is closed.
|
|
///
|
|
/// Note that if the given file represents a handle to a directory, then
|
|
/// it is generally required that it have been opened with the
|
|
/// [`FILE_FLAG_BACKUP_SEMANTICS`] flag in order to use it in various
|
|
/// calls such as `information` or `typ`. To have this done automatically
|
|
/// for you, use the `from_path_any` constructor.
|
|
///
|
|
/// [`FILE_FLAG_BACKUP_SEMANTICS`]: https://docs.microsoft.com/en-us/windows/desktop/api/FileAPI/nf-fileapi-createfilea
|
|
pub fn from_file(file: File) -> Handle {
|
|
Handle(file)
|
|
}
|
|
|
|
/// Open a file to the given file path, and return an owned handle to that
|
|
/// file.
|
|
///
|
|
/// When the returned handle is dropped, the file is closed.
|
|
///
|
|
/// If there was a problem opening the file, then the corresponding error
|
|
/// is returned.
|
|
pub fn from_path<P: AsRef<Path>>(path: P) -> io::Result<Handle> {
|
|
Ok(Handle::from_file(File::open(path)?))
|
|
}
|
|
|
|
/// Like `from_path`, but supports opening directory handles as well.
|
|
///
|
|
/// If you use `from_path` on a directory, then subsequent queries using
|
|
/// that handle will fail.
|
|
pub fn from_path_any<P: AsRef<Path>>(path: P) -> io::Result<Handle> {
|
|
use std::fs::OpenOptions;
|
|
use std::os::windows::fs::OpenOptionsExt;
|
|
use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS;
|
|
|
|
let file = OpenOptions::new()
|
|
.read(true)
|
|
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
|
|
.open(path)?;
|
|
Ok(Handle::from_file(file))
|
|
}
|
|
|
|
/// Return this handle as a standard `File` reference.
|
|
pub fn as_file(&self) -> &File {
|
|
&self.0
|
|
}
|
|
|
|
/// Return this handle as a standard `File` mutable reference.
|
|
pub fn as_file_mut(&mut self) -> &mut File {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
/// Represents a borrowed and valid Windows handle to a file-like object, such
|
|
/// as stdin/stdout/stderr or an actual file.
|
|
///
|
|
/// When a borrowed handle is dropped, then the underlying raw handle is
|
|
/// **not** closed. To get an owned handle, use `Handle`.
|
|
#[derive(Debug)]
|
|
pub struct HandleRef(HandleRefInner);
|
|
|
|
/// The representation of a HandleRef, on which we define a custom Drop impl
|
|
/// that avoids closing the underlying raw handle.
|
|
#[derive(Debug)]
|
|
struct HandleRefInner(Option<File>);
|
|
|
|
impl Drop for HandleRefInner {
|
|
fn drop(&mut self) {
|
|
self.0.take().unwrap().into_raw_handle();
|
|
}
|
|
}
|
|
|
|
impl AsRawHandle for HandleRef {
|
|
fn as_raw_handle(&self) -> RawHandle {
|
|
self.as_file().as_raw_handle()
|
|
}
|
|
}
|
|
|
|
impl Clone for HandleRef {
|
|
fn clone(&self) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(self.as_raw_handle()) }
|
|
}
|
|
}
|
|
|
|
impl HandleRef {
|
|
/// Create a borrowed handle to stdin.
|
|
///
|
|
/// When the returned handle is dropped, stdin is not closed.
|
|
pub fn stdin() -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(io::stdin().as_raw_handle()) }
|
|
}
|
|
|
|
/// Create a handle to stdout.
|
|
///
|
|
/// When the returned handle is dropped, stdout is not closed.
|
|
pub fn stdout() -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(io::stdout().as_raw_handle()) }
|
|
}
|
|
|
|
/// Create a handle to stderr.
|
|
///
|
|
/// When the returned handle is dropped, stderr is not closed.
|
|
pub fn stderr() -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(io::stderr().as_raw_handle()) }
|
|
}
|
|
|
|
/// Create a borrowed handle to the given file.
|
|
///
|
|
/// When the returned handle is dropped, the file is not closed.
|
|
pub fn from_file(file: &File) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(file.as_raw_handle()) }
|
|
}
|
|
|
|
/// Create a borrowed handle from the given raw handle.
|
|
///
|
|
/// Note that unlike the `FromRawHandle` trait, this constructor does
|
|
/// **not** consume ownership of the given handle. That is, when the
|
|
/// borrowed handle created by this constructor is dropped, the underlying
|
|
/// handle will not be closed.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// This is unsafe because there is no guarantee that the given raw handle
|
|
/// is a valid handle. The caller must ensure this is true before invoking
|
|
/// this constructor.
|
|
pub unsafe fn from_raw_handle(handle: RawHandle) -> HandleRef {
|
|
HandleRef(HandleRefInner(Some(File::from_raw_handle(handle))))
|
|
}
|
|
|
|
/// Return this handle as a standard `File` reference.
|
|
pub fn as_file(&self) -> &File {
|
|
(self.0).0.as_ref().unwrap()
|
|
}
|
|
|
|
/// Return this handle as a standard `File` mutable reference.
|
|
pub fn as_file_mut(&mut self) -> &mut File {
|
|
(self.0).0.as_mut().unwrap()
|
|
}
|
|
}
|
|
|
|
/// Construct borrowed and valid Windows handles from file-like objects.
|
|
pub trait AsHandleRef {
|
|
/// A borrowed handle that wraps the raw handle of the `Self` object.
|
|
fn as_handle_ref(&self) -> HandleRef;
|
|
|
|
/// A convenience routine for extracting a `HandleRef` from `Self`, and
|
|
/// then extracting a raw handle from the `HandleRef`.
|
|
fn as_raw(&self) -> RawHandle {
|
|
self.as_handle_ref().as_raw_handle()
|
|
}
|
|
}
|
|
|
|
impl<'a, T: AsHandleRef> AsHandleRef for &'a T {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
(**self).as_handle_ref()
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for Handle {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(self.as_raw_handle()) }
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for HandleRef {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
self.clone()
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for File {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
HandleRef::from_file(self)
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for io::Stdin {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(self.as_raw_handle()) }
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for io::Stdout {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(self.as_raw_handle()) }
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for io::Stderr {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(self.as_raw_handle()) }
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for process::ChildStdin {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(self.as_raw_handle()) }
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for process::ChildStdout {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(self.as_raw_handle()) }
|
|
}
|
|
}
|
|
|
|
impl AsHandleRef for process::ChildStderr {
|
|
fn as_handle_ref(&self) -> HandleRef {
|
|
unsafe { HandleRef::from_raw_handle(self.as_raw_handle()) }
|
|
}
|
|
}
|