Browse Source
Improve embed terminal
Improve embed terminal
- Add character attribute support - Add cursor key mode support - Fix buggy set fg / bg sequences And added a bin under tools to test arbitrary apps using the embedded terminal: cargo run -p tools --bin embed -- "htop" 2> .htop.debug.logmutt2meli
15 changed files with 1208 additions and 138 deletions
-
5Cargo.lock
-
4Cargo.toml
-
2src/components/mail/compose.rs
-
38src/conf/listing.rs
-
5src/jobs.rs
-
80src/lib.rs
-
53src/main.rs
-
2src/terminal.rs
-
48src/terminal/cells.rs
-
4src/terminal/color.rs
-
21src/terminal/embed.rs
-
456src/terminal/embed/grid.rs
-
33src/terminal/position.rs
-
14tools/Cargo.toml
-
581tools/src/embed.rs
@ -0,0 +1,80 @@ |
|||
/*
|
|||
* meli - lib.rs
|
|||
*
|
|||
* Copyright 2017-2022 Manos Pitsidianakis
|
|||
*
|
|||
* This file is part of meli.
|
|||
*
|
|||
* meli is free software: you can redistribute it and/or modify
|
|||
* it under the terms of the GNU General Public License as published by
|
|||
* the Free Software Foundation, either version 3 of the License, or
|
|||
* (at your option) any later version.
|
|||
*
|
|||
* meli is distributed in the hope that it will be useful,
|
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
* GNU General Public License for more details.
|
|||
*
|
|||
* You should have received a copy of the GNU General Public License
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/
|
|||
|
|||
//!
|
|||
//! This crate contains the frontend stuff of the application. The application entry way on
|
|||
//! `src/bin.rs` creates an event loop and passes input to a thread.
|
|||
//!
|
|||
//! The mail handling stuff is done in the `melib` crate which includes all backend needs. The
|
|||
//! split is done to theoretically be able to create different frontends with the same innards.
|
|||
//!
|
|||
|
|||
use std::alloc::System;
|
|||
pub use std::collections::VecDeque;
|
|||
pub use std::path::PathBuf;
|
|||
|
|||
#[macro_use]
|
|||
extern crate serde_derive;
|
|||
extern crate linkify;
|
|||
pub use melib::uuid;
|
|||
|
|||
pub extern crate bitflags;
|
|||
pub extern crate serde_json;
|
|||
#[macro_use]
|
|||
pub extern crate smallvec;
|
|||
pub extern crate termion;
|
|||
|
|||
pub use structopt::StructOpt;
|
|||
|
|||
#[global_allocator]
|
|||
static GLOBAL: System = System;
|
|||
|
|||
#[macro_use]
|
|||
extern crate melib;
|
|||
pub use melib::*;
|
|||
|
|||
#[macro_use]
|
|||
pub mod types;
|
|||
pub use crate::types::*;
|
|||
|
|||
#[macro_use]
|
|||
pub mod terminal;
|
|||
pub use crate::terminal::*;
|
|||
|
|||
#[macro_use]
|
|||
pub mod command;
|
|||
pub use crate::command::*;
|
|||
|
|||
pub mod state;
|
|||
pub use crate::state::*;
|
|||
|
|||
pub mod components;
|
|||
pub use crate::components::*;
|
|||
|
|||
#[macro_use]
|
|||
pub mod conf;
|
|||
pub use crate::conf::*;
|
|||
|
|||
#[cfg(feature = "sqlite3")]
|
|||
pub mod sqlite3;
|
|||
|
|||
pub mod jobs;
|
|||
pub mod mailcap;
|
@ -1,7 +1,582 @@ |
|||
extern crate ui;
|
|||
use ui::terminal::embed::create_pty;
|
|||
use meli::terminal::embed::*;
|
|||
use meli::terminal::*;
|
|||
use meli::*;
|
|||
use nix::sys::wait::WaitStatus;
|
|||
use std::fs::File;
|
|||
use std::io::prelude::*;
|
|||
use std::os::raw::c_int;
|
|||
use std::sync::{Arc, Mutex};
|
|||
|
|||
fn notify(
|
|||
signals: &[c_int],
|
|||
sender: crossbeam::channel::Sender<ThreadEvent>,
|
|||
) -> std::result::Result<crossbeam::channel::Receiver<c_int>, std::io::Error> {
|
|||
use std::time::Duration;
|
|||
let (alarm_pipe_r, alarm_pipe_w) =
|
|||
nix::unistd::pipe().map_err(|err| std::io::Error::from_raw_os_error(err as i32))?;
|
|||
let alarm_handler = move |info: &nix::libc::siginfo_t| {
|
|||
let value = unsafe { info.si_value().sival_ptr as u8 };
|
|||
let _ = nix::unistd::write(alarm_pipe_w, &[value]);
|
|||
};
|
|||
unsafe {
|
|||
signal_hook_registry::register_sigaction(signal_hook::consts::SIGALRM, alarm_handler)?;
|
|||
}
|
|||
let (s, r) = crossbeam::channel::bounded(100);
|
|||
let mut signals = signal_hook::iterator::Signals::new(signals)?;
|
|||
let _ = nix::fcntl::fcntl(
|
|||
alarm_pipe_r,
|
|||
nix::fcntl::FcntlArg::F_SETFL(nix::fcntl::OFlag::O_NONBLOCK),
|
|||
);
|
|||
std::thread::spawn(move || {
|
|||
let mut ctr = 0;
|
|||
loop {
|
|||
ctr %= 3;
|
|||
if ctr == 0 {
|
|||
let _ = sender
|
|||
.send_timeout(ThreadEvent::Pulse, Duration::from_millis(500))
|
|||
.ok();
|
|||
}
|
|||
|
|||
for signal in signals.pending() {
|
|||
let _ = s.send_timeout(signal, Duration::from_millis(500)).ok();
|
|||
}
|
|||
|
|||
std::thread::sleep(std::time::Duration::from_millis(100));
|
|||
ctr += 1;
|
|||
}
|
|||
});
|
|||
Ok(r)
|
|||
}
|
|||
|
|||
#[derive(Debug)]
|
|||
enum EmbedStatus {
|
|||
Stopped(Arc<Mutex<EmbedTerminal>>),
|
|||
Running(Arc<Mutex<EmbedTerminal>>),
|
|||
}
|
|||
|
|||
impl EmbedStatus {
|
|||
#[inline(always)]
|
|||
fn is_stopped(&self) -> bool {
|
|||
matches!(self, Self::Stopped(_))
|
|||
}
|
|||
}
|
|||
|
|||
impl std::ops::Deref for EmbedStatus {
|
|||
type Target = Arc<Mutex<EmbedTerminal>>;
|
|||
fn deref(&self) -> &Arc<Mutex<EmbedTerminal>> {
|
|||
use EmbedStatus::*;
|
|||
match self {
|
|||
Stopped(ref e) | Running(ref e) => e,
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl std::ops::DerefMut for EmbedStatus {
|
|||
fn deref_mut(&mut self) -> &mut Arc<Mutex<EmbedTerminal>> {
|
|||
use EmbedStatus::*;
|
|||
match self {
|
|||
Stopped(ref mut e) | Running(ref mut e) => e,
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Debug)]
|
|||
struct EmbedContainer {
|
|||
command: String,
|
|||
embed_area: Area,
|
|||
embed: Option<EmbedStatus>,
|
|||
id: ComponentId,
|
|||
dirty: bool,
|
|||
log_file: File,
|
|||
}
|
|||
|
|||
impl EmbedContainer {
|
|||
fn new(command: String) -> Box<dyn Component> {
|
|||
Box::new(Self {
|
|||
command,
|
|||
embed: None,
|
|||
embed_area: ((0, 0), (80, 20)),
|
|||
dirty: true,
|
|||
log_file: File::open(".embed.out").unwrap(),
|
|||
id: ComponentId::new_v4(),
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl std::fmt::Display for EmbedContainer {
|
|||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|||
write!(fmt, "embed")
|
|||
}
|
|||
}
|
|||
|
|||
impl Component for EmbedContainer {
|
|||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
|||
if let Some(ref mut embed_pty) |