From 8fb90598532b4e143cd2b5bf7c9edab48a18b985 Mon Sep 17 00:00:00 2001 From: Robert Vojta Date: Tue, 10 Dec 2019 20:41:41 +0100 Subject: [PATCH] Implemented `poll` Waker. --- Cargo.toml | 2 +- examples/event-poll-read.rs | 2 +- src/event/read.rs | 32 ++++++++--- src/event/source.rs | 22 ++++---- src/event/source/unix.rs | 66 ++++++++-------------- src/event/source/windows.rs | 7 ++- src/event/stream.rs | 63 +++++++++++++++------ src/event/sys.rs | 9 ++- src/event/sys/unix.rs | 41 ++------------ src/event/sys/unix/waker.rs | 100 +++++++++++++++++++++++++++++++++ src/event/sys/windows.rs | 59 +++++++++++-------- src/event/sys/windows/waker.rs | 42 ++++++++++++++ src/utils/functions.rs | 3 +- 13 files changed, 303 insertions(+), 145 deletions(-) create mode 100644 src/event/sys/unix/waker.rs create mode 100644 src/event/sys/windows/waker.rs diff --git a/Cargo.toml b/Cargo.toml index f3dc930..25d8599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ version = "0.3.8" features = ["winuser"] [target.'cfg(windows)'.dependencies] -crossterm_winapi = "0.5.0" +crossterm_winapi = "0.5.1" # # UNIX dependencies diff --git a/examples/event-poll-read.rs b/examples/event-poll-read.rs index ead03a8..4867644 100644 --- a/examples/event-poll-read.rs +++ b/examples/event-poll-read.rs @@ -49,7 +49,7 @@ fn print_events() -> Result<()> { fn main() -> Result<()> { println!("{}", HELP); - enable_raw_mode(); + enable_raw_mode()?; let mut stdout = stdout(); execute!(stdout, EnableMouseCapture)?; diff --git a/src/event/read.rs b/src/event/read.rs index e57a70c..ec2d895 100644 --- a/src/event/read.rs +++ b/src/event/read.rs @@ -1,9 +1,13 @@ -use std::{collections::vec_deque::VecDeque, time::Duration}; +use std::{collections::vec_deque::VecDeque, io, time::Duration}; + +use crate::ErrorKind; #[cfg(unix)] use super::source::unix::UnixInternalEventSource; #[cfg(windows)] use super::source::windows::WindowsEventSource; +#[cfg(feature = "event-stream")] +use super::sys::Waker; use super::{filter::Filter, source::EventSource, timeout::PollTimeout, InternalEvent, Result}; /// Can be used to read `InternalEvent`s. @@ -31,11 +35,10 @@ impl Default for InternalEventReader { } impl InternalEventReader { + /// Returns a `Waker` allowing to wake/force the `poll` method to return `Ok(false)`. #[cfg(feature = "event-stream")] - pub(crate) fn wake(&self) { - if let Some(source) = self.source.as_ref() { - source.wake(); - } + pub(crate) fn waker(&self) -> Waker { + self.source.as_ref().expect("reader source not set").waker() } pub(crate) fn poll(&mut self, timeout: Option, filter: &F) -> Result @@ -62,9 +65,9 @@ impl InternalEventReader { let poll_timeout = PollTimeout::new(timeout); loop { - let maybe_event = match event_source.try_read(timeout)? { - None => None, - Some(event) => { + let maybe_event = match event_source.try_read(timeout) { + Ok(None) => None, + Ok(Some(event)) => { if filter.eval(&event) { Some(event) } else { @@ -72,6 +75,14 @@ impl InternalEventReader { None } } + Err(ErrorKind::IoError(e)) => { + if e.kind() == io::ErrorKind::Interrupted { + return Ok(false); + } + + return Err(ErrorKind::IoError(e)); + } + Err(e) => return Err(e), }; if poll_timeout.elapsed() || maybe_event.is_some() { @@ -442,6 +453,9 @@ mod tests { Ok(None) } - fn wake(&self) {} + #[cfg(feature = "event-stream")] + fn waker(&self) -> super::super::sys::Waker { + unimplemented!(); + } } } diff --git a/src/event/source.rs b/src/event/source.rs index 61e1379..a9c2f65 100644 --- a/src/event/source.rs +++ b/src/event/source.rs @@ -1,25 +1,27 @@ use std::time::Duration; +#[cfg(feature = "event-stream")] +use super::sys::Waker; use super::InternalEvent; #[cfg(unix)] -pub mod unix; +pub(crate) mod unix; #[cfg(windows)] -pub mod windows; +pub(crate) mod windows; /// An interface for trying to read an `InternalEvent` within an optional `Duration`. pub(crate) trait EventSource: Sync + Send { /// Tries to read an `InternalEvent` within the given duration. /// - /// This function takes in an optional duration. - /// * `None`: blocks indefinitely until an event is able to be read. - /// * `Some(duration)`: blocks for the given duration. + /// # Arguments /// - /// Returns: - /// `Ok(Some(event))`: in case an event is ready. - /// `Ok(None)`: in case an event is not ready. + /// * `timeout` - `None` block indefinitely until an event is available, `Some(duration)` blocks + /// for the given timeout + /// + /// Returns `Ok(None)` if there's no event available and timeout expires. fn try_read(&mut self, timeout: Option) -> crate::Result>; - /// Forces the `try_read` method to return `Ok(None)` immediately. - fn wake(&self); + /// Returns a `Waker` allowing to wake/force the `try_read` method to return `Ok(None)`. + #[cfg(feature = "event-stream")] + fn waker(&self) -> Waker; } diff --git a/src/event/source/unix.rs b/src/event/source/unix.rs index ab6ba31..c1a97d2 100644 --- a/src/event/source/unix.rs +++ b/src/event/source/unix.rs @@ -1,11 +1,11 @@ -use std::collections::VecDeque; -use std::{io, time::Duration}; - use mio::{unix::EventedFd, Events, Poll, PollOpt, Ready, Token}; use signal_hook::iterator::Signals; +use std::{collections::VecDeque, time::Duration}; use crate::Result; +#[cfg(feature = "event-stream")] +use super::super::sys::Waker; use super::super::{ source::EventSource, sys::unix::{parse_event, tty_fd, FileDesc}, @@ -16,6 +16,7 @@ use super::super::{ // Tokens to identify file descriptor const TTY_TOKEN: Token = Token(0); const SIGNAL_TOKEN: Token = Token(1); +#[cfg(feature = "event-stream")] const WAKE_TOKEN: Token = Token(2); // I (@zrzka) wasn't able to read more than 1_022 bytes when testing @@ -23,22 +24,6 @@ const WAKE_TOKEN: Token = Token(2); // is enough. const TTY_BUFFER_SIZE: usize = 1_204; -/// Creates a new pipe and returns `(read, write)` file descriptors. -fn pipe() -> Result<(FileDesc, FileDesc)> { - let (read_fd, write_fd) = unsafe { - let mut pipe_fds: [libc::c_int; 2] = [0; 2]; - if libc::pipe(pipe_fds.as_mut_ptr()) == -1 { - return Err(io::Error::last_os_error().into()); - } - (pipe_fds[0], pipe_fds[1]) - }; - - let read_fd = FileDesc::new(read_fd, true); - let write_fd = FileDesc::new(write_fd, true); - - Ok((read_fd, write_fd)) -} - pub(crate) struct UnixInternalEventSource { poll: Poll, events: Events, @@ -46,8 +31,8 @@ pub(crate) struct UnixInternalEventSource { tty_buffer: [u8; TTY_BUFFER_SIZE], tty_fd: FileDesc, signals: Signals, - wake_read_fd: FileDesc, - wake_write_fd: FileDesc, + #[cfg(feature = "event-stream")] + waker: Waker, } impl UnixInternalEventSource { @@ -81,15 +66,10 @@ impl UnixInternalEventSource { let signals = Signals::new(&[signal_hook::SIGWINCH])?; poll.register(&signals, SIGNAL_TOKEN, Ready::readable(), PollOpt::level())?; - let (wake_read_fd, wake_write_fd) = pipe()?; - let wake_read_raw_fd = wake_read_fd.raw_fd(); - let wake_read_ev = EventedFd(&wake_read_raw_fd); - poll.register( - &wake_read_ev, - WAKE_TOKEN, - Ready::readable(), - PollOpt::level(), - )?; + #[cfg(feature = "event-stream")] + let waker = Waker::new()?; + #[cfg(feature = "event-stream")] + poll.register(&waker, WAKE_TOKEN, Ready::readable(), PollOpt::level())?; Ok(UnixInternalEventSource { poll, @@ -98,8 +78,8 @@ impl UnixInternalEventSource { tty_buffer: [0u8; TTY_BUFFER_SIZE], tty_fd: input_fd, signals, - wake_read_fd, - wake_write_fd, + #[cfg(feature = "event-stream")] + waker, }) } } @@ -164,13 +144,14 @@ impl EventSource for UnixInternalEventSource { }; } } + #[cfg(feature = "event-stream")] WAKE_TOKEN => { - // Something happened on the self pipe. Try to read single byte - // (see wake() fn) and ignore result. If we can't read the byte, - // mio Poll::poll will fire another event with WAKE_TOKEN. - let mut buf = [0u8; 1]; - let _ = self.wake_read_fd.read(&mut buf, 1); - return Ok(None); + let _ = self.waker.reset(); + return Err(std::io::Error::new( + std::io::ErrorKind::Interrupted, + "Poll operation was woken up by `Waker::wake`", + ) + .into()); } _ => unreachable!("Synchronize Evented handle registration & token handling"), } @@ -183,12 +164,9 @@ impl EventSource for UnixInternalEventSource { } } - fn wake(&self) { - // DO NOT write more than 1 byte. See try_read & WAKE_TOKEN - // handling - it reads just 1 byte. If you write more than - // 1 byte, lets say N, then the try_read will be woken up - // N times. - let _ = self.wake_write_fd.write(&[0x57]); + #[cfg(feature = "event-stream")] + fn waker(&self) -> Waker { + self.waker.clone() } } diff --git a/src/event/source/windows.rs b/src/event/source/windows.rs index aa6ac1d..0218b72 100644 --- a/src/event/source/windows.rs +++ b/src/event/source/windows.rs @@ -4,6 +4,8 @@ use crossterm_winapi::{Console, Handle, InputEventType, KeyEventRecord, MouseEve use crate::event::{sys::windows::WinApiPoll, Event}; +#[cfg(feature = "event-stream")] +use super::super::sys::Waker; use super::super::{ source::EventSource, sys::windows::{handle_key_event, handle_mouse_event}, @@ -64,7 +66,8 @@ impl EventSource for WindowsEventSource { } } - fn wake(&self) { - let _ = self.poll.cancel(); + #[cfg(feature = "event-stream")] + fn waker(&self) -> Waker { + self.poll.waker() } } diff --git a/src/event/stream.rs b/src/event/stream.rs index 4d1db58..f7885cc 100644 --- a/src/event/stream.rs +++ b/src/event/stream.rs @@ -16,7 +16,8 @@ use futures::{ use crate::Result; use super::{ - filter::EventFilter, poll_internal, read_internal, Event, InternalEvent, INTERNAL_EVENT_READER, + filter::EventFilter, poll_internal, read_internal, sys::Waker, Event, InternalEvent, + INTERNAL_EVENT_READER, }; /// A stream of `Result`. @@ -30,22 +31,47 @@ use super::{ /// /// Check the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder to see how to use /// it (`event-stream-*`). -#[derive(Default)] pub struct EventStream { - wake_thread_spawned: Arc, - wake_thread_should_shutdown: Arc, + poll_internal_waker: Waker, + stream_wake_thread_spawned: Arc, + stream_wake_thread_should_shutdown: Arc, +} + +impl Default for EventStream { + fn default() -> Self { + EventStream { + poll_internal_waker: INTERNAL_EVENT_READER.write().waker(), + stream_wake_thread_spawned: Arc::new(AtomicBool::new(false)), + stream_wake_thread_should_shutdown: Arc::new(AtomicBool::new(false)), + } + } } impl EventStream { /// Constructs a new instance of `EventStream`. pub fn new() -> EventStream { - EventStream { - wake_thread_spawned: Arc::new(AtomicBool::new(false)), - wake_thread_should_shutdown: Arc::new(AtomicBool::new(false)), - } + EventStream::default() } } +// Note to future me +// +// We need two wakers in order to implement EventStream correctly. +// +// 1. futures::Stream waker +// +// Stream::poll_next can return Poll::Pending which means that there's no +// event available. We are going to spawn a thread with the +// poll_internal(None, &EventFilter) call. This call blocks until an +// event is available and then we have to wake up the executor with notification +// that the task can be resumed. +// +// 2. poll_internal waker +// +// There's no event available, Poll::Pending was returned, stream waker thread +// is up and sitting in the poll_internal. User wants to drop the EventStream. +// We have to wake up the poll_internal (force it to return Ok(false)) and quit +// the thread before we drop. impl Stream for EventStream { type Item = Result; @@ -59,14 +85,15 @@ impl Stream for EventStream { }, Ok(false) => { if !self - .wake_thread_spawned + .stream_wake_thread_spawned .compare_and_swap(false, true, Ordering::SeqCst) { - let waker = cx.waker().clone(); - let wake_thread_spawned = self.wake_thread_spawned.clone(); - let wake_thread_should_shutdown = self.wake_thread_should_shutdown.clone(); + let stream_waker = cx.waker().clone(); + let stream_wake_thread_spawned = self.stream_wake_thread_spawned.clone(); + let stream_wake_thread_should_shutdown = + self.stream_wake_thread_should_shutdown.clone(); - wake_thread_should_shutdown.store(false, Ordering::SeqCst); + stream_wake_thread_should_shutdown.store(false, Ordering::SeqCst); thread::spawn(move || { loop { @@ -74,12 +101,12 @@ impl Stream for EventStream { break; } - if wake_thread_should_shutdown.load(Ordering::SeqCst) { + if stream_wake_thread_should_shutdown.load(Ordering::SeqCst) { break; } } - wake_thread_spawned.store(false, Ordering::SeqCst); - waker.wake(); + stream_wake_thread_spawned.store(false, Ordering::SeqCst); + stream_waker.wake(); }); } Poll::Pending @@ -92,8 +119,8 @@ impl Stream for EventStream { impl Drop for EventStream { fn drop(&mut self) { - self.wake_thread_should_shutdown + self.stream_wake_thread_should_shutdown .store(true, Ordering::SeqCst); - INTERNAL_EVENT_READER.read().wake(); + let _ = self.poll_internal_waker.wake(); } } diff --git a/src/event/sys.rs b/src/event/sys.rs index 1746a5a..85ffa92 100644 --- a/src/event/sys.rs +++ b/src/event/sys.rs @@ -1,4 +1,9 @@ +#[cfg(all(unix, feature = "event-stream"))] +pub(crate) use unix::Waker; +#[cfg(all(windows, feature = "event-stream"))] +pub(crate) use windows::Waker; + #[cfg(unix)] -pub mod unix; +pub(crate) mod unix; #[cfg(windows)] -pub mod windows; +pub(crate) mod windows; diff --git a/src/event/sys/unix.rs b/src/event/sys/unix.rs index 43e2fac..b36bfe9 100644 --- a/src/event/sys/unix.rs +++ b/src/event/sys/unix.rs @@ -3,7 +3,10 @@ use std::{ os::unix::io::{IntoRawFd, RawFd}, }; -use libc::{c_int, c_void, size_t, ssize_t}; +use libc::size_t; + +#[cfg(feature = "event-stream")] +pub(crate) use waker::Waker; use crate::{ event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent}, @@ -12,22 +15,8 @@ use crate::{ use super::super::InternalEvent; -// libstd::sys::unix::fd.rs -fn max_len() -> usize { - // The maximum read limit on most posix-like systems is `SSIZE_MAX`, - // with the man page quoting that if the count of bytes to read is - // greater than `SSIZE_MAX` the result is "unspecified". - // - // On macOS, however, apparently the 64-bit libc is either buggy or - // intentionally showing odd behavior by rejecting any read with a size - // larger than or equal to INT_MAX. To handle both of these the read - // size is capped on both platforms. - if cfg!(target_os = "macos") { - ::max_value() as usize - 1 - } else { - ::max_value() as usize - } -} +#[cfg(feature = "event-stream")] +mod waker; /// A file descriptor wrapper. /// @@ -65,24 +54,6 @@ impl FileDesc { } } - pub fn write(&self, buf: &[u8]) -> io::Result { - // libstd::sys::unix::fd.rs - - let ret = unsafe { - libc::write( - self.fd, - buf.as_ptr() as *const c_void, - std::cmp::min(buf.len(), max_len()) as size_t, - ) as c_int - }; - - if ret == -1 { - return Err(io::Error::last_os_error()); - } - - Ok(ret as usize) - } - /// Returns the underlying file descriptor. pub fn raw_fd(&self) -> RawFd { self.fd diff --git a/src/event/sys/unix/waker.rs b/src/event/sys/unix/waker.rs new file mode 100644 index 0000000..adc0ae5 --- /dev/null +++ b/src/event/sys/unix/waker.rs @@ -0,0 +1,100 @@ +// TODO Replace with `mio::Waker` when the 0.7 is released (not available in 0.6). + +use std::sync::{Arc, Mutex}; + +use mio::{Evented, Poll, PollOpt, Ready, Registration, SetReadiness, Token}; + +use crate::Result; + +struct WakerInner { + registration: Registration, + set_readiness: SetReadiness, +} + +impl WakerInner { + fn new() -> Self { + let (registration, set_readiness) = Registration::new2(); + + Self { + registration, + set_readiness, + } + } + + fn wake(&self) -> Result<()> { + self.set_readiness.set_readiness(Ready::readable())?; + Ok(()) + } + + fn reset(&self) -> Result<()> { + self.set_readiness.set_readiness(Ready::empty())?; + Ok(()) + } +} + +/// Allows to wake up the `mio::Poll::poll()` method. +#[derive(Clone)] +pub(crate) struct Waker { + inner: Arc>, +} + +impl Waker { + /// Creates a new waker. + /// + /// `Waker` implements the `mio::Evented` trait and you have to register + /// it in order to use it. + pub(crate) fn new() -> Result { + Ok(Self { + inner: Arc::new(Mutex::new(WakerInner::new())), + }) + } + + /// Wakes the `mio::Poll.poll()` method. + /// + /// Readiness is set to `Ready::readable()`. + pub(crate) fn wake(&self) -> Result<()> { + self.inner.lock().unwrap().wake() + } + + /// Resets the state so the same waker can be reused. + /// + /// Readiness is set back to `Ready::empty()`. + pub(crate) fn reset(&self) -> Result<()> { + self.inner.lock().unwrap().reset() + } +} + +impl Evented for Waker { + fn register( + &self, + poll: &Poll, + token: Token, + interest: Ready, + opts: PollOpt, + ) -> ::std::io::Result<()> { + self.inner + .lock() + .unwrap() + .registration + .register(poll, token, interest, opts) + } + + fn reregister( + &self, + poll: &Poll, + token: Token, + interest: Ready, + opts: PollOpt, + ) -> ::std::io::Result<()> { + self.inner + .lock() + .unwrap() + .registration + .reregister(poll, token, interest, opts) + } + + #[allow(deprecated)] + fn deregister(&self, poll: &Poll) -> ::std::io::Result<()> { + self.inner.lock().unwrap().registration.deregister(poll) + } +} diff --git a/src/event/sys/windows.rs b/src/event/sys/windows.rs index c0b9bd1..824b0f7 100644 --- a/src/event/sys/windows.rs +++ b/src/event/sys/windows.rs @@ -1,10 +1,9 @@ //! This is a WINDOWS specific implementation for input related action. -use std::{io, io::ErrorKind, sync::Mutex, time::Duration}; +use std::{io, sync::Mutex, time::Duration}; use crossterm_winapi::{ ConsoleMode, ControlKeyState, EventFlags, Handle, KeyEventRecord, MouseEvent, ScreenBuffer, - Semaphore, }; use winapi::{ shared::winerror::WAIT_TIMEOUT, @@ -23,12 +22,17 @@ use winapi::{ }; use lazy_static::lazy_static; +#[cfg(feature = "event-stream")] +pub(crate) use waker::Waker; use crate::{ event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton}, Result, }; +#[cfg(feature = "event-stream")] +mod waker; + const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008; lazy_static! { @@ -241,12 +245,21 @@ fn parse_mouse_event_record(event: &MouseEvent) -> Result, + #[cfg(feature = "event-stream")] + waker: Waker, } impl WinApiPoll { + #[cfg(not(feature = "event-stream"))] pub(crate) fn new() -> Result { - Ok(WinApiPoll { semaphore: None }) + Ok(WinApiPoll {}) + } + + #[cfg(feature = "event-stream")] + pub(crate) fn new() -> Result { + Ok(WinApiPoll { + waker: Waker::new()?, + }) } } @@ -258,46 +271,48 @@ impl WinApiPoll { INFINITE }; - let semaphore = Semaphore::new()?; let console_handle = Handle::current_in_handle()?; - let handles = &[*console_handle, semaphore.handle()]; - self.semaphore = Some(semaphore); + #[cfg(feature = "event-stream")] + let semaphore = self.waker.semaphore(); + #[cfg(feature = "event-stream")] + let handles = &[*console_handle, **semaphore.handle()]; + #[cfg(not(feature = "event-stream"))] + let handles = &[*console_handle]; let output = unsafe { WaitForMultipleObjects(handles.len() as u32, handles.as_ptr(), 0, dw_millis) }; - let result = match output { + match output { output if output == WAIT_OBJECT_0 => { // input handle triggered Ok(Some(true)) } + #[cfg(feature = "event-stream")] output if output == WAIT_OBJECT_0 + 1 => { // semaphore handle triggered - Ok(None) + let _ = self.waker.reset(); + Err(io::Error::new( + io::ErrorKind::Interrupted, + "Poll operation was woken up by `Waker::wake`", + ) + .into()) } WAIT_TIMEOUT | WAIT_ABANDONED_0 => { // timeout elapsed Ok(None) } - WAIT_FAILED => return Err(io::Error::last_os_error().into()), + WAIT_FAILED => Err(io::Error::last_os_error().into()), _ => Err(io::Error::new( - ErrorKind::Other, + io::ErrorKind::Other, "WaitForMultipleObjects returned unexpected result.", ) .into()), - }; - - self.semaphore = None; - - result + } } - pub fn cancel(&self) -> Result<()> { - if let Some(semaphore) = &self.semaphore { - semaphore.release()? - } - - Ok(()) + #[cfg(feature = "event-stream")] + pub fn waker(&self) -> Waker { + self.waker.clone() } } diff --git a/src/event/sys/windows/waker.rs b/src/event/sys/windows/waker.rs new file mode 100644 index 0000000..6bfccaf --- /dev/null +++ b/src/event/sys/windows/waker.rs @@ -0,0 +1,42 @@ +use std::sync::{Arc, Mutex}; + +use crossterm_winapi::Semaphore; + +use crate::Result; + +/// Allows to wake up the `WinApiPoll::poll()` method. +#[derive(Clone)] +pub(crate) struct Waker { + inner: Arc>, +} + +impl Waker { + /// Creates a new waker. + /// + /// `Waker` is based on the `Semaphore`. You have to use the semaphore + /// handle along with the `WaitForMultipleObjects`. + pub(crate) fn new() -> Result { + let inner = Semaphore::new()?; + + Ok(Self { + inner: Arc::new(Mutex::new(inner)), + }) + } + + /// Wakes the `WaitForMultipleObjects`. + pub(crate) fn wake(&self) -> Result<()> { + self.inner.lock().unwrap().release()?; + Ok(()) + } + + /// Replaces the current semaphore with a new one allowing us to reuse the same `Waker`. + pub(crate) fn reset(&self) -> Result<()> { + *self.inner.lock().unwrap() = Semaphore::new()?; + Ok(()) + } + + /// Returns the semaphore associated with the waker. + pub(crate) fn semaphore(&self) -> Semaphore { + self.inner.lock().unwrap().clone() + } +} diff --git a/src/utils/functions.rs b/src/utils/functions.rs index a7c9e2d..2a806a2 100644 --- a/src/utils/functions.rs +++ b/src/utils/functions.rs @@ -1,6 +1,7 @@ -use super::sys::windows::set_virtual_terminal_processing; use lazy_static::lazy_static; +use super::sys::windows::set_virtual_terminal_processing; + lazy_static! { static ref SUPPORTS_ANSI_ESCAPE_CODES: bool = { // Some terminals on windows like GitBash can't use WinaApi calls directly