Skip to content

Upgrade from 0.13 to 0.14

Timon edited this page Nov 23, 2019 · 19 revisions

Table of Contents

1. Release Notes

The following chapter contains explanation of new features, and how we implemented those.

1.1 New API

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 an Event immediately (if available) or blocks until an Event is available.
  • The poll function allows you to check if there is or isn't an Event available within the given period of time. In other words - if a subsequent call to the read the function will block or not.

Async API

You can enable the event-stream feature flag for event::EventStream, which implements future::Stream, and produces Result<Event>.

1.2 Terminal Resize Events

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.

1.3 Advanced Modifier Support

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.

1.4 Less Resource-Consuming

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.

1.5 Event Polling

UNIX

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.

Windows

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.

Migrations

In this chapter you can find notes on how to migrate to this version.

Feature Flags

  • Removed cursor, style, terminal, screen and input 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.

Motivation

  • 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 brings futures crate dependency.
  • The number of crates you have to compile is roughly doubled with this feature and the compile-time is 4x longer.

Removed TerminalInput

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)?;
Clone this wiki locally