ui: embed editor cleanups

jmap
Manos Pitsidianakis 2019-11-19 22:47:34 +02:00
parent ce646abc7a
commit 62bfe2a91f
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
5 changed files with 85 additions and 59 deletions

View File

@ -126,14 +126,6 @@ impl ViewMode {
false false
} }
} }
fn is_embed(&self) -> bool {
if let ViewMode::Embed = self {
true
} else {
false
}
}
} }
impl fmt::Display for Composer { impl fmt::Display for Composer {
@ -543,23 +535,24 @@ impl Component for Composer {
/* Regardless of view mode, do the following */ /* Regardless of view mode, do the following */
self.form.draw(grid, header_area, context); self.form.draw(grid, header_area, context);
if let Some(ref mut embed_pty) = self.embed { if let Some(ref mut embed_pty) = self.embed {
let body_area = (upper_left!(header_area), bottom_right!(body_area)); let embed_area = (upper_left!(header_area), bottom_right!(body_area));
clear_area(grid, body_area);
match embed_pty { match embed_pty {
EmbedStatus::Running(_, _) => { EmbedStatus::Running(_, _) => {
let mut guard = embed_pty.lock().unwrap(); let mut guard = embed_pty.lock().unwrap();
clear_area(grid, embed_area);
copy_area( copy_area(
grid, grid,
&guard.grid, &guard.grid,
body_area, embed_area,
((0, 0), pos_dec(guard.terminal_size, (1, 1))), ((0, 0), pos_dec(guard.terminal_size, (1, 1))),
); );
guard.set_terminal_size((width!(body_area), height!(body_area))); guard.set_terminal_size((width!(embed_area), height!(embed_area)));
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
self.dirty = false; self.dirty = false;
return; return;
} }
EmbedStatus::Stopped(_, _) => { EmbedStatus::Stopped(_, _) => {
clear_area(grid, body_area);
write_string_to_grid( write_string_to_grid(
"process has stopped, press 'e' to re-activate", "process has stopped, press 'e' to re-activate",
grid, grid,
@ -569,9 +562,7 @@ impl Component for Composer {
body_area, body_area,
None, None,
); );
context.dirty_areas.push_back(body_area); context.dirty_areas.push_back(area);
self.dirty = false;
return;
} }
} }
} else { } else {
@ -706,7 +697,10 @@ impl Component for Composer {
} }
_ => {} _ => {}
} }
if self.cursor == Cursor::Headers && self.form.process_event(event, context) { if self.cursor == Cursor::Headers
&& self.mode.is_edit()
&& self.form.process_event(event, context)
{
if let UIEvent::InsertInput(_) = event { if let UIEvent::InsertInput(_) = event {
self.has_changes = true; self.has_changes = true;
} }
@ -762,6 +756,12 @@ impl Component for Composer {
} }
UIEvent::EmbedInput((Key::Ctrl('z'), _)) => { UIEvent::EmbedInput((Key::Ctrl('z'), _)) => {
self.embed.as_ref().unwrap().lock().unwrap().stop(); self.embed.as_ref().unwrap().lock().unwrap().stop();
match self.embed.take() {
Some(EmbedStatus::Running(e, f)) | Some(EmbedStatus::Stopped(e, f)) => {
self.embed = Some(EmbedStatus::Stopped(e, f));
}
_ => {}
}
context context
.replies .replies
.push_back(UIEvent::ChangeMode(UIMode::Normal)); .push_back(UIEvent::ChangeMode(UIMode::Normal));
@ -824,6 +824,10 @@ impl Component for Composer {
} }
_ => {} _ => {}
} }
self.mode = ViewMode::Edit;
context
.replies
.push_back(UIEvent::ChangeMode(UIMode::Normal));
self.dirty = true; self.dirty = true;
return true; return true;
} }
@ -852,15 +856,22 @@ impl Component for Composer {
self.set_dirty(); self.set_dirty();
return true; return true;
} }
UIEvent::Input(Key::Char('e')) if self.mode.is_embed() => { UIEvent::Input(Key::Char('e')) if self.embed.is_some() => {
self.embed.as_ref().unwrap().lock().unwrap().wake_up(); self.embed.as_ref().unwrap().lock().unwrap().wake_up();
match self.embed.take() {
Some(EmbedStatus::Running(e, f)) | Some(EmbedStatus::Stopped(e, f)) => {
self.embed = Some(EmbedStatus::Running(e, f));
}
_ => {}
}
self.mode = ViewMode::Embed;
context context
.replies .replies
.push_back(UIEvent::ChangeMode(UIMode::Embed)); .push_back(UIEvent::ChangeMode(UIMode::Embed));
self.set_dirty(); self.set_dirty();
return true; return true;
} }
UIEvent::Input(Key::Char('e')) => { UIEvent::Input(Key::Char('e')) if self.mode.is_edit() => {
/* Edit draft in $EDITOR */ /* Edit draft in $EDITOR */
let settings = &context.settings; let settings = &context.settings;
let editor = if let Some(editor_cmd) = settings.composing.editor_cmd.as_ref() { let editor = if let Some(editor_cmd) = settings.composing.editor_cmd.as_ref() {
@ -901,6 +912,9 @@ impl Component for Composer {
context context
.replies .replies
.push_back(UIEvent::ChangeMode(UIMode::Embed)); .push_back(UIEvent::ChangeMode(UIMode::Embed));
context.replies.push_back(UIEvent::Fork(ForkType::Embed(
self.embed.as_ref().unwrap().lock().unwrap().child_pid,
)));
self.mode = ViewMode::Embed; self.mode = ViewMode::Embed;
return true; return true;
} }

View File

@ -196,6 +196,13 @@ impl Drop for State {
fn drop(&mut self) { fn drop(&mut self) {
// When done, restore the defaults to avoid messing with the terminal. // When done, restore the defaults to avoid messing with the terminal.
self.switch_to_main_screen(); self.switch_to_main_screen();
if let Some(ForkType::Embed(child_pid)) = self.child.take() {
use nix::sys::wait::{waitpid, WaitPidFlag};
/* Try wait, we don't want to block */
if let Err(e) = waitpid(child_pid, Some(WaitPidFlag::WNOHANG)) {
eprintln!("Failed to wait on subprocess {}: {}", child_pid, e);
}
}
} }
} }

View File

@ -6,7 +6,7 @@ use melib::ERROR;
use nix::fcntl::{open, OFlag}; use nix::fcntl::{open, OFlag};
use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt, Winsize}; use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt, Winsize};
use nix::sys::{stat, wait::waitpid}; use nix::sys::stat;
use nix::unistd::{dup2, fork, ForkResult}; use nix::unistd::{dup2, fork, ForkResult};
use nix::{ioctl_none_bad, ioctl_write_ptr_bad}; use nix::{ioctl_none_bad, ioctl_write_ptr_bad};
use std::ffi::CString; use std::ffi::CString;
@ -66,45 +66,49 @@ pub fn create_pty(
/* Open slave end for pseudoterminal */ /* Open slave end for pseudoterminal */
let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())?; let slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, stat::Mode::empty())?;
let child_pid = match fork() { // assign stdin, stdout, stderr to the tty
Ok(ForkResult::Child) => { dup2(slave_fd, STDIN_FILENO).unwrap();
// assign stdin, stdout, stderr to the tty dup2(slave_fd, STDOUT_FILENO).unwrap();
dup2(slave_fd, STDIN_FILENO).unwrap(); dup2(slave_fd, STDERR_FILENO).unwrap();
dup2(slave_fd, STDOUT_FILENO).unwrap(); /* Become session leader */
dup2(slave_fd, STDERR_FILENO).unwrap(); nix::unistd::setsid().unwrap();
/* Become session leader */ match unsafe { set_controlling_terminal(slave_fd) } {
nix::unistd::setsid().unwrap(); Ok(c) if c < 0 => {
match unsafe { set_controlling_terminal(slave_fd) } { log(
Ok(c) if c < 0 => { format!(
log(format!("Could not execute `{}`: ioctl(fd, TIOCSCTTY, NULL) returned {}", command, c,), ERROR); "Could not execute `{}`: ioctl(fd, TIOCSCTTY, NULL) returned {}",
std::process::exit(c); command, c,
} ),
Ok(_) => {} ERROR,
Err(err) => { );
log(format!("Could not execute `{}`: ioctl(fd, TIOCSCTTY, NULL) returned {}", command, err,), ERROR); std::process::exit(c);
std::process::exit(-1); }
} Ok(_) => {}
} Err(err) => {
let parts = split_command!(command); log(
let (cmd, _) = (parts[0], &parts[1..]); format!(
if let Err(e) = nix::unistd::execv( "Could not execute `{}`: ioctl(fd, TIOCSCTTY, NULL) returned {}",
&CString::new(cmd).unwrap(), command, err,
&parts ),
.iter() ERROR,
.map(|&a| CString::new(a).unwrap()) );
.collect::<Vec<CString>>(),
) {
log(format!("Could not execute `{}`: {}", command, e,), ERROR);
std::process::exit(-1);
}
/* This path shouldn't be executed. */
std::process::exit(-1); std::process::exit(-1);
} }
Ok(ForkResult::Parent { child }) => child, }
Err(e) => panic!(e), let parts = split_command!(command);
}; let (cmd, _) = (parts[0], &parts[1..]);
waitpid(child_pid, None).unwrap(); if let Err(e) = nix::unistd::execv(
std::process::exit(0); &CString::new(cmd).unwrap(),
&parts
.iter()
.map(|&a| CString::new(a).unwrap())
.collect::<Vec<CString>>(),
) {
log(format!("Could not execute `{}`: {}", command, e,), ERROR);
std::process::exit(-1);
}
/* This path shouldn't be executed. */
std::process::exit(-1);
} }
Ok(ForkResult::Parent { child }) => child, Ok(ForkResult::Parent { child }) => child,
Err(e) => panic!(e), Err(e) => panic!(e),

View File

@ -633,7 +633,7 @@ impl EmbedGrid {
.write_all((terminal_size.0).to_string().as_bytes()) .write_all((terminal_size.0).to_string().as_bytes())
.unwrap(); .unwrap();
stdin.write_all(&[b't']).unwrap(); stdin.write_all(&[b't']).unwrap();
stdin.flush(); stdin.flush().unwrap();
} else { } else {
debug!("ignoring unknown code {}", EscCode::from((&(*state), byte))); debug!("ignoring unknown code {}", EscCode::from((&(*state), byte)));
} }
@ -653,7 +653,7 @@ impl EmbedGrid {
.write_all((cursor.0 + 1).to_string().as_bytes()) .write_all((cursor.0 + 1).to_string().as_bytes())
.unwrap(); .unwrap();
stdin.write_all(&[b'R']).unwrap(); stdin.write_all(&[b'R']).unwrap();
stdin.flush(); stdin.flush().unwrap();
*state = State::Normal; *state = State::Normal;
} }
(b'A', State::Csi1(buf)) => { (b'A', State::Csi1(buf)) => {

View File

@ -28,6 +28,7 @@ use super::terminal::*;
use melib::backends::FolderHash; use melib::backends::FolderHash;
use melib::{EnvelopeHash, RefreshEvent}; use melib::{EnvelopeHash, RefreshEvent};
use nix::unistd::Pid;
use std; use std;
use std::fmt; use std::fmt;
use std::thread; use std::thread;
@ -69,7 +70,7 @@ pub enum ForkType {
/// Already finished fork, we only want to restore input/output /// Already finished fork, we only want to restore input/output
Finished, Finished,
/// Embed pty /// Embed pty
Embed, Embed(Pid),
Generic(std::process::Child), Generic(std::process::Child),
NewDraft(File, std::process::Child), NewDraft(File, std::process::Child),
} }