Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use windows crate instead of winapi #29

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Check

on:
push:
branches:
- master
pull_request:

env:
CARGO_TERM_COLOR: always
MSRV: "1.62.0"

# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel
# and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
fmt:
name: fmt (stable)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu
- name: Run cargo fmt
run: cargo fmt -- --check
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
clippy:
name: clippy (${{ matrix.toolchain }})
runs-on: windows-latest
permissions:
checks: write
strategy:
fail-fast: false
matrix:
# Get early warnings about new lints introduced in the beta channel
toolchain: [stable, beta]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
components: clippy
targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu
- name: Run clippy action
uses: clechasseur/rs-clippy-check@v3
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
doc:
# run docs generation on nightly rather than stable. This enables features like
# https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an
# API be documented as only available in some specific platforms.
name: doc (nightly)
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
with:
targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu
- name: Run cargo doc
run: cargo doc --no-deps --all-features
env:
RUSTDOCFLAGS: --cfg docsrs
msrv:
# check that we can build using the minimal rust version that is specified by this crate
name: check msrv
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust ${{ env.MSRV }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.MSRV }}
targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu
- name: cargo +${{ env.MSRV }} check
run: cargo check
93 changes: 93 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Test
# This is the main CI workflow that runs the test suite on all pushes to main and all pull requests.
# It runs the following jobs:
# - test: runs the test suite on ubuntu with stable and beta rust toolchains
# - minimal: runs the test suite with the minimal versions of the dependencies that satisfy the
# requirements of this crate, and its dependencies
# See check.yml for information about how the concurrency cancellation and workflow triggering works
# and for the fmt, clippy, doc, and msrv jobs.
on:
push:
branches:
- master
pull_request:

# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel
# and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
test:
runs-on: windows-latest
name: Test (${{ matrix.toolchain }})
strategy:
matrix:
# run on stable and beta to ensure that tests won't break on the next version of the rust
# toolchain
toolchain: [1.62.0, stable, beta]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust ${{ matrix.toolchain }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu
# Generate a lockfile to use if one is not checked in. This makes the next step able to
# run regardless of whether a lockfile is checked in or not.
- name: cargo generate-lockfile
if: hashFiles('Cargo.lock') == ''
run: cargo generate-lockfile
- name: cargo test --locked
run: cargo test --locked --all-features --all-targets
- name: cargo test --doc
run: cargo test --locked --all-features --doc
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2

# This action chooses the oldest version of the dependencies permitted by Cargo.toml to ensure
# that this crate is compatible with the minimal version that this crate and its dependencies
# require. This will pickup issues where this create relies on functionality that was introduced
# later than the actual version specified (e.g., when we choose just a major version, but a
# method was added after this version).
#
# This particular check can be difficult to get to succeed as often transitive dependencies may
# be incorrectly specified (e.g., a dependency specifies 1.0 but really requires 1.1.5). There
# is an alternative flag available -Zdirect-minimal-versions that uses the minimal versions for
# direct dependencies of this crate, while selecting the maximal versions for the transitive
# dependencies. Alternatively, you can add a line in your Cargo.toml to artificially increase
# the minimal dependency, which you do with e.g.:
# ```toml
# # for minimal-versions
# [target.'cfg(any())'.dependencies]
# openssl = { version = "0.10.55", optional = true } # needed to allow foo to build with -Zminimal-versions
# ```
# The optional = true is necessary in case that dependency isn't otherwise transitively required
# by your library, and the target bit is so that this dependency edge never actually affects
# Cargo build order. See also
# https://github.com/jonhoo/fantoccini/blob/fde336472b712bc7ebf5b4e772023a7ba71b2262/Cargo.toml#L47-L49.
# This action is run on ubuntu with the stable toolchain, as it is not expected to fail
minimal-versions:
runs-on: windows-latest
name: Check minimal-versions
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu
- name: Install Rust nightly (for -Zdirect-minimal-versions)
uses: dtolnay/rust-toolchain@nightly
with:
targets: x86_64-pc-windows-msvc,x86_64-pc-windows-gnu
- name: rustup default stable
run: rustup default stable
- name: cargo update -Zdirect-minimal-versions
run: cargo +nightly update -Zdirect-minimal-versions
- name: cargo test
run: cargo test --locked --all-features --all-targets
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
14 changes: 11 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ license = "MIT"
keywords = ["winapi", "abstractions", "crossterm", "windows", "screen_buffer"]
exclude = ["target", "Cargo.lock"]
readme = "README.md"
edition = "2018"
edition = "2021"
rust-version = "1.62.0"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.8", features = ["winbase", "consoleapi", "processenv", "handleapi", "synchapi", "impl-default"] }

# Note that 0.56.0 is the last version that supports Rust 1.62.0, the next version requires Rust
# 1.70.0. This would prevent crossterm from being able to compile this on the stable version of
# Debian (which ships with Rust 1.63.0).
windows = { version = "0.56.0", features = [
"Win32_Security",
"Win32_Storage_FileSystem",
"Win32_System_Console",
"Win32_System_Threading",
] }
[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"
4 changes: 2 additions & 2 deletions examples/coloring_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ fn set_foreground_color() -> Result<()> {

// background intensity is a separate value in attrs,
// wee need to check if this was applied to the current bg color.
if (attrs & 0x0080 as u16) != 0 {
color = color | 0x0080 as u16;
if (attrs & 0x0080_u16) != 0 {
color |= 0x0080_u16;
}

// set the console text attribute to the new color value.
Expand Down
13 changes: 9 additions & 4 deletions src/cfi.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::fmt;
use std::mem::zeroed;

use winapi::um::wincontypes::CONSOLE_FONT_INFO;
use windows::Win32::System::Console::CONSOLE_FONT_INFO;

use crate::Size;

Expand All @@ -23,9 +22,9 @@ impl fmt::Debug for FontInfo {
}

impl FontInfo {
/// Create a new font info without all zeroed properties.
/// Create a new font info with default (zeroed) properties.
pub fn new() -> FontInfo {
FontInfo(unsafe { zeroed() })
FontInfo(CONSOLE_FONT_INFO::default())
}

/// Get the size of the font.
Expand All @@ -40,3 +39,9 @@ impl FontInfo {
self.0.nFont
}
}

impl Default for FontInfo {
fn default() -> Self {
Self::new()
}
}
74 changes: 23 additions & 51 deletions src/console.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
use std::io::{self, Result};
use std::iter;
use std::slice;
use std::str;

use winapi::ctypes::c_void;
use winapi::shared::minwindef::DWORD;
use winapi::shared::ntdef::NULL;
use winapi::um::consoleapi::{GetNumberOfConsoleInputEvents, ReadConsoleInputW, WriteConsoleW};
use winapi::um::wincon::{
use std::{
io::{self, Result},
iter, slice, str,
};

use windows::Win32::System::Console::{
FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetLargestConsoleWindowSize,
SetConsoleTextAttribute, SetConsoleWindowInfo, COORD, INPUT_RECORD, SMALL_RECT,
GetNumberOfConsoleInputEvents, ReadConsoleInputW, SetConsoleTextAttribute,
SetConsoleWindowInfo, WriteConsoleW, CONSOLE_CHARACTER_ATTRIBUTES, COORD, INPUT_RECORD,
SMALL_RECT,
};

use super::{result, Coord, Handle, HandleType, InputRecord, WindowPositions};
use super::{Coord, Handle, HandleType, InputRecord, WindowPositions};

/// A wrapper around a screen buffer.
#[derive(Debug, Clone)]
Expand All @@ -39,7 +37,7 @@ impl Console {
/// This wraps
/// [`SetConsoleTextAttribute`](https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute).
pub fn set_text_attribute(&self, value: u16) -> Result<()> {
result(unsafe { SetConsoleTextAttribute(*self.handle, value) })?;
unsafe { SetConsoleTextAttribute(*self.handle, CONSOLE_CHARACTER_ATTRIBUTES(value)) }?;
Ok(())
}

Expand All @@ -48,14 +46,8 @@ impl Console {
/// This wraps
/// [`SetConsoleWindowInfo`](https://docs.microsoft.com/en-us/windows/console/setconsolewindowinfo).
pub fn set_console_info(&self, absolute: bool, rect: WindowPositions) -> Result<()> {
let absolute = match absolute {
true => 1,
false => 0,
};
let a = SMALL_RECT::from(rect);

result(unsafe { SetConsoleWindowInfo(*self.handle, absolute, &a) })?;

unsafe { SetConsoleWindowInfo(*self.handle, absolute, &a) }?;
Ok(())
}

Expand All @@ -64,14 +56,14 @@ impl Console {
///
/// This wraps
/// [`FillConsoleOutputCharacterA`](https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter).
pub fn fill_whit_character(
pub fn fill_with_character(
&self,
start_location: Coord,
cells_to_write: u32,
filling_char: char,
) -> Result<u32> {
let mut chars_written = 0;
result(unsafe {
unsafe {
// fill the cells in console with blanks
FillConsoleOutputCharacterA(
*self.handle,
Expand All @@ -80,7 +72,7 @@ impl Console {
COORD::from(start_location),
&mut chars_written,
)
})?;
}?;

Ok(chars_written)
}
Expand All @@ -98,15 +90,15 @@ impl Console {
) -> Result<u32> {
let mut cells_written = 0;
// Get the position of the current console window
result(unsafe {
unsafe {
FillConsoleOutputAttribute(
*self.handle,
dw_attribute,
cells_to_write,
COORD::from(start_location),
&mut cells_written,
)
})?;
}?;

Ok(cells_written)
}
Expand Down Expand Up @@ -134,20 +126,9 @@ impl Console {
}
};

let utf16: Vec<u16> = utf8.encode_utf16().collect();
let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void;
let cells_written: Option<*mut u32> = None;

let mut cells_written: u32 = 0;

result(unsafe {
WriteConsoleW(
*self.handle,
utf16_ptr,
utf16.len() as u32,
&mut cells_written,
NULL,
)
})?;
unsafe { WriteConsoleW(*self.handle, buf, cells_written, None) }?;

Ok(utf8.as_bytes().len())
}
Expand Down Expand Up @@ -202,8 +183,8 @@ impl Console {
/// This wraps
/// [`GetNumberOfConsoleInputEvents`](https://docs.microsoft.com/en-us/windows/console/getnumberofconsoleinputevents).
pub fn number_of_console_input_events(&self) -> Result<u32> {
let mut buf_len: DWORD = 0;
result(unsafe { GetNumberOfConsoleInputEvents(*self.handle, &mut buf_len) })?;
let mut buf_len = 0;
unsafe { GetNumberOfConsoleInputEvents(*self.handle, &mut buf_len) }?;
Ok(buf_len)
}

Expand All @@ -213,17 +194,8 @@ impl Console {
/// a u32.
fn read_input(&self, buf: &mut [INPUT_RECORD]) -> Result<usize> {
let mut num_records = 0;
debug_assert!(buf.len() < std::u32::MAX as usize);

result(unsafe {
ReadConsoleInputW(
*self.handle,
buf.as_mut_ptr(),
buf.len() as u32,
&mut num_records,
)
})?;

debug_assert!(buf.len() < u32::MAX as usize);
unsafe { ReadConsoleInputW(*self.handle, buf, &mut num_records) }?;
Ok(num_records as usize)
}
}
Expand Down
Loading