mirror of
https://github.com/zellij-org/zellij.git
synced 2024-11-23 08:57:14 +03:00
feat(ipc): listen to external ipc messages including a basic api
This commit is contained in:
parent
e50e9770fd
commit
bd5824ce3f
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -65,6 +65,16 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
@ -91,6 +101,12 @@ version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cache-padded"
|
||||
version = "1.1.1"
|
||||
@ -391,10 +407,12 @@ name = "mosaic"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"bincode",
|
||||
"futures",
|
||||
"insta",
|
||||
"libc",
|
||||
"nix",
|
||||
"serde",
|
||||
"signal-hook",
|
||||
"termios",
|
||||
"unicode-truncate",
|
||||
|
@ -15,6 +15,8 @@ unicode-width = "0.1.8"
|
||||
unicode-truncate = "0.1.1"
|
||||
vte = "0.8.0"
|
||||
futures = "0.3.5"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
bincode = "1.3.1"
|
||||
|
||||
[dependencies.async-std]
|
||||
version = "1.3.0"
|
||||
|
72
src/main.rs
72
src/main.rs
@ -10,10 +10,16 @@ mod boundaries;
|
||||
use std::io::{Read, Write};
|
||||
use ::std::thread;
|
||||
|
||||
use std::os::unix::net::{UnixStream, UnixListener};
|
||||
use std::io::prelude::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::os_input_output::{get_os_input, OsApi};
|
||||
use crate::terminal_pane::TerminalOutput;
|
||||
use crate::pty_bus::{VteEvent, PtyBus, PtyInstruction};
|
||||
use crate::screen::{Screen, ScreenInstruction};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::remove_file;
|
||||
|
||||
// sigwinch stuff
|
||||
use ::signal_hook::iterator::Signals;
|
||||
@ -21,6 +27,14 @@ use ::signal_hook::iterator::Signals;
|
||||
pub type OnSigWinch = dyn Fn(Box<dyn Fn()>) + Send;
|
||||
pub type SigCleanup = dyn Fn() + Send;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
enum ApiCommand {
|
||||
OpenFile(String),
|
||||
SplitHorizontally,
|
||||
SplitVertically,
|
||||
MoveFocus,
|
||||
}
|
||||
|
||||
fn debug_log_to_file (message: String) {
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::prelude::*;
|
||||
@ -68,17 +82,17 @@ pub fn start(mut os_input: Box<dyn OsApi>) {
|
||||
.name("pty".to_string())
|
||||
.spawn({
|
||||
move || {
|
||||
pty_bus.spawn_terminal_vertically();
|
||||
pty_bus.spawn_terminal_vertically(None);
|
||||
loop {
|
||||
let event = pty_bus.receive_pty_instructions
|
||||
.recv()
|
||||
.expect("failed to receive event on channel");
|
||||
match event {
|
||||
PtyInstruction::SpawnTerminalVertically => {
|
||||
pty_bus.spawn_terminal_vertically();
|
||||
PtyInstruction::SpawnTerminalVertically(file_to_open) => {
|
||||
pty_bus.spawn_terminal_vertically(file_to_open);
|
||||
}
|
||||
PtyInstruction::SpawnTerminalHorizontally => {
|
||||
pty_bus.spawn_terminal_horizontally();
|
||||
PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
|
||||
pty_bus.spawn_terminal_horizontally(file_to_open);
|
||||
}
|
||||
PtyInstruction::Quit => {
|
||||
break;
|
||||
@ -147,6 +161,50 @@ pub fn start(mut os_input: Box<dyn OsApi>) {
|
||||
}).unwrap()
|
||||
);
|
||||
|
||||
// TODO: currently we don't push this into active_threads
|
||||
// because otherwise the app will hang. Need to fix this so it both
|
||||
// listens to the ipc-bus and is able to quit cleanly
|
||||
#[cfg(not(test))]
|
||||
let ipc_thread = thread::Builder::new()
|
||||
.name("ipc_server".to_string())
|
||||
.spawn({
|
||||
let send_pty_instructions = send_pty_instructions.clone();
|
||||
let send_screen_instructions = send_screen_instructions.clone();
|
||||
move || {
|
||||
remove_file("/tmp/mosaic").ok();
|
||||
let listener = UnixListener::bind("/tmp/mosaic").expect("could not listen on ipc socket");
|
||||
|
||||
for stream in listener.incoming() {
|
||||
match stream {
|
||||
Ok(mut stream) => {
|
||||
let mut buffer = [0; 65535]; // TODO: more accurate
|
||||
stream.read(&mut buffer).expect("failed to parse ipc message");
|
||||
let decoded: ApiCommand = bincode::deserialize(&buffer).expect("failed to deserialize ipc message");
|
||||
match &decoded {
|
||||
ApiCommand::OpenFile(file_name) => {
|
||||
let path = PathBuf::from(file_name);
|
||||
send_pty_instructions.send(PtyInstruction::SpawnTerminalVertically(Some(path))).unwrap();
|
||||
}
|
||||
ApiCommand::SplitHorizontally => {
|
||||
send_pty_instructions.send(PtyInstruction::SpawnTerminalHorizontally(None)).unwrap();
|
||||
}
|
||||
ApiCommand::SplitVertically => {
|
||||
send_pty_instructions.send(PtyInstruction::SpawnTerminalVertically(None)).unwrap();
|
||||
}
|
||||
ApiCommand::MoveFocus => {
|
||||
send_screen_instructions.send(ScreenInstruction::MoveFocus).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("err {:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
|
||||
let mut stdin = os_input.get_stdin_reader();
|
||||
loop {
|
||||
let mut buffer = [0; 1];
|
||||
@ -162,9 +220,9 @@ pub fn start(mut os_input: Box<dyn OsApi>) {
|
||||
} else if buffer[0] == 12 { // ctrl-l
|
||||
send_screen_instructions.send(ScreenInstruction::ResizeRight).unwrap();
|
||||
} else if buffer[0] == 14 { // ctrl-n
|
||||
send_pty_instructions.send(PtyInstruction::SpawnTerminalVertically).unwrap();
|
||||
send_pty_instructions.send(PtyInstruction::SpawnTerminalVertically(None)).unwrap();
|
||||
} else if buffer[0] == 2 { // ctrl-b
|
||||
send_pty_instructions.send(PtyInstruction::SpawnTerminalHorizontally).unwrap();
|
||||
send_pty_instructions.send(PtyInstruction::SpawnTerminalHorizontally(None)).unwrap();
|
||||
} else if buffer[0] == 17 { // ctrl-q
|
||||
send_screen_instructions.send(ScreenInstruction::Quit).unwrap();
|
||||
send_pty_instructions.send(PtyInstruction::Quit).unwrap();
|
||||
|
@ -12,6 +12,7 @@ use nix::pty::{forkpty, Winsize};
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::process::Command;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use std::env;
|
||||
|
||||
@ -55,7 +56,7 @@ pub fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) {
|
||||
unsafe { ioctl(fd, TIOCSWINSZ.into(), &winsize) };
|
||||
}
|
||||
|
||||
fn spawn_terminal () -> (RawFd, RawFd) {
|
||||
fn spawn_terminal (file_to_open: Option<PathBuf>) -> (RawFd, RawFd) {
|
||||
let (pid_primary, pid_secondary): (RawFd, RawFd) = {
|
||||
match forkpty(None, None) {
|
||||
Ok(fork_pty_res) => {
|
||||
@ -67,9 +68,22 @@ fn spawn_terminal () -> (RawFd, RawFd) {
|
||||
child
|
||||
},
|
||||
ForkResult::Child => {
|
||||
Command::new(env::var("SHELL").unwrap()).spawn().expect("failed to spawn");
|
||||
::std::thread::park(); // TODO: if we remove this, we seem to lose bytes from stdin - find out why
|
||||
Pid::from_raw(0) // TODO: better
|
||||
match file_to_open {
|
||||
Some(file_to_open) => {
|
||||
if env::var("EDITOR").is_err() && env::var("VISUAL").is_err() {
|
||||
panic!("Can't edit files if an editor is not define. To fix: define the EDITOR or VISUAL environment variables with the path to your editor (eg. /usr/bin/vim)");
|
||||
}
|
||||
let editor = env::var("EDITOR").unwrap_or_else(|_| env::var("VISUAL").unwrap());
|
||||
Command::new(editor).args(&[file_to_open]).spawn().expect("failed to spawn");
|
||||
::std::thread::park(); // TODO: if we remove this, we seem to lose bytes from stdin - find out why
|
||||
Pid::from_raw(0) // TODO: better
|
||||
},
|
||||
None => {
|
||||
Command::new(env::var("SHELL").unwrap()).spawn().expect("failed to spawn");
|
||||
::std::thread::park(); // TODO: if we remove this, we seem to lose bytes from stdin - find out why
|
||||
Pid::from_raw(0) // TODO: better
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
(pid_primary, pid_secondary.as_raw())
|
||||
@ -89,7 +103,7 @@ pub trait OsApi: Send + Sync {
|
||||
fn get_terminal_size_using_fd(&self, pid: RawFd) -> Winsize;
|
||||
fn set_terminal_size_using_fd(&mut self, pid: RawFd, cols: u16, rows: u16);
|
||||
fn into_raw_mode(&mut self, pid: RawFd);
|
||||
fn spawn_terminal(&mut self) -> (RawFd, RawFd);
|
||||
fn spawn_terminal(&mut self, file_to_open: Option<PathBuf>) -> (RawFd, RawFd);
|
||||
fn read_from_tty_stdout(&mut self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
||||
fn write_to_tty_stdin(&mut self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error>;
|
||||
fn tcdrain(&mut self, pid: RawFd) -> Result<(), nix::Error>;
|
||||
@ -109,8 +123,8 @@ impl OsApi for OsInputOutput {
|
||||
fn into_raw_mode(&mut self, pid: RawFd) {
|
||||
into_raw_mode(pid);
|
||||
}
|
||||
fn spawn_terminal(&mut self) -> (RawFd, RawFd) {
|
||||
spawn_terminal()
|
||||
fn spawn_terminal(&mut self, file_to_open: Option<PathBuf>) -> (RawFd, RawFd) {
|
||||
spawn_terminal(file_to_open)
|
||||
}
|
||||
fn read_from_tty_stdout(&mut self, pid: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> {
|
||||
read(pid, buf)
|
||||
|
@ -5,6 +5,7 @@ use ::async_std::task::*;
|
||||
use ::std::pin::*;
|
||||
use ::std::sync::mpsc::{channel, Sender, Receiver};
|
||||
use ::std::time::{Instant, Duration};
|
||||
use std::path::{Path, PathBuf};
|
||||
use ::vte;
|
||||
|
||||
use crate::os_input_output::OsApi;
|
||||
@ -133,8 +134,8 @@ impl vte::Perform for VteEventSender {
|
||||
}
|
||||
|
||||
pub enum PtyInstruction {
|
||||
SpawnTerminalVertically,
|
||||
SpawnTerminalHorizontally,
|
||||
SpawnTerminalVertically(Option<PathBuf>),
|
||||
SpawnTerminalHorizontally(Option<PathBuf>),
|
||||
Quit
|
||||
}
|
||||
|
||||
@ -209,13 +210,13 @@ impl PtyBus {
|
||||
os_input,
|
||||
}
|
||||
}
|
||||
pub fn spawn_terminal_vertically(&mut self) {
|
||||
let (pid_primary, _pid_secondary): (RawFd, RawFd) = self.os_input.spawn_terminal();
|
||||
pub fn spawn_terminal_vertically(&mut self, file_to_open: Option<PathBuf>) {
|
||||
let (pid_primary, _pid_secondary): (RawFd, RawFd) = self.os_input.spawn_terminal(file_to_open);
|
||||
stream_terminal_bytes(pid_primary, self.send_screen_instructions.clone(), self.os_input.clone());
|
||||
self.send_screen_instructions.send(ScreenInstruction::VerticalSplit(pid_primary)).unwrap();
|
||||
}
|
||||
pub fn spawn_terminal_horizontally(&mut self) {
|
||||
let (pid_primary, _pid_secondary): (RawFd, RawFd) = self.os_input.spawn_terminal();
|
||||
pub fn spawn_terminal_horizontally(&mut self, file_to_open: Option<PathBuf>) {
|
||||
let (pid_primary, _pid_secondary): (RawFd, RawFd) = self.os_input.spawn_terminal(file_to_open);
|
||||
stream_terminal_bytes(pid_primary, self.send_screen_instructions.clone(), self.os_input.clone());
|
||||
self.send_screen_instructions.send(ScreenInstruction::HorizontalSplit(pid_primary)).unwrap();
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use ::std::os::unix::io::RawFd;
|
||||
use ::std::io::{Read, Write};
|
||||
use ::std::collections::HashMap;
|
||||
use ::std::sync::{Arc, Mutex};
|
||||
use ::std::path::PathBuf;
|
||||
|
||||
use crate::os_input_output::OsApi;
|
||||
use crate::tests::possible_tty_inputs::{Bytes, get_possible_tty_inputs};
|
||||
@ -118,7 +119,7 @@ impl OsApi for FakeInputOutput {
|
||||
fn into_raw_mode(&mut self, pid: RawFd) {
|
||||
self.io_events.lock().unwrap().push(IoEvent::IntoRawMode(pid));
|
||||
}
|
||||
fn spawn_terminal(&mut self) -> (RawFd, RawFd) {
|
||||
fn spawn_terminal(&mut self, file_to_open: Option<PathBuf>) -> (RawFd, RawFd) {
|
||||
let next_terminal_id = { self.read_buffers.lock().unwrap().keys().len() as RawFd + 1 };
|
||||
self.add_terminal(next_terminal_id);
|
||||
(next_terminal_id as i32, next_terminal_id + 1000) // secondary number is arbitrary here
|
||||
|
Loading…
Reference in New Issue
Block a user