aboutsummaryrefslogtreecommitdiffstats
path: root/examples/tmux_impl/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/tmux_impl/mod.rs')
-rw-r--r--examples/tmux_impl/mod.rs220
1 files changed, 101 insertions, 119 deletions
diff --git a/examples/tmux_impl/mod.rs b/examples/tmux_impl/mod.rs
index de026e4..313aaf2 100644
--- a/examples/tmux_impl/mod.rs
+++ b/examples/tmux_impl/mod.rs
@@ -1,12 +1,13 @@
-use pty_process::Command as _;
-use smol::io::{AsyncReadExt as _, AsyncWriteExt as _};
use textmode::Textmode as _;
+use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
+#[derive(Debug)]
enum Command {
NewWindow,
NextWindow,
}
+#[derive(Debug)]
enum Event {
Input(textmode::Key),
Output,
@@ -16,8 +17,8 @@ enum Event {
}
struct Window {
- child: std::sync::Arc<pty_process::smol::Child>,
- vt: std::sync::Arc<smol::lock::Mutex<vt100::Parser>>,
+ vt: std::sync::Arc<tokio::sync::Mutex<vt100::Parser>>,
+ pty_w: pty_process::OwnedWritePty,
screen: vt100::Screen,
}
@@ -33,13 +34,13 @@ struct State {
next_window_id: usize,
notifications: std::collections::BTreeMap<usize, Notification>,
next_notification_id: usize,
- wevents: smol::channel::Sender<Event>,
- revents: smol::channel::Receiver<Event>,
+ wevents: tokio::sync::mpsc::UnboundedSender<Event>,
+ revents: tokio::sync::mpsc::UnboundedReceiver<Event>,
}
impl State {
fn new() -> Self {
- let (sender, receiver) = smol::channel::unbounded();
+ let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
Self {
windows: std::collections::BTreeMap::new(),
current_window: 0,
@@ -59,7 +60,7 @@ impl State {
self.windows.get_mut(&self.current_window).unwrap()
}
- fn next_window(&mut self, ex: &smol::Executor<'_>) {
+ fn next_window(&mut self) {
self.current_window = self
.windows
.keys()
@@ -68,13 +69,10 @@ impl State {
.skip_while(|&id| id < self.current_window)
.nth(1)
.unwrap();
- self.notify(
- ex,
- &format!("switched to window {}", self.current_window),
- );
+ self.notify(&format!("switched to window {}", self.current_window));
}
- fn notify(&mut self, ex: &smol::Executor<'_>, text: &str) {
+ fn notify(&mut self, text: &str) {
let now = std::time::Instant::now();
let expiry = now + std::time::Duration::from_secs(5);
let text = text.to_string();
@@ -83,43 +81,35 @@ impl State {
self.next_notification_id += 1;
self.notifications.insert(id, notification);
let notify = self.wevents.clone();
- ex.spawn(async move {
- smol::Timer::at(expiry).await;
- notify.send(Event::Notification).await.unwrap();
- })
- .detach();
+ tokio::task::spawn(async move {
+ tokio::time::sleep_until(tokio::time::Instant::from_std(expiry))
+ .await;
+ notify.send(Event::Notification).unwrap();
+ });
}
- fn spawn_input_task(
- &self,
- ex: &smol::Executor<'_>,
- mut input: textmode::Input,
- ) {
+ fn spawn_input_thread(&self, mut input: textmode::blocking::Input) {
let notify = self.wevents.clone();
- ex.spawn(async move {
+ std::thread::spawn(move || {
let mut waiting_for_command = false;
input.parse_utf8(false);
input.parse_meta(false);
input.parse_special_keys(false);
loop {
input.parse_single(waiting_for_command);
- match input.read_key().await {
+ match input.read_key() {
Ok(Some(key)) => {
if waiting_for_command {
waiting_for_command = false;
match key {
textmode::Key::Ctrl(b'n') => {
- notify
- .send(Event::Input(key))
- .await
- .unwrap();
+ notify.send(Event::Input(key)).unwrap();
}
textmode::Key::Byte(b'c') => {
notify
.send(Event::Command(
Command::NewWindow,
))
- .await
.unwrap();
}
textmode::Key::Byte(b'n') => {
@@ -127,7 +117,6 @@ impl State {
.send(Event::Command(
Command::NextWindow,
))
- .await
.unwrap();
}
_ => {} // ignore
@@ -138,10 +127,7 @@ impl State {
waiting_for_command = true;
}
_ => {
- notify
- .send(Event::Input(key))
- .await
- .unwrap();
+ notify.send(Event::Input(key)).unwrap();
}
}
}
@@ -155,59 +141,67 @@ impl State {
}
}
}
- })
- .detach();
+ });
}
fn new_window(
&mut self,
- ex: &smol::Executor<'_>,
- notify: smol::channel::Sender<Event>,
+ notify: tokio::sync::mpsc::UnboundedSender<Event>,
) {
- let child = smol::process::Command::new("zsh")
- .spawn_pty(Some(&pty_process::Size::new(24, 80)))
- .unwrap();
- let child = std::sync::Arc::new(child);
+ let pty = pty_process::Pty::new().unwrap();
+ let pts = pty.pts().unwrap();
+ pty.resize(pty_process::Size::new(24, 80)).unwrap();
+ let mut cmd = pty_process::Command::new("zsh");
+ let mut child = cmd.spawn(&pts).unwrap();
+ let (mut pty_r, pty_w) = pty.into_split();
let vt = vt100::Parser::default();
let screen = vt.screen().clone();
- let vt = std::sync::Arc::new(smol::lock::Mutex::new(vt));
+ let vt = std::sync::Arc::new(tokio::sync::Mutex::new(vt));
let id = self.next_window_id;
self.next_window_id += 1;
let window = Window {
- child: child.clone(),
+ pty_w,
vt: vt.clone(),
screen,
};
self.windows.insert(id, window);
self.current_window = id;
- self.notify(ex, &format!("created window {}", id));
- ex.spawn(async move {
+ self.notify(&format!("created window {}", id));
+ tokio::task::spawn(async move {
+ let _pts = pts;
let mut buf = [0_u8; 4096];
loop {
- match child.pty().read(&mut buf).await {
- Ok(bytes) => {
- vt.lock_arc().await.process(&buf[..bytes]);
- notify.send(Event::Output).await.unwrap();
- }
- Err(e) => {
- // EIO means that the process closed the other
- // end of the pty
- if e.raw_os_error() != Some(libc::EIO) {
+ tokio::select! {
+ bytes = pty_r.read(&mut buf) => match bytes {
+ Ok(bytes) => {
+ if bytes == 0 {
+ continue;
+ }
+ vt.clone()
+ .lock_owned()
+ .await
+ .process(&buf[..bytes]);
+ notify.send(Event::Output).unwrap();
+ }
+ Err(e) => {
eprintln!("pty read failed: {:?}", e);
+ break;
}
- notify.send(Event::WindowExit(id)).await.unwrap();
+ },
+ _ = child.wait() => {
+ notify.send(Event::WindowExit(id)).unwrap();
break;
- }
+ },
}
}
- })
- .detach();
+ });
}
async fn redraw_current_window(&mut self, tm: &mut textmode::Output) {
let window = self.current_window();
tm.clear();
- let new_screen = window.vt.lock_arc().await.screen().clone();
+ let new_screen =
+ window.vt.clone().lock_owned().await.screen().clone();
tm.write(&new_screen.state_formatted());
self.draw_notifications(tm, &new_screen);
tm.refresh().await.unwrap();
@@ -216,7 +210,8 @@ impl State {
async fn update_current_window(&mut self, tm: &mut textmode::Output) {
let window = self.current_window();
let old_screen = window.screen.clone();
- let new_screen = window.vt.lock_arc().await.screen().clone();
+ let new_screen =
+ window.vt.clone().lock_owned().await.screen().clone();
let diff = new_screen.state_diff(&old_screen);
self.clear_notifications(tm, &old_screen);
tm.write(&diff);
@@ -292,88 +287,75 @@ impl State {
#[must_use]
pub struct Tmux {
- input: textmode::Input,
+ input: textmode::blocking::Input,
tm: textmode::Output,
state: State,
}
impl Tmux {
pub async fn new() -> Self {
- let input = textmode::Input::new().await.unwrap();
+ let input = textmode::blocking::Input::new().unwrap();
let tm = textmode::Output::new().await.unwrap();
let state = State::new();
Self { input, tm, state }
}
- pub async fn run(self, ex: &smol::Executor<'_>) {
+ pub async fn run(self) {
let Self {
- input,
+ mut input,
mut tm,
mut state,
} = self;
- state.spawn_input_task(ex, input);
+ let _raw_guard = input.take_raw_guard();
+ state.spawn_input_thread(input);
- ex.run(async {
- state.new_window(ex, state.wevents.clone());
+ state.new_window(state.wevents.clone());
- loop {
- match state.revents.recv().await {
- Ok(Event::Output) => {
- state.update_current_window(&mut tm).await;
+ loop {
+ match state.revents.recv().await {
+ Some(Event::Output) => {
+ state.update_current_window(&mut tm).await;
+ }
+ Some(Event::Input(key)) => {
+ state
+ .current_window_mut()
+ .pty_w
+ .write_all(&key.into_bytes())
+ .await
+ .unwrap();
+ }
+ Some(Event::WindowExit(id)) => {
+ // do this first because next_window breaks if
+ // current_window is greater than all existing windows
+ if state.current_window == id {
+ state.next_window()
}
- Ok(Event::Input(key)) => {
- state
- .current_window()
- .child
- .pty()
- .write_all(&key.into_bytes())
- .await
- .unwrap();
+ state.windows.remove(&id).unwrap();
+ if state.windows.is_empty() {
+ break;
}
- Ok(Event::WindowExit(id)) => {
- // do this first because next_window breaks if
- // current_window is greater than all existing windows
- if state.current_window == id {
- state.next_window(ex)
- }
- let mut dropped_window =
- state.windows.remove(&id).unwrap();
- // i can get_mut because at this point the future
- // holding the other copy of child has already been
- // dropped
- std::sync::Arc::get_mut(&mut dropped_window.child)
- .unwrap()
- .status()
- .await
- .unwrap();
- if state.windows.is_empty() {
- break;
- }
- state.notify(ex, &format!("window {} exited", id));
+ state.notify(&format!("window {} exited", id));
+ state.redraw_current_window(&mut tm).await;
+ }
+ Some(Event::Command(c)) => match c {
+ Command::NewWindow => {
+ state.new_window(state.wevents.clone());
state.redraw_current_window(&mut tm).await;
}
- Ok(Event::Command(c)) => match c {
- Command::NewWindow => {
- state.new_window(ex, state.wevents.clone());
- state.redraw_current_window(&mut tm).await;
- }
- Command::NextWindow => {
- state.next_window(ex);
- state.redraw_current_window(&mut tm).await;
- }
- },
- Ok(Event::Notification) => {
- state.update_current_window(&mut tm).await;
- }
- Err(e) => {
- eprintln!("{}", e);
- break;
+ Command::NextWindow => {
+ state.next_window();
+ state.redraw_current_window(&mut tm).await;
}
+ },
+ Some(Event::Notification) => {
+ state.update_current_window(&mut tm).await;
+ }
+ None => {
+ break;
}
}
- })
- .await;
+ }
}
}