Skip to content
This repository has been archived by the owner on Oct 22, 2019. It is now read-only.

Commit

Permalink
Slightly different approach
Browse files Browse the repository at this point in the history
Signed-off-by: Robert Vojta <rvojta@me.com>
  • Loading branch information
zrzka committed Oct 11, 2019
1 parent 0efd4c1 commit d1af8c8
Show file tree
Hide file tree
Showing 4 changed files with 329 additions and 224 deletions.
20 changes: 13 additions & 7 deletions examples/foo.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
// TEMPORARY IN THIS PR - IT WONT BE PART OF THE FINAL PR

#![allow(dead_code)]
use std::io::{stdout, Write};

use crossterm_input::{
InputEvent, InternalEvent, KeyEvent, MouseEvent, RawScreen, Result, TerminalInput,
};
use crossterm_input::{InputEvent, KeyEvent, MouseEvent, RawScreen, Result, TerminalInput};

// Sample implementation for crossterm_cursor & pos_raw
//
// This is sample `crossterm_cursor::pos_raw` implementation we will have to use.
//
// Once the crates will be merged, `pos_raw` will gain access to `internal_event_receiver()`
// function and thus we can drop `InputEvent::CursorPosition` and use `InternalEvent::CursorPosition`
// to hide this implementation detail from the user.
//
fn pos_raw() -> Result<(u16, u16)> {
let input = TerminalInput::new();
let mut reader = input.read_sync();
Expand All @@ -16,7 +22,7 @@ fn pos_raw() -> Result<(u16, u16)> {
stdout.flush()?;

loop {
if let Some(InputEvent::Internal(InternalEvent::CursorPosition(x, y))) = reader.next() {
if let Some(InputEvent::CursorPosition(x, y)) = reader.next() {
return Ok((x, y));
}
}
Expand All @@ -43,7 +49,7 @@ fn async_test() -> Result<()> {
_ => {}
};
}
InputEvent::Internal(_) => {}
InputEvent::CursorPosition(_, _) => {}
e => {
println!("Event: {:?}", e);
}
Expand Down Expand Up @@ -76,7 +82,7 @@ fn sync_test() -> Result<()> {
_ => {}
};
}
InputEvent::Internal(_) => {}
InputEvent::CursorPosition(_, _) => {}
e => {
println!("Event: {:?}", e);
}
Expand Down
76 changes: 55 additions & 21 deletions src/input/unix.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! This is a UNIX specific implementation for input related action.
use std::sync::mpsc::Receiver;
use std::{char, sync::mpsc};

use crossterm_utils::{csi, write_cout, Result};

use crate::sys::unix::event_receiver;
use crate::{input::Input, InputEvent, KeyEvent};
use crate::sys::unix::internal_event_receiver;
use crate::{input::Input, InputEvent, InternalEvent, KeyEvent};

pub(crate) struct UnixInput;

Expand Down Expand Up @@ -126,7 +127,7 @@ impl Input for UnixInput {
/// } // `reader` dropped <- thread cleaned up, `_raw` dropped <- raw mode disabled
/// ```
pub struct AsyncReader {
rx: Option<mpsc::Receiver<InputEvent>>,
rx: Option<Receiver<InternalEvent>>,
sentinel: Option<InputEvent>,
}

Expand All @@ -139,7 +140,7 @@ impl AsyncReader {
/// * The reading thread is cleaned up when you drop the `AsyncReader`.
fn new(sentinel: Option<InputEvent>) -> AsyncReader {
AsyncReader {
rx: Some(event_receiver()),
rx: Some(internal_event_receiver()),
sentinel,
}
}
Expand All @@ -165,20 +166,39 @@ impl Iterator for AsyncReader {
/// `None` doesn't mean that the iteration is finished. See the
/// [`AsyncReader`](struct.AsyncReader.html) documentation for more information.
fn next(&mut self) -> Option<Self::Item> {
// TODO 1.0: This whole `InternalEvent` -> `InputEvent` mapping should be shared
// between UNIX & Windows implementations
if let Some(rx) = self.rx.take() {
let result = match rx.try_recv() {
Ok(x) => Some(x),
Err(mpsc::TryRecvError::Empty) => None,
// TODO 1.0: We should propagate errors here?
Err(mpsc::TryRecvError::Disconnected) => None,
};
match rx.try_recv() {
Ok(internal_event) => {
// Map `InternalEvent` to `InputEvent`
match internal_event.into() {
Some(input_event) => {
// Did we reach sentinel?
if !(self.sentinel.is_some()
&& self.sentinel.as_ref() == Some(&input_event))
{
// No sentinel reached, put the receiver back
self.rx = Some(rx);
}

if !(self.sentinel.is_some() && self.sentinel == result) {
// No sentinel reached, put the receiver back
self.rx = Some(rx);
Some(input_event)
}
None => {
// `InternalEvent` swallowed, put the receiver back for another events
self.rx = Some(rx);
None
}
}
}
Err(mpsc::TryRecvError::Empty) => {
// No event, put the receiver back for another events
self.rx = Some(rx);
None
}
// Sender closed, drop the receiver (do not put it back)
Err(mpsc::TryRecvError::Disconnected) => None,
}

result
} else {
None
}
Expand Down Expand Up @@ -241,13 +261,13 @@ impl Iterator for AsyncReader {
/// } // `_raw` dropped <- raw mode disabled
/// ```
pub struct SyncReader {
rx: mpsc::Receiver<InputEvent>,
rx: Option<Receiver<InternalEvent>>,
}

impl SyncReader {
fn new() -> SyncReader {
SyncReader {
rx: event_receiver(),
rx: Some(internal_event_receiver()),
}
}
}
Expand All @@ -260,10 +280,24 @@ impl Iterator for SyncReader {
/// `None` doesn't mean that the iteration is finished. See the
/// [`SyncReader`](struct.SyncReader.html) documentation for more information.
fn next(&mut self) -> Option<Self::Item> {
match self.rx.recv() {
Ok(x) => Some(x),
// TODO 1.0: We should propagate errors here?
Err(mpsc::RecvError) => None,
// TODO 1.0: This whole `InternalEvent` -> `InputEvent` mapping should be shared
// between UNIX & Windows implementations
if let Some(rx) = self.rx.take() {
match rx.recv() {
Ok(internal_event) => {
// We have an internal event, map it to `InputEvent`
let event = internal_event.into();

// Put the receiver back for more events
self.rx = Some(rx);

event
}
// Sender is closed, do not put receiver back
Err(mpsc::RecvError) => None,
}
} else {
None
}
}
}
37 changes: 28 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,10 @@ pub enum InputEvent {
Unsupported(Vec<u8>), // TODO Not used, should be removed.
/// An unknown event.
Unknown,
/// For INTERNAL use only, will be removed!
Internal(InternalEvent),
}

/// For INTERNAL use only, will be removed!
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialOrd, PartialEq, Hash, Clone)]
pub enum InternalEvent {
CursorPosition(u16, u16),
/// Internal cursor position event. Don't use it, it will be removed in the
/// `crossterm` 1.0.
#[doc(hidden)]
CursorPosition(u16, u16), // TODO 1.0: Remove
}

/// Represents a mouse event.
Expand Down Expand Up @@ -179,6 +174,30 @@ pub enum KeyEvent {
ShiftLeft,
}

/// An internal event.
///
/// Encapsulates publicly available `InputEvent` with additional internal
/// events that shouldn't be publicly available to the crate users.
#[derive(Debug, PartialOrd, PartialEq, Hash, Clone)]
pub(crate) enum InternalEvent {
/// An input event.
Input(InputEvent),
/// A cursor position (`x`, `y`).
CursorPosition(u16, u16),
}

/// Converts an `InternalEvent` into a possible `InputEvent`.
impl From<InternalEvent> for Option<InputEvent> {
fn from(ie: InternalEvent) -> Self {
match ie {
InternalEvent::Input(input_event) => Some(input_event),
// TODO 1.0: Swallow `CursorPosition` and return `None`.
// `cursor::pos_raw()` will be able to use this module `internal_event_receiver()`
InternalEvent::CursorPosition(x, y) => Some(InputEvent::CursorPosition(x, y)),
}
}
}

/// A terminal input.
///
/// # Examples
Expand Down
Loading

0 comments on commit d1af8c8

Please sign in to comment.