feat: add display UI with splash screen, dashboard, and RSSI sparkline
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>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+1
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"140946cfffbe55e0d12174361f24c6636c1b91dbbdf45d56e918bdc26f87ab59",".github/FUNDING.yml":"0c65f392d32a8639ba7986bbb42ca124505b462122382f314c89d84c95dd27f1",".github/workflows/ci.yml":"305081bfe28e36a1ebcd9a1d89915c6b810e3cc8cbba51a99c7ef22e72e0da93","COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.lock":"d8b0f4e5c08384ae0fa3e5c6e087e6be29ab848939bab7d4aa75d7ecac36afd0","Cargo.toml":"90ba2d06ba1317a8807c94508ecf576a2c10ebc6b2445f3d842ba734415a6a68","Cargo.toml.orig":"92ffccf05797f435a3587c5e020ec052703d6a64e597f31478eea76229361aa2","LICENSE-MIT":"cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14","README.md":"a7f54a0abf91365baa51097afe72f4ed0156ef2152cf22800c280af24357dd9a","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","rustfmt.toml":"1ca600239a27401c4a43f363cf3f38183a212affc1f31bff3ae93234bbaec228","src/console.rs":"2b2443c05a9d796b9c28b911312af4131136d2dc177ee16115fb929576dce84d","src/file.rs":"694b5ab307b72d4b658e3aff2bc8ad7c1dbced6c807447ff9f09fd0f529fef44","src/lib.rs":"0ea298f753f7dfb951376ec9e6c1801415eb430c2030e44b280e810cc639be91","src/sysinfo.rs":"92e7d87d33d5a5bb8ec5f83ed9f172927f57c88a3ac7d1d6da6dad362f9e1c31","src/win.rs":"7ac48f929bc41f17d2de5c68666640060ca56a2ab1db45bf1267d7059eb4207b"},"package":"c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "803874c57dc1f10ecd42f7c86d9de72f53818432"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
github: [BurntSushi]
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
name: ci
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: '00 01 * * *'
|
||||
|
||||
# The section is needed to drop write-all permissions that are granted on
|
||||
# `schedule` event. By specifying any permission explicitly all others are set
|
||||
# to none. By using the principle of least privilege the damage a compromised
|
||||
# workflow can do (because of an injection or compromised third party tool or
|
||||
# action) is restricted. Currently the worklow doesn't need any additional
|
||||
# permission except for pulling the code. Adding labels to issues, commenting
|
||||
# on pull-requests, etc. may need additional permissions:
|
||||
#
|
||||
# Syntax for this section:
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
|
||||
#
|
||||
# Reference for how to assign permissions on a job-by-job basis:
|
||||
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
|
||||
#
|
||||
# Reference for available permissions that we can enable if needed:
|
||||
# https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
|
||||
permissions:
|
||||
# to fetch code (actions/checkout)
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: test
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- build: pinned
|
||||
os: windows-latest
|
||||
rust: 1.72.0
|
||||
- build: stable
|
||||
os: windows-latest
|
||||
rust: stable
|
||||
- build: beta
|
||||
os: windows-latest
|
||||
rust: beta
|
||||
- build: nightly
|
||||
os: windows-latest
|
||||
rust: nightly
|
||||
- build: win-gnu
|
||||
os: windows-latest
|
||||
rust: stable-x86_64-gnu
|
||||
- build: linux
|
||||
os: ubuntu-latest
|
||||
rust: stable
|
||||
- build: macos
|
||||
os: macos-latest
|
||||
rust: stable
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
- run: cargo build --verbose
|
||||
- run: cargo doc --verbose
|
||||
- run: cargo test --verbose
|
||||
- name: Show all computer names
|
||||
run: cargo test --lib sysinfo::tests::itworks -- --nocapture
|
||||
|
||||
rustfmt:
|
||||
name: rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
- name: Check formatting
|
||||
run: |
|
||||
cargo fmt --all -- --check
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
This project is dual-licensed under the Unlicense and MIT licenses.
|
||||
|
||||
You may use this code under the terms of either license.
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||
build = false
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "A dumping ground for high level safe wrappers over windows-sys."
|
||||
homepage = "https://github.com/BurntSushi/winapi-util"
|
||||
documentation = "https://docs.rs/winapi-util"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"windows",
|
||||
"windows-sys",
|
||||
"util",
|
||||
"win",
|
||||
]
|
||||
categories = [
|
||||
"os::windows-apis",
|
||||
"external-ffi-bindings",
|
||||
]
|
||||
license = "Unlicense OR MIT"
|
||||
repository = "https://github.com/BurntSushi/winapi-util"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-pc-windows-msvc"]
|
||||
|
||||
[lib]
|
||||
name = "winapi_util"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows-sys]
|
||||
version = ">=0.48.0, <=0.61.*"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_Console",
|
||||
"Win32_System_SystemInformation",
|
||||
]
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Andrew Gallant
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
winapi-util
|
||||
===========
|
||||
This crate provides a smattering of safe wrappers around various parts of the
|
||||
[windows-sys](https://crates.io/crates/windows-sys) crate.
|
||||
|
||||
[](https://github.com/BurntSushi/winapi-util/actions)
|
||||
[](https://crates.io/crates/winapi-util)
|
||||
|
||||
Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org).
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
https://docs.rs/winapi-util
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
Run `cargo add winapi-util` to add this dependency to your `Cargo.toml` file.
|
||||
|
||||
|
||||
### Notes
|
||||
|
||||
This crate was born out of frustration with having to write lots of little
|
||||
ffi utility bindings in a variety of crates in order to get Windows support.
|
||||
Eventually, I started needing to copy & paste a lot of those utility routines.
|
||||
Since they are utility routines, they often don't make sense to expose directly
|
||||
in the crate in which they are defined. Instead of continuing this process,
|
||||
I decided to make a crate instead.
|
||||
|
||||
Normally, I'm not a huge fan of "utility" crates like this that don't have a
|
||||
well defined scope, but this is primarily a practical endeavor to make it
|
||||
easier to isolate Windows specific ffi code.
|
||||
|
||||
While I don't have a long term vision for this crate, I will welcome additional
|
||||
PRs that add more high level routines/types on an as-needed basis.
|
||||
|
||||
**WARNING:** I am not a Windows developer, so extra review to make sure I've
|
||||
got things right is most appreciated.
|
||||
|
||||
|
||||
### Naming
|
||||
|
||||
This crate was originally born on top of `winapi`, and thus, it is called
|
||||
`winapi-util`. As time passed, Microsoft eventually started providing their
|
||||
own official `windows` and `windows-sys` crates. As a result, [`winapi` itself
|
||||
got less activity](https://github.com/BurntSushi/winapi-util/pull/13#issuecomment-1991282893).
|
||||
As the ecosystem moved on to `windows-sys`, it became clear that `winapi-util`
|
||||
should too. Thus, its name is now officially historical and not reflective
|
||||
of what it actually does.
|
||||
|
||||
|
||||
### Minimum Rust version policy
|
||||
|
||||
This crate's minimum supported `rustc` version is `1.72.0`.
|
||||
|
||||
The current policy is that the minimum Rust version required to use this crate
|
||||
can be increased in non-breaking version updates. For example, if `crate 1.0`
|
||||
requires Rust 1.20.0, then `crate 1.0.z` for all values of `z` will also
|
||||
require Rust 1.20.0 or newer. However, `crate 1.y` for `y > 0` may require a
|
||||
newer minimum version of Rust.
|
||||
|
||||
In general, this crate will be conservative with respect to the minimum
|
||||
supported version of Rust.
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
max_width = 79
|
||||
use_small_heuristics = "max"
|
||||
Vendored
+407
@@ -0,0 +1,407 @@
|
||||
use std::{io, mem};
|
||||
|
||||
use windows_sys::Win32::Foundation::HANDLE;
|
||||
use windows_sys::Win32::System::Console::{GetConsoleMode, SetConsoleMode};
|
||||
use windows_sys::Win32::System::Console::{
|
||||
GetConsoleScreenBufferInfo, SetConsoleTextAttribute,
|
||||
CONSOLE_SCREEN_BUFFER_INFO, ENABLE_VIRTUAL_TERMINAL_PROCESSING,
|
||||
FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_INTENSITY, FOREGROUND_RED,
|
||||
};
|
||||
|
||||
use crate::{AsHandleRef, HandleRef};
|
||||
|
||||
use FOREGROUND_BLUE as FG_BLUE;
|
||||
use FOREGROUND_GREEN as FG_GREEN;
|
||||
use FOREGROUND_INTENSITY as FG_INTENSITY;
|
||||
use FOREGROUND_RED as FG_RED;
|
||||
|
||||
const FG_CYAN: u16 = FG_BLUE | FG_GREEN;
|
||||
const FG_MAGENTA: u16 = FG_BLUE | FG_RED;
|
||||
const FG_YELLOW: u16 = FG_GREEN | FG_RED;
|
||||
const FG_WHITE: u16 = FG_BLUE | FG_GREEN | FG_RED;
|
||||
|
||||
/// Query the given handle for information about the console's screen buffer.
|
||||
///
|
||||
/// The given handle should represent a console. Otherwise, an error is
|
||||
/// returned.
|
||||
///
|
||||
/// This corresponds to calling [`GetConsoleScreenBufferInfo`].
|
||||
///
|
||||
/// [`GetConsoleScreenBufferInfo`]: https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo
|
||||
pub fn screen_buffer_info<H: AsHandleRef>(
|
||||
h: H,
|
||||
) -> io::Result<ScreenBufferInfo> {
|
||||
unsafe {
|
||||
let mut info: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
|
||||
let rc = GetConsoleScreenBufferInfo(h.as_raw() as HANDLE, &mut info);
|
||||
if rc == 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(ScreenBufferInfo(info))
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the text attributes of the console represented by the given handle.
|
||||
///
|
||||
/// This corresponds to calling [`SetConsoleTextAttribute`].
|
||||
///
|
||||
/// [`SetConsoleTextAttribute`]: https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute
|
||||
pub fn set_text_attributes<H: AsHandleRef>(
|
||||
h: H,
|
||||
attributes: u16,
|
||||
) -> io::Result<()> {
|
||||
if unsafe { SetConsoleTextAttribute(h.as_raw() as HANDLE, attributes) }
|
||||
== 0
|
||||
{
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Query the mode of the console represented by the given handle.
|
||||
///
|
||||
/// This corresponds to calling [`GetConsoleMode`], which describes the return
|
||||
/// value.
|
||||
///
|
||||
/// [`GetConsoleMode`]: https://docs.microsoft.com/en-us/windows/console/getconsolemode
|
||||
pub fn mode<H: AsHandleRef>(h: H) -> io::Result<u32> {
|
||||
let mut mode = 0;
|
||||
if unsafe { GetConsoleMode(h.as_raw() as HANDLE, &mut mode) } == 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(mode)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the mode of the console represented by the given handle.
|
||||
///
|
||||
/// This corresponds to calling [`SetConsoleMode`], which describes the format
|
||||
/// of the mode parameter.
|
||||
///
|
||||
/// [`SetConsoleMode`]: https://docs.microsoft.com/en-us/windows/console/setconsolemode
|
||||
pub fn set_mode<H: AsHandleRef>(h: H, mode: u32) -> io::Result<()> {
|
||||
if unsafe { SetConsoleMode(h.as_raw() as HANDLE, mode) } == 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents console screen buffer information such as size, cursor position
|
||||
/// and styling attributes.
|
||||
///
|
||||
/// This wraps a [`CONSOLE_SCREEN_BUFFER_INFO`].
|
||||
///
|
||||
/// [`CONSOLE_SCREEN_BUFFER_INFO`]: https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str
|
||||
#[derive(Clone)]
|
||||
pub struct ScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO);
|
||||
|
||||
impl ScreenBufferInfo {
|
||||
/// Returns the size of the console screen buffer, in character columns and
|
||||
/// rows.
|
||||
///
|
||||
/// This corresponds to `dwSize`.
|
||||
pub fn size(&self) -> (i16, i16) {
|
||||
(self.0.dwSize.X, self.0.dwSize.Y)
|
||||
}
|
||||
|
||||
/// Returns the position of the cursor in terms of column and row
|
||||
/// coordinates of the console screen buffer.
|
||||
///
|
||||
/// This corresponds to `dwCursorPosition`.
|
||||
pub fn cursor_position(&self) -> (i16, i16) {
|
||||
(self.0.dwCursorPosition.X, self.0.dwCursorPosition.Y)
|
||||
}
|
||||
|
||||
/// Returns the character attributes associated with this console.
|
||||
///
|
||||
/// This corresponds to `wAttributes`.
|
||||
///
|
||||
/// See [`char info`] for more details.
|
||||
///
|
||||
/// [`char info`]: https://docs.microsoft.com/en-us/windows/console/char-info-str
|
||||
pub fn attributes(&self) -> u16 {
|
||||
self.0.wAttributes
|
||||
}
|
||||
|
||||
/// Returns the maximum size of the console window, in character columns
|
||||
/// and rows, given the current screen buffer size and font and the screen
|
||||
/// size.
|
||||
pub fn max_window_size(&self) -> (i16, i16) {
|
||||
(self.0.dwMaximumWindowSize.X, self.0.dwMaximumWindowSize.Y)
|
||||
}
|
||||
|
||||
/// Returns the console screen buffer coordinates of the upper-left and
|
||||
/// lower-right corners of the display window.
|
||||
///
|
||||
/// This corresponds to `srWindow`.
|
||||
pub fn window_rect(&self) -> SmallRect {
|
||||
SmallRect {
|
||||
left: self.0.srWindow.Left,
|
||||
top: self.0.srWindow.Top,
|
||||
right: self.0.srWindow.Right,
|
||||
bottom: self.0.srWindow.Bottom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the coordinates of the upper left and lower right corners of a rectangle.
|
||||
///
|
||||
/// This corresponds to [`SMALL_RECT`].
|
||||
///
|
||||
/// [`SMALL_RECT`]: https://docs.microsoft.com/en-us/windows/console/small-rect-str
|
||||
pub struct SmallRect {
|
||||
pub left: i16,
|
||||
pub top: i16,
|
||||
pub right: i16,
|
||||
pub bottom: i16,
|
||||
}
|
||||
|
||||
/// A Windows console.
|
||||
///
|
||||
/// This represents a very limited set of functionality available to a Windows
|
||||
/// console. In particular, it can only change text attributes such as color
|
||||
/// and intensity. This may grow over time. If you need more routines, please
|
||||
/// file an issue and/or PR.
|
||||
///
|
||||
/// There is no way to "write" to this console. Simply write to
|
||||
/// stdout or stderr instead, while interleaving instructions to the console
|
||||
/// to change text attributes.
|
||||
///
|
||||
/// A common pitfall when using a console is to forget to flush writes to
|
||||
/// stdout before setting new text attributes.
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # #[cfg(windows)]
|
||||
/// # {
|
||||
/// use winapi_util::console::{Console, Color, Intense};
|
||||
///
|
||||
/// let mut con = Console::stdout().unwrap();
|
||||
/// con.fg(Intense::Yes, Color::Cyan).unwrap();
|
||||
/// println!("This text will be intense cyan.");
|
||||
/// con.reset().unwrap();
|
||||
/// println!("This text will be normal.");
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Console {
|
||||
kind: HandleKind,
|
||||
start_attr: TextAttributes,
|
||||
cur_attr: TextAttributes,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum HandleKind {
|
||||
Stdout,
|
||||
Stderr,
|
||||
}
|
||||
|
||||
impl HandleKind {
|
||||
fn handle(&self) -> HandleRef {
|
||||
match *self {
|
||||
HandleKind::Stdout => HandleRef::stdout(),
|
||||
HandleKind::Stderr => HandleRef::stderr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Console {
|
||||
/// Get a console for a standard I/O stream.
|
||||
fn create_for_stream(kind: HandleKind) -> io::Result<Console> {
|
||||
let h = kind.handle();
|
||||
let info = screen_buffer_info(&h)?;
|
||||
let attr = TextAttributes::from_word(info.attributes());
|
||||
Ok(Console { kind, start_attr: attr, cur_attr: attr })
|
||||
}
|
||||
|
||||
/// Create a new Console to stdout.
|
||||
///
|
||||
/// If there was a problem creating the console, then an error is returned.
|
||||
pub fn stdout() -> io::Result<Console> {
|
||||
Self::create_for_stream(HandleKind::Stdout)
|
||||
}
|
||||
|
||||
/// Create a new Console to stderr.
|
||||
///
|
||||
/// If there was a problem creating the console, then an error is returned.
|
||||
pub fn stderr() -> io::Result<Console> {
|
||||
Self::create_for_stream(HandleKind::Stderr)
|
||||
}
|
||||
|
||||
/// Applies the current text attributes.
|
||||
fn set(&mut self) -> io::Result<()> {
|
||||
set_text_attributes(self.kind.handle(), self.cur_attr.to_word())
|
||||
}
|
||||
|
||||
/// Apply the given intensity and color attributes to the console
|
||||
/// foreground.
|
||||
///
|
||||
/// If there was a problem setting attributes on the console, then an error
|
||||
/// is returned.
|
||||
pub fn fg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
|
||||
self.cur_attr.fg_color = color;
|
||||
self.cur_attr.fg_intense = intense;
|
||||
self.set()
|
||||
}
|
||||
|
||||
/// Apply the given intensity and color attributes to the console
|
||||
/// background.
|
||||
///
|
||||
/// If there was a problem setting attributes on the console, then an error
|
||||
/// is returned.
|
||||
pub fn bg(&mut self, intense: Intense, color: Color) -> io::Result<()> {
|
||||
self.cur_attr.bg_color = color;
|
||||
self.cur_attr.bg_intense = intense;
|
||||
self.set()
|
||||
}
|
||||
|
||||
/// Reset the console text attributes to their original settings.
|
||||
///
|
||||
/// The original settings correspond to the text attributes on the console
|
||||
/// when this `Console` value was created.
|
||||
///
|
||||
/// If there was a problem setting attributes on the console, then an error
|
||||
/// is returned.
|
||||
pub fn reset(&mut self) -> io::Result<()> {
|
||||
self.cur_attr = self.start_attr;
|
||||
self.set()
|
||||
}
|
||||
|
||||
/// Toggle virtual terminal processing.
|
||||
///
|
||||
/// This method attempts to toggle virtual terminal processing for this
|
||||
/// console. If there was a problem toggling it, then an error returned.
|
||||
/// On success, the caller may assume that toggling it was successful.
|
||||
///
|
||||
/// When virtual terminal processing is enabled, characters emitted to the
|
||||
/// console are parsed for VT100 and similar control character sequences
|
||||
/// that control color and other similar operations.
|
||||
pub fn set_virtual_terminal_processing(
|
||||
&mut self,
|
||||
yes: bool,
|
||||
) -> io::Result<()> {
|
||||
let vt = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
let handle = self.kind.handle();
|
||||
let old_mode = mode(&handle)?;
|
||||
let new_mode = if yes { old_mode | vt } else { old_mode & !vt };
|
||||
if old_mode == new_mode {
|
||||
return Ok(());
|
||||
}
|
||||
set_mode(&handle, new_mode)
|
||||
}
|
||||
}
|
||||
|
||||
/// A representation of text attributes for the Windows console.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
struct TextAttributes {
|
||||
fg_color: Color,
|
||||
fg_intense: Intense,
|
||||
bg_color: Color,
|
||||
bg_intense: Intense,
|
||||
}
|
||||
|
||||
impl TextAttributes {
|
||||
fn to_word(&self) -> u16 {
|
||||
let mut w = 0;
|
||||
w |= self.fg_color.to_fg();
|
||||
w |= self.fg_intense.to_fg();
|
||||
w |= self.bg_color.to_bg();
|
||||
w |= self.bg_intense.to_bg();
|
||||
w
|
||||
}
|
||||
|
||||
fn from_word(word: u16) -> TextAttributes {
|
||||
TextAttributes {
|
||||
fg_color: Color::from_fg(word),
|
||||
fg_intense: Intense::from_fg(word),
|
||||
bg_color: Color::from_bg(word),
|
||||
bg_intense: Intense::from_bg(word),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to use intense colors or not.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Intense {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
impl Intense {
|
||||
fn to_bg(&self) -> u16 {
|
||||
self.to_fg() << 4
|
||||
}
|
||||
|
||||
fn from_bg(word: u16) -> Intense {
|
||||
Intense::from_fg(word >> 4)
|
||||
}
|
||||
|
||||
fn to_fg(&self) -> u16 {
|
||||
match *self {
|
||||
Intense::No => 0,
|
||||
Intense::Yes => FG_INTENSITY,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_fg(word: u16) -> Intense {
|
||||
if word & FG_INTENSITY > 0 {
|
||||
Intense::Yes
|
||||
} else {
|
||||
Intense::No
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The set of available colors for use with a Windows console.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Color {
|
||||
Black,
|
||||
Blue,
|
||||
Green,
|
||||
Red,
|
||||
Cyan,
|
||||
Magenta,
|
||||
Yellow,
|
||||
White,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
fn to_bg(&self) -> u16 {
|
||||
self.to_fg() << 4
|
||||
}
|
||||
|
||||
fn from_bg(word: u16) -> Color {
|
||||
Color::from_fg(word >> 4)
|
||||
}
|
||||
|
||||
fn to_fg(&self) -> u16 {
|
||||
match *self {
|
||||
Color::Black => 0,
|
||||
Color::Blue => FG_BLUE,
|
||||
Color::Green => FG_GREEN,
|
||||
Color::Red => FG_RED,
|
||||
Color::Cyan => FG_CYAN,
|
||||
Color::Magenta => FG_MAGENTA,
|
||||
Color::Yellow => FG_YELLOW,
|
||||
Color::White => FG_WHITE,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_fg(word: u16) -> Color {
|
||||
match word & 0b111 {
|
||||
FG_BLUE => Color::Blue,
|
||||
FG_GREEN => Color::Green,
|
||||
FG_RED => Color::Red,
|
||||
FG_CYAN => Color::Cyan,
|
||||
FG_MAGENTA => Color::Magenta,
|
||||
FG_YELLOW => Color::Yellow,
|
||||
FG_WHITE => Color::White,
|
||||
_ => Color::Black,
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+166
@@ -0,0 +1,166 @@
|
||||
use std::{io, mem};
|
||||
|
||||
use windows_sys::Win32::Foundation::HANDLE;
|
||||
use windows_sys::Win32::Foundation::{GetLastError, FILETIME, NO_ERROR};
|
||||
use windows_sys::Win32::Storage::FileSystem::{
|
||||
GetFileInformationByHandle, GetFileType, BY_HANDLE_FILE_INFORMATION,
|
||||
FILE_ATTRIBUTE_HIDDEN,
|
||||
};
|
||||
|
||||
use crate::AsHandleRef;
|
||||
|
||||
/// Return various pieces of information about a file.
|
||||
///
|
||||
/// This includes information such as a file's size, unique identifier and
|
||||
/// time related fields.
|
||||
///
|
||||
/// This corresponds to calling [`GetFileInformationByHandle`].
|
||||
///
|
||||
/// [`GetFileInformationByHandle`]: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle
|
||||
pub fn information<H: AsHandleRef>(h: H) -> io::Result<Information> {
|
||||
unsafe {
|
||||
let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed();
|
||||
let rc = GetFileInformationByHandle(h.as_raw() as HANDLE, &mut info);
|
||||
if rc == 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
};
|
||||
Ok(Information(info))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the file type of the given handle.
|
||||
///
|
||||
/// If there was a problem querying the file type, then an error is returned.
|
||||
///
|
||||
/// This corresponds to calling [`GetFileType`].
|
||||
///
|
||||
/// [`GetFileType`]: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfiletype
|
||||
pub fn typ<H: AsHandleRef>(h: H) -> io::Result<Type> {
|
||||
unsafe {
|
||||
let rc = GetFileType(h.as_raw() as HANDLE);
|
||||
if rc == 0 && GetLastError() != NO_ERROR {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(Type(rc))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if and only if the given file attributes contain the
|
||||
/// `FILE_ATTRIBUTE_HIDDEN` attribute.
|
||||
pub fn is_hidden(file_attributes: u64) -> bool {
|
||||
file_attributes & (FILE_ATTRIBUTE_HIDDEN as u64) > 0
|
||||
}
|
||||
|
||||
/// Represents file information such as creation time, file size, etc.
|
||||
///
|
||||
/// This wraps a [`BY_HANDLE_FILE_INFORMATION`].
|
||||
///
|
||||
/// [`BY_HANDLE_FILE_INFORMATION`]: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information
|
||||
#[derive(Clone)]
|
||||
pub struct Information(BY_HANDLE_FILE_INFORMATION);
|
||||
|
||||
impl Information {
|
||||
/// Returns file attributes.
|
||||
///
|
||||
/// This corresponds to `dwFileAttributes`.
|
||||
pub fn file_attributes(&self) -> u64 {
|
||||
self.0.dwFileAttributes as u64
|
||||
}
|
||||
|
||||
/// Returns true if and only if this file information has the
|
||||
/// `FILE_ATTRIBUTE_HIDDEN` attribute.
|
||||
pub fn is_hidden(&self) -> bool {
|
||||
is_hidden(self.file_attributes())
|
||||
}
|
||||
|
||||
/// Return the creation time, if one exists.
|
||||
///
|
||||
/// This corresponds to `ftCreationTime`.
|
||||
pub fn creation_time(&self) -> Option<u64> {
|
||||
filetime_to_u64(self.0.ftCreationTime)
|
||||
}
|
||||
|
||||
/// Return the last access time, if one exists.
|
||||
///
|
||||
/// This corresponds to `ftLastAccessTime`.
|
||||
pub fn last_access_time(&self) -> Option<u64> {
|
||||
filetime_to_u64(self.0.ftLastAccessTime)
|
||||
}
|
||||
|
||||
/// Return the last write time, if one exists.
|
||||
///
|
||||
/// This corresponds to `ftLastWriteTime`.
|
||||
pub fn last_write_time(&self) -> Option<u64> {
|
||||
filetime_to_u64(self.0.ftLastWriteTime)
|
||||
}
|
||||
|
||||
/// Return the serial number of the volume that the file is on.
|
||||
///
|
||||
/// This corresponds to `dwVolumeSerialNumber`.
|
||||
pub fn volume_serial_number(&self) -> u64 {
|
||||
self.0.dwVolumeSerialNumber as u64
|
||||
}
|
||||
|
||||
/// Return the file size, in bytes.
|
||||
///
|
||||
/// This corresponds to `nFileSizeHigh` and `nFileSizeLow`.
|
||||
pub fn file_size(&self) -> u64 {
|
||||
((self.0.nFileSizeHigh as u64) << 32) | (self.0.nFileSizeLow as u64)
|
||||
}
|
||||
|
||||
/// Return the number of links to this file.
|
||||
///
|
||||
/// This corresponds to `nNumberOfLinks`.
|
||||
pub fn number_of_links(&self) -> u64 {
|
||||
self.0.nNumberOfLinks as u64
|
||||
}
|
||||
|
||||
/// Return the index of this file. The index of a file is a purpotedly
|
||||
/// unique identifier for a file within a particular volume.
|
||||
pub fn file_index(&self) -> u64 {
|
||||
((self.0.nFileIndexHigh as u64) << 32) | (self.0.nFileIndexLow as u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a Windows file type.
|
||||
///
|
||||
/// This wraps the result of [`GetFileType`].
|
||||
///
|
||||
/// [`GetFileType`]: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-getfiletype
|
||||
#[derive(Clone)]
|
||||
pub struct Type(u32);
|
||||
|
||||
impl Type {
|
||||
/// Returns true if this type represents a character file, which is
|
||||
/// typically an LPT device or a console.
|
||||
pub fn is_char(&self) -> bool {
|
||||
self.0 == ::windows_sys::Win32::Storage::FileSystem::FILE_TYPE_CHAR
|
||||
}
|
||||
|
||||
/// Returns true if this type represents a disk file.
|
||||
pub fn is_disk(&self) -> bool {
|
||||
self.0 == ::windows_sys::Win32::Storage::FileSystem::FILE_TYPE_DISK
|
||||
}
|
||||
|
||||
/// Returns true if this type represents a sock, named pipe or an
|
||||
/// anonymous pipe.
|
||||
pub fn is_pipe(&self) -> bool {
|
||||
self.0 == ::windows_sys::Win32::Storage::FileSystem::FILE_TYPE_PIPE
|
||||
}
|
||||
|
||||
/// Returns true if this type is not known.
|
||||
///
|
||||
/// Note that this never corresponds to a failure.
|
||||
pub fn is_unknown(&self) -> bool {
|
||||
self.0 == ::windows_sys::Win32::Storage::FileSystem::FILE_TYPE_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
fn filetime_to_u64(t: FILETIME) -> Option<u64> {
|
||||
let v = ((t.dwHighDateTime as u64) << 32) | (t.dwLowDateTime as u64);
|
||||
if v == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
This crate provides a smattering of safe routines for parts of windows-sys. The
|
||||
primary purpose of this crate is to serve as a dumping ground for various
|
||||
utility functions that make interactions with windows-sys safe. This permits the
|
||||
centralization of `unsafe` when dealing with Windows APIs, and thus makes it
|
||||
easier to audit.
|
||||
|
||||
A key abstraction in this crate is the combination of the
|
||||
[`Handle`](struct.Handle.html)
|
||||
and
|
||||
[`HandleRef`](struct.HandleRef.html)
|
||||
types. Both represent a valid Windows handle to an I/O-like object, where
|
||||
`Handle` is owned (the resource is closed when the handle is dropped) and
|
||||
`HandleRef` is borrowed (the resource is not closed when the handle is
|
||||
dropped). Many of the routines in this crate work on handles and accept
|
||||
anything that can be safely converted into a `HandleRef`. This includes
|
||||
standard library types such as `File`, `Stdin`, `Stdout` and `Stderr`.
|
||||
|
||||
Note that this crate is completely empty on non-Windows platforms.
|
||||
*/
|
||||
|
||||
#[cfg(windows)]
|
||||
pub use win::*;
|
||||
|
||||
/// Safe routines for dealing with the Windows console.
|
||||
#[cfg(windows)]
|
||||
pub mod console;
|
||||
/// Safe routines for dealing with files and handles on Windows.
|
||||
#[cfg(windows)]
|
||||
pub mod file;
|
||||
#[cfg(windows)]
|
||||
/// Safe routines for querying various Windows specific properties.
|
||||
pub mod sysinfo;
|
||||
#[cfg(windows)]
|
||||
mod win;
|
||||
Vendored
+161
@@ -0,0 +1,161 @@
|
||||
use std::{ffi::OsString, io};
|
||||
|
||||
use windows_sys::Win32::System::SystemInformation::{
|
||||
GetComputerNameExW, COMPUTER_NAME_FORMAT,
|
||||
};
|
||||
|
||||
/// The type of name to be retrieved by [`get_computer_name`].
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum ComputerNameKind {
|
||||
/// The name of the DNS domain assigned to the local computer. If the local
|
||||
/// computer is a node in a cluster, lpBuffer receives the DNS domain name
|
||||
/// of the cluster virtual server.
|
||||
DnsDomain,
|
||||
/// The fully qualified DNS name that uniquely identifies the local
|
||||
/// computer. This name is a combination of the DNS host name and the DNS
|
||||
/// domain name, using the form HostName.DomainName. If the local computer
|
||||
/// is a node in a cluster, lpBuffer receives the fully qualified DNS name
|
||||
/// of the cluster virtual server.
|
||||
DnsFullyQualified,
|
||||
/// The DNS host name of the local computer. If the local computer is a
|
||||
/// node in a cluster, lpBuffer receives the DNS host name of the cluster
|
||||
/// virtual server.
|
||||
DnsHostname,
|
||||
/// The NetBIOS name of the local computer. If the local computer is a node
|
||||
/// in a cluster, lpBuffer receives the NetBIOS name of the cluster virtual
|
||||
/// server.
|
||||
NetBios,
|
||||
/// The name of the DNS domain assigned to the local computer. If the local
|
||||
/// computer is a node in a cluster, lpBuffer receives the DNS domain name
|
||||
/// of the local computer, not the name of the cluster virtual server.
|
||||
PhysicalDnsDomain,
|
||||
/// The fully qualified DNS name that uniquely identifies the computer. If
|
||||
/// the local computer is a node in a cluster, lpBuffer receives the fully
|
||||
/// qualified DNS name of the local computer, not the name of the cluster
|
||||
/// virtual server.
|
||||
///
|
||||
/// The fully qualified DNS name is a combination of the DNS host name and
|
||||
/// the DNS domain name, using the form HostName.DomainName.
|
||||
PhysicalDnsFullyQualified,
|
||||
/// The DNS host name of the local computer. If the local computer is a
|
||||
/// node in a cluster, lpBuffer receives the DNS host name of the local
|
||||
/// computer, not the name of the cluster virtual server.
|
||||
PhysicalDnsHostname,
|
||||
/// The NetBIOS name of the local computer. If the local computer is a node
|
||||
/// in a cluster, lpBuffer receives the NetBIOS name of the local computer,
|
||||
/// not the name of the cluster virtual server.
|
||||
PhysicalNetBios,
|
||||
}
|
||||
|
||||
impl ComputerNameKind {
|
||||
fn to_format(&self) -> COMPUTER_NAME_FORMAT {
|
||||
use self::ComputerNameKind::*;
|
||||
use windows_sys::Win32::System::SystemInformation;
|
||||
|
||||
match *self {
|
||||
DnsDomain => SystemInformation::ComputerNameDnsDomain,
|
||||
DnsFullyQualified => {
|
||||
SystemInformation::ComputerNameDnsFullyQualified
|
||||
}
|
||||
DnsHostname => SystemInformation::ComputerNameDnsHostname,
|
||||
NetBios => SystemInformation::ComputerNameNetBIOS,
|
||||
PhysicalDnsDomain => {
|
||||
SystemInformation::ComputerNamePhysicalDnsDomain
|
||||
}
|
||||
PhysicalDnsFullyQualified => {
|
||||
SystemInformation::ComputerNamePhysicalDnsFullyQualified
|
||||
}
|
||||
PhysicalDnsHostname => {
|
||||
SystemInformation::ComputerNamePhysicalDnsHostname
|
||||
}
|
||||
PhysicalNetBios => SystemInformation::ComputerNamePhysicalNetBIOS,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Retrieves a NetBIOS or DNS name associated with the local computer.
|
||||
///
|
||||
/// The names are established at system startup, when the system reads them
|
||||
/// from the registry.
|
||||
///
|
||||
/// This corresponds to calling [`GetComputerNameExW`].
|
||||
///
|
||||
/// [`GetComputerNameExW`]: https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw
|
||||
pub fn get_computer_name(kind: ComputerNameKind) -> io::Result<OsString> {
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
let format = kind.to_format();
|
||||
let mut len1 = 0;
|
||||
// SAFETY: As documented, we call this with a null pointer which will in
|
||||
// turn cause this routine to write the required buffer size fo `len1`.
|
||||
// Also, we explicitly ignore the return value since we expect this call to
|
||||
// fail given that the destination buffer is too small by design.
|
||||
let _ =
|
||||
unsafe { GetComputerNameExW(format, std::ptr::null_mut(), &mut len1) };
|
||||
|
||||
let len = match usize::try_from(len1) {
|
||||
Ok(len) => len,
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"GetComputerNameExW buffer length overflowed usize",
|
||||
))
|
||||
}
|
||||
};
|
||||
let mut buf = vec![0; len];
|
||||
let mut len2 = len1;
|
||||
// SAFETY: We pass a valid pointer to an appropriately sized Vec<u16>.
|
||||
let rc =
|
||||
unsafe { GetComputerNameExW(format, buf.as_mut_ptr(), &mut len2) };
|
||||
if rc == 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
// Apparently, the subsequent call writes the number of characters written
|
||||
// to the buffer to `len2` but not including the NUL terminator. Notice
|
||||
// that in the first call above, the length written to `len1` *does*
|
||||
// include the NUL terminator. Therefore, we expect `len1` to be at least
|
||||
// one greater than `len2`. If not, then something weird has happened and
|
||||
// we report an error.
|
||||
if len1 <= len2 {
|
||||
let msg = format!(
|
||||
"GetComputerNameExW buffer length mismatch, \
|
||||
expected length strictly less than {} \
|
||||
but got {}",
|
||||
len1, len2,
|
||||
);
|
||||
return Err(io::Error::new(io::ErrorKind::Other, msg));
|
||||
}
|
||||
let len = usize::try_from(len2).expect("len1 fits implies len2 fits");
|
||||
Ok(OsString::from_wide(&buf[..len]))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// This test doesn't really check anything other than that we can
|
||||
// successfully query all kinds of computer names. We just print them out
|
||||
// since there aren't really any properties about the names that we can
|
||||
// assert.
|
||||
//
|
||||
// We specifically run this test in CI with --nocapture so that we can see
|
||||
// the output.
|
||||
#[test]
|
||||
fn itworks() {
|
||||
let kinds = [
|
||||
ComputerNameKind::DnsDomain,
|
||||
ComputerNameKind::DnsFullyQualified,
|
||||
ComputerNameKind::DnsHostname,
|
||||
ComputerNameKind::NetBios,
|
||||
ComputerNameKind::PhysicalDnsDomain,
|
||||
ComputerNameKind::PhysicalDnsFullyQualified,
|
||||
ComputerNameKind::PhysicalDnsHostname,
|
||||
ComputerNameKind::PhysicalNetBios,
|
||||
];
|
||||
for kind in kinds {
|
||||
let result = get_computer_name(kind);
|
||||
let name = result.unwrap();
|
||||
println!("{kind:?}: {name:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+246
@@ -0,0 +1,246 @@
|
||||
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()) }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user