From 1418580fed3ac104ed0612f3a3eb652e8d3d1697 Mon Sep 17 00:00:00 2001 From: Koxiaet Date: Sun, 3 Jan 2021 13:40:22 +0000 Subject: [PATCH] Remove lazy_static dependency (#530) --- Cargo.toml | 1 - README.md | 3 +-- src/ansi_support.rs | 27 ++++++++++++++++----------- src/cursor/sys/windows.rs | 22 +++++++++++++--------- src/event.rs | 33 +++++++++++++++++++++++---------- src/event/stream.rs | 6 +++--- src/event/sys/windows.rs | 27 +++++++++++++-------------- src/style/sys/windows.rs | 31 ++++++++++++++----------------- src/terminal/sys/unix.rs | 18 ++++++++---------- 9 files changed, 91 insertions(+), 77 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c6a3ea..749dba2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ event-stream = ["futures-core"] # [dependencies] bitflags = "1.2" -lazy_static = "1.4" parking_lot = "0.11" # optional deps only added when requested diff --git a/README.md b/README.md index b4d8f77..81e3bda 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,7 @@ features = ["event-stream"] | Dependency | Used for | Included | | :----- | :----- | :----- | `bitflags` | `KeyModifiers`, those are differ based on input.| always -| `lazy_static` | original console color, original terminal mode, saved cursor position, supports ANSI on windows, single event reader per application.| always -| `parking_lot` | used for an RW LOCK. | always +| `parking_lot` | locking `RwLock`s with a timeout, const mutexes. | always | `libc` | UNIX terminal_size/raw modes/set_title and several other lowlevel functionality. | UNIX only | `Mio` | event readiness polling, waking up poller | UNIX only | `signal-hook`| signalhook is used to handle terminal resize SIGNAL with Mio. | UNIX only diff --git a/src/ansi_support.rs b/src/ansi_support.rs index fb07c0f..9409ced 100644 --- a/src/ansi_support.rs +++ b/src/ansi_support.rs @@ -1,7 +1,8 @@ -use crossterm_winapi::{ConsoleMode, Handle}; -use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; +use std::sync::atomic::{AtomicBool, Ordering}; -use lazy_static::lazy_static; +use crossterm_winapi::{ConsoleMode, Handle}; +use parking_lot::Once; +use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; use crate::Result; @@ -27,17 +28,21 @@ fn enable_vt_processing() -> Result<()> { Ok(()) } -lazy_static! { - static ref SUPPORTS_ANSI_ESCAPE_CODES: bool = { +static SUPPORTS_ANSI_ESCAPE_CODES: AtomicBool = AtomicBool::new(false); +static INITIALIZER: Once = Once::new(); + +/// Checks if the current terminal supports ansi escape sequences +pub fn supports_ansi() -> bool { + INITIALIZER.call_once(|| { // Some terminals on Windows like GitBash can't use WinAPI calls directly // so when we try to enable the ANSI-flag for Windows this won't work. // Because of that we should check first if the TERM-variable is set // and see if the current terminal is a terminal who does support ANSI. - std::env::var("TERM").map_or(false, |term| term != "dumb") || enable_vt_processing().is_ok() - }; -} + let supported = std::env::var("TERM").map_or(false, |term| term != "dumb") + || enable_vt_processing().is_ok(); -/// Checks if the current terminal supports ansi escape sequences -pub fn supports_ansi() -> bool { - *SUPPORTS_ANSI_ESCAPE_CODES + SUPPORTS_ANSI_ESCAPE_CODES.store(supported, Ordering::SeqCst); + }); + + SUPPORTS_ANSI_ESCAPE_CODES.load(Ordering::SeqCst) } diff --git a/src/cursor/sys/windows.rs b/src/cursor/sys/windows.rs index f41193a..1d95d71 100644 --- a/src/cursor/sys/windows.rs +++ b/src/cursor/sys/windows.rs @@ -1,6 +1,8 @@ //! WinAPI related logic to cursor manipulation. -use std::{io, sync::Mutex}; +use std::convert::TryFrom; +use std::io; +use std::sync::atomic::{AtomicU64, Ordering}; use crossterm_winapi::{is_true, Coord, Handle, HandleType, ScreenBuffer}; use winapi::{ @@ -8,13 +10,13 @@ use winapi::{ um::wincon::{SetConsoleCursorInfo, SetConsoleCursorPosition, CONSOLE_CURSOR_INFO, COORD}, }; -use lazy_static::lazy_static; - use crate::Result; -lazy_static! { - static ref SAVED_CURSOR_POS: Mutex> = Mutex::new(None); -} +/// The position of the cursor, written when you save the cursor's position. +/// +/// This is `u64::MAX` initially. Otherwise, it stores the cursor's x position bit-shifted left 16 +/// times or-ed with the cursor's y position, where both are `i16`s. +static SAVED_CURSOR_POS: AtomicU64 = AtomicU64::new(u64::MAX); // The 'y' position of the cursor is not relative to the window but absolute to screen buffer. // We can calculate the relative cursor position by subtracting the top position of the terminal window from the y position. @@ -176,7 +178,9 @@ impl ScreenBufferCursor { } fn restore_position(&self) -> Result<()> { - if let Some((x, y)) = *SAVED_CURSOR_POS.lock().unwrap() { + if let Ok(val) = u32::try_from(SAVED_CURSOR_POS.load(Ordering::Relaxed)) { + let x = (val >> 16) as i16; + let y = val as i16; self.move_to(x, y)?; } @@ -186,8 +190,8 @@ impl ScreenBufferCursor { fn save_position(&self) -> Result<()> { let position = self.position()?; - let mut locked_pos = SAVED_CURSOR_POS.lock().unwrap(); - *locked_pos = Some((position.x, position.y)); + let bits = u64::from(u32::from(position.x as u16) << 16 | u32::from(position.y as u16)); + SAVED_CURSOR_POS.store(bits, Ordering::Relaxed); Ok(()) } diff --git a/src/event.rs b/src/event.rs index 2cce657..89d05cf 100644 --- a/src/event.rs +++ b/src/event.rs @@ -75,16 +75,17 @@ use std::fmt; use std::time::Duration; -use parking_lot::RwLock; +use bitflags::bitflags; +use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate::{Command, Result}; use bitflags::bitflags; use lazy_static::lazy_static; -use crate::{csi, Command, Result}; - use filter::{EventFilter, Filter}; +use read::InternalEventReader; #[cfg(feature = "event-stream")] pub use stream::EventStream; use timeout::PollTimeout; @@ -97,10 +98,22 @@ mod stream; pub(crate) mod sys; mod timeout; -lazy_static! { - /// Static instance of `InternalEventReader`. - /// This needs to be static because there can be one event reader. - static ref INTERNAL_EVENT_READER: RwLock = RwLock::new(read::InternalEventReader::default()); +/// Static instance of `InternalEventReader`. +/// This needs to be static because there can be one event reader. +static INTERNAL_EVENT_READER: Mutex> = parking_lot::const_mutex(None); + +fn lock_internal_event_reader() -> MappedMutexGuard<'static, InternalEventReader> { + MutexGuard::map(INTERNAL_EVENT_READER.lock(), |reader| { + reader.get_or_insert_with(InternalEventReader::default) + }) +} +fn try_lock_internal_event_reader_for( + duration: Duration, +) -> Option> { + Some(MutexGuard::map( + INTERNAL_EVENT_READER.try_lock_for(duration)?, + |reader| reader.get_or_insert_with(InternalEventReader::default), + )) } /// Checks if there is an [`Event`](enum.Event.html) available. @@ -201,13 +214,13 @@ where { let (mut reader, timeout) = if let Some(timeout) = timeout { let poll_timeout = PollTimeout::new(Some(timeout)); - if let Some(reader) = INTERNAL_EVENT_READER.try_write_for(timeout) { + if let Some(reader) = try_lock_internal_event_reader_for(timeout) { (reader, poll_timeout.leftover()) } else { return Ok(false); } } else { - (INTERNAL_EVENT_READER.write(), None) + (lock_internal_event_reader(), None) }; reader.poll(timeout, filter) } @@ -217,7 +230,7 @@ pub(crate) fn read_internal(filter: &F) -> Result where F: Filter, { - let mut reader = INTERNAL_EVENT_READER.write(); + let mut reader = lock_internal_event_reader(); reader.read(filter) } diff --git a/src/event/stream.rs b/src/event/stream.rs index a7f61b5..fd677fd 100644 --- a/src/event/stream.rs +++ b/src/event/stream.rs @@ -15,8 +15,8 @@ use futures_core::stream::Stream; use crate::Result; use super::{ - filter::EventFilter, poll_internal, read_internal, sys::Waker, Event, InternalEvent, - INTERNAL_EVENT_READER, + filter::EventFilter, lock_internal_event_reader, poll_internal, read_internal, sys::Waker, + Event, InternalEvent, }; /// A stream of `Result`. @@ -60,7 +60,7 @@ impl Default for EventStream { }); EventStream { - poll_internal_waker: INTERNAL_EVENT_READER.write().waker(), + poll_internal_waker: lock_internal_event_reader().waker(), stream_wake_task_executed: Arc::new(AtomicBool::new(false)), stream_wake_task_should_shutdown: Arc::new(AtomicBool::new(false)), task_sender, diff --git a/src/event/sys/windows.rs b/src/event/sys/windows.rs index d872a6c..7485d55 100644 --- a/src/event/sys/windows.rs +++ b/src/event/sys/windows.rs @@ -1,9 +1,9 @@ //! This is a WINDOWS specific implementation for input related action. -use std::sync::Mutex; +use std::convert::TryFrom; +use std::sync::atomic::{AtomicU64, Ordering}; use crossterm_winapi::{ConsoleMode, Handle}; -use lazy_static::lazy_static; use crate::Result; @@ -15,25 +15,24 @@ pub(crate) mod poll; const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008; -lazy_static! { - static ref ORIGINAL_CONSOLE_MODE: Mutex> = Mutex::new(None); -} +/// This is a either `u64::MAX` if it's uninitialized or a valid `u32` that stores the original +/// console mode if it's initialized. +static ORIGINAL_CONSOLE_MODE: AtomicU64 = AtomicU64::new(u64::MAX); /// Initializes the default console color. It will will be skipped if it has already been initialized. fn init_original_console_mode(original_mode: u32) { - let mut lock = ORIGINAL_CONSOLE_MODE.lock().unwrap(); - - if lock.is_none() { - *lock = Some(original_mode); - } + let _ = ORIGINAL_CONSOLE_MODE.compare_exchange( + u64::MAX, + u64::from(original_mode), + Ordering::Relaxed, + Ordering::Relaxed, + ); } /// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. fn original_console_mode() -> u32 { - // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()` - ORIGINAL_CONSOLE_MODE - .lock() - .unwrap() + u32::try_from(ORIGINAL_CONSOLE_MODE.load(Ordering::Relaxed)) + // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()` .expect("Original console mode not set") } diff --git a/src/style/sys/windows.rs b/src/style/sys/windows.rs index 80c9ede..b2bb7f6 100644 --- a/src/style/sys/windows.rs +++ b/src/style/sys/windows.rs @@ -1,10 +1,9 @@ -use std::sync::Mutex; +use std::convert::TryFrom; +use std::sync::atomic::{AtomicU32, Ordering}; use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; use winapi::um::wincon; -use lazy_static::lazy_static; - use crate::Result; use super::super::{Color, Colored}; @@ -70,7 +69,7 @@ pub(crate) fn set_background_color(bg_color: Color) -> Result<()> { } pub(crate) fn reset() -> Result<()> { - if let Some(original_color) = *ORIGINAL_CONSOLE_COLOR.lock().unwrap() { + if let Ok(original_color) = u16::try_from(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed)) { Console::from(Handle::new(HandleType::CurrentOutputHandle)?) .set_text_attribute(original_color)?; } @@ -80,12 +79,10 @@ pub(crate) fn reset() -> Result<()> { /// Initializes the default console color. It will will be skipped if it has already been initialized. pub(crate) fn init_console_color() -> Result<()> { - let mut locked_pos = ORIGINAL_CONSOLE_COLOR.lock().unwrap(); - - if locked_pos.is_none() { + if ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed) == u32::MAX { let screen_buffer = ScreenBuffer::current()?; let attr = screen_buffer.info()?.attributes(); - *locked_pos = Some(attr); + ORIGINAL_CONSOLE_COLOR.store(u32::from(attr), Ordering::Relaxed); } Ok(()) @@ -93,16 +90,14 @@ pub(crate) fn init_console_color() -> Result<()> { /// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic. pub(crate) fn original_console_color() -> u16 { - // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()` - ORIGINAL_CONSOLE_COLOR - .lock() - .unwrap() + u16::try_from(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed)) + // safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()` .expect("Initial console color not set") } -lazy_static! { - static ref ORIGINAL_CONSOLE_COLOR: Mutex> = Mutex::new(None); -} +// This is either a valid u16 in which case it stores the original console color or it is u32::MAX +// in which case it is uninitialized. +static ORIGINAL_CONSOLE_COLOR: AtomicU32 = AtomicU32::new(u32::MAX); impl From for u16 { /// Returns the WinAPI color value (u16) from the `Colored` struct. @@ -180,6 +175,8 @@ impl From for u16 { #[cfg(test)] mod tests { + use std::sync::atomic::Ordering; + use crate::style::sys::windows::set_foreground_color; use super::{ @@ -200,11 +197,11 @@ mod tests { #[test] fn test_original_console_color_is_set() { - assert!(ORIGINAL_CONSOLE_COLOR.lock().unwrap().is_none()); + assert_eq!(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed), u32::MAX); // will call `init_console_color` set_foreground_color(Color::Blue).unwrap(); - assert!(ORIGINAL_CONSOLE_COLOR.lock().unwrap().is_some()); + assert_ne!(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed), u32::MAX); } } diff --git a/src/terminal/sys/unix.rs b/src/terminal/sys/unix.rs index 18c626d..d5e5827 100644 --- a/src/terminal/sys/unix.rs +++ b/src/terminal/sys/unix.rs @@ -2,25 +2,23 @@ use std::fs::File; use std::os::unix::io::{IntoRawFd, RawFd}; -use std::{io, mem, process, sync::Mutex}; +use std::{io, mem, process}; -use lazy_static::lazy_static; use libc::{ cfmakeraw, ioctl, tcgetattr, tcsetattr, termios as Termios, winsize, STDOUT_FILENO, TCSANOW, TIOCGWINSZ, }; +use parking_lot::Mutex; use crate::error::{ErrorKind, Result}; use crate::event::sys::unix::file_descriptor::{tty_fd, FileDesc}; -lazy_static! { - // Some(Termios) -> we're in the raw mode and this is the previous mode - // None -> we're not in the raw mode - static ref TERMINAL_MODE_PRIOR_RAW_MODE: Mutex> = Mutex::new(None); -} +// Some(Termios) -> we're in the raw mode and this is the previous mode +// None -> we're not in the raw mode +static TERMINAL_MODE_PRIOR_RAW_MODE: Mutex> = parking_lot::const_mutex(None); pub(crate) fn is_raw_mode_enabled() -> bool { - TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap().is_some() + TERMINAL_MODE_PRIOR_RAW_MODE.lock().is_some() } #[allow(clippy::useless_conversion)] @@ -49,7 +47,7 @@ pub(crate) fn size() -> Result<(u16, u16)> { } pub(crate) fn enable_raw_mode() -> Result<()> { - let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap(); + let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock(); if original_mode.is_some() { return Ok(()); @@ -70,7 +68,7 @@ pub(crate) fn enable_raw_mode() -> Result<()> { } pub(crate) fn disable_raw_mode() -> Result<()> { - let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock().unwrap(); + let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock(); if let Some(original_mode_ios) = original_mode.as_ref() { let tty = tty_fd()?;