-
Notifications
You must be signed in to change notification settings - Fork 305
Upgrade from 0.13 to 0.14
The following chapter contains explanation of new features, and how we implemented those.
We designed a new API that is flexible and easier to use. This API can be used in both asynchronous and synchronous environments. Please have a look over at the examples (event-*
) for detailed implementations.
The API consists of two functions:
- The
read
function returns anEvent
immediately (if available) or blocks until anEvent
is available. - The
poll
function allows you to check if there is or isn't anEvent
available within the given period of time. In other words - if a subsequent call to theread
the function will block or not.
You can enable the event-stream
feature flag for event::EventStream
, which implements future::Stream
, and produces Result<Event>
.
Terminal resize events can be captured when the size of the terminal is adjusted. For UNIX systems, crossterm captures resize signals with the signal-hook MIO extension. With windows, these are sent along in the INPUT_RECORD.
A modifier is a key like ALT, SHIFT, CTRL, META which can be pressed alongside another key. This version introduces modifier support for both mouse and key events.
Crossterm used to stored modifier information like:
InputEvent::Key(KeyEvent::Ctrl(char))
InputEvent::Key(KeyEvent::Alt(char))
InputEvent::Key(KeyEvent::ShiftLeft)
However, it would be a maintenance hell, if we had to keep track of all the keys with all the possible modifiers. And therefore also limited to the number of combinations we implement.
Therefore, we moved the modifier information out of the KeyEvent
into its own type, KeyModifier
. This type is sent along with the key and mouse events, and contains three fields: ALT
, CTRL
, SHIFT
. It uses bitflags and therefore can it contain multiple modifiers at the same time.
UNIX systems send events in the form of signals, and or over the TTY. To read events, you need to do a read
operation or set up a thread
to listen for the signal events. And so in the previous versions, this is what crossterm did.
However, we've put this time behind us, and are now using a poll/read
API that no longer works with a thread, spinning loop, or short polling.
Uses MIO to poll more efficiently for events. With that, we use the signal-hook crate to catch system signals. We only handle the terminal resize (SIGWINCH) signal.
Uses WaitForMultipleObjects, with this call we wait for a signal from eighter the input HANDLE or a semaphore HANDLE.
When we receive a signal from the input HANDLE, we do a check with GetNumberOfConsoleInputEvents to check if there is actually something to read, if there is we can perform a read with ReadConsoleInputW to read the available event.
The semaphore HANDLE can be used to cancel the waiting for signals. We no longer use a spinning loop to check if there are events to read and use proper signaling.
In this chapter you can find notes on how to migrate to this version.
- Removed
cursor
,style
,terminal
,screen
andinput
feature flags.- All the functionality is included by default and there's no way how to disable it.
- New feature flag
event-stream
.- Provides
EventStream
(futures::Stream
) that can be used for asynchronous event reading.
- Provides
- The previous features flags were getting unmanageable (cyclic dependencies, ...).
- Crossterm has shrunk a few thousand lines of code since the feature flags were introduced.
- Easier development on our side, easier usage on your side.
Why did we introduce event-stream
feature flag?
- The
event-stream
bringsfutures
crate dependency. - The number of crates you have to compile is roughly doubled with this feature and the compile-time is 4x longer.
Terminal Input and all of it's functions are removed:
-
read_line()
,read_char()
,enable_mouse_modes()
,disable_mouse_modes()
Those functions can be replaced with the following implementations:
read char
pub fn read_char() -> Result<char> {
loop {
if let Ok(Event::Key(KeyEvent {code: KeyCode::Char(c), .. })) = event::read()
{
return Ok(c);
}
}
}
read line
pub fn read_line() -> Result<String> {
let mut line = String::new();
while let Event::Key(KeyEvent { code: code, .. }) = event::read()? {
match code {
KeyCode::Enter => {
break;
},
KeyCode::Char(c) => {
line.push(c);
},
_ => {}
}
}
return Ok(line);
}
enable mouse modes
let mut stdout = stdout();
execute!(stdout, EnableMouseCapture)?;
disable mouse modes
let mut stdout = stdout();
execute!(stdout, DisableMouseCapture)?;