aboutsummaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
authora-kenji <aks.kenji@protonmail.com>2021-04-17 00:15:52 +0200
committera-kenji <aks.kenji@protonmail.com>2021-04-17 00:15:52 +0200
commit60ad7494739b4cbcd857e74df9d05830459323eb (patch)
tree772d24c71de04d150a7b7a447842429e5a82e786 /src/common
parentAdd Unbind Setting for Keybinds (diff)
parentMerge branch 'main' of github.com:zellij-org/zellij (diff)
downloadsl0ppij-60ad7494739b4cbcd857e74df9d05830459323eb.tar.gz
sl0ppij-60ad7494739b4cbcd857e74df9d05830459323eb.tar.xz
sl0ppij-60ad7494739b4cbcd857e74df9d05830459323eb.zip
Merge branch 'main' of https://github.com/zellij-org/zellij into unbind-default-keys
Diffstat (limited to 'src/common')
-rw-r--r--src/common/command_is_executing.rs4
-rw-r--r--src/common/errors.rs24
-rw-r--r--src/common/input/actions.rs4
-rw-r--r--src/common/input/config.rs8
-rw-r--r--src/common/input/handler.rs58
-rw-r--r--src/common/input/keybinds.rs49
-rw-r--r--src/common/install.rs31
-rw-r--r--src/common/mod.rs118
-rw-r--r--src/common/os_input_output.rs21
-rw-r--r--src/common/pty_bus.rs103
-rw-r--r--src/common/screen.rs10
-rw-r--r--src/common/utils/logging.rs6
-rw-r--r--src/common/utils/shared.rs4
13 files changed, 246 insertions, 194 deletions
diff --git a/src/common/command_is_executing.rs b/src/common/command_is_executing.rs
index 93c44eb6..775a7bfc 100644
--- a/src/common/command_is_executing.rs
+++ b/src/common/command_is_executing.rs
@@ -23,7 +23,7 @@ impl CommandIsExecuting {
let (lock, cvar) = &*self.closing_pane;
let mut closing_pane = lock.lock().unwrap();
*closing_pane = false;
- cvar.notify_one();
+ cvar.notify_all();
}
pub fn opening_new_pane(&mut self) {
let (lock, _cvar) = &*self.opening_new_pane;
@@ -34,7 +34,7 @@ impl CommandIsExecuting {
let (lock, cvar) = &*self.opening_new_pane;
let mut opening_new_pane = lock.lock().unwrap();
*opening_new_pane = false;
- cvar.notify_one();
+ cvar.notify_all();
}
pub fn wait_until_pane_is_closed(&self) {
let (lock, cvar) = &*self.closing_pane;
diff --git a/src/common/errors.rs b/src/common/errors.rs
index 00284cd4..d0b748ab 100644
--- a/src/common/errors.rs
+++ b/src/common/errors.rs
@@ -1,7 +1,7 @@
//! Error context system based on a thread-local representation of the call stack, itself based on
//! the instructions that are sent between threads.
-use super::{AppInstruction, OPENCALLS};
+use super::{AppInstruction, ASYNCOPENCALLS, OPENCALLS};
use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction;
@@ -72,6 +72,12 @@ pub fn handle_panic(
}
}
+pub fn get_current_ctx() -> ErrorContext {
+ ASYNCOPENCALLS
+ .try_with(|ctx| *ctx.borrow())
+ .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow()))
+}
+
/// A representation of the call stack.
#[derive(Clone, Copy)]
pub struct ErrorContext {
@@ -95,7 +101,9 @@ impl ErrorContext {
break;
}
}
- OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self);
+ ASYNCOPENCALLS
+ .try_with(|ctx| *ctx.borrow_mut() = *self)
+ .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self));
}
}
@@ -168,7 +176,7 @@ impl Display for ContextType {
/// Stack call representations corresponding to the different types of [`ScreenInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ScreenContext {
- HandlePtyEvent,
+ HandlePtyBytes,
Render,
NewPane,
HorizontalSplit,
@@ -178,7 +186,9 @@ pub enum ScreenContext {
ResizeRight,
ResizeDown,
ResizeUp,
- MoveFocus,
+ SwitchFocus,
+ FocusNextPane,
+ FocusPreviousPane,
MoveFocusLeft,
MoveFocusDown,
MoveFocusUp,
@@ -208,7 +218,7 @@ pub enum ScreenContext {
impl From<&ScreenInstruction> for ScreenContext {
fn from(screen_instruction: &ScreenInstruction) -> Self {
match *screen_instruction {
- ScreenInstruction::Pty(..) => ScreenContext::HandlePtyEvent,
+ ScreenInstruction::PtyBytes(..) => ScreenContext::HandlePtyBytes,
ScreenInstruction::Render => ScreenContext::Render,
ScreenInstruction::NewPane(_) => ScreenContext::NewPane,
ScreenInstruction::HorizontalSplit(_) => ScreenContext::HorizontalSplit,
@@ -218,7 +228,9 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::ResizeRight => ScreenContext::ResizeRight,
ScreenInstruction::ResizeDown => ScreenContext::ResizeDown,
ScreenInstruction::ResizeUp => ScreenContext::ResizeUp,
- ScreenInstruction::MoveFocus => ScreenContext::MoveFocus,
+ ScreenInstruction::SwitchFocus => ScreenContext::SwitchFocus,
+ ScreenInstruction::FocusNextPane => ScreenContext::FocusNextPane,
+ ScreenInstruction::FocusPreviousPane => ScreenContext::FocusPreviousPane,
ScreenInstruction::MoveFocusLeft => ScreenContext::MoveFocusLeft,
ScreenInstruction::MoveFocusDown => ScreenContext::MoveFocusDown,
ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp,
diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs
index ac923f7d..47a5f763 100644
--- a/src/common/input/actions.rs
+++ b/src/common/input/actions.rs
@@ -24,8 +24,10 @@ pub enum Action {
/// Resize focus pane in specified direction.
Resize(Direction),
/// Switch focus to next pane in specified direction.
- SwitchFocus(Direction),
+ FocusNextPane,
+ FocusPreviousPane,
/// Move the focus pane in specified direction.
+ SwitchFocus,
MoveFocus(Direction),
/// Scroll up in focus pane.
ScrollUp,
diff --git a/src/common/input/config.rs b/src/common/input/config.rs
index 0713e54e..1926b11c 100644
--- a/src/common/input/config.rs
+++ b/src/common/input/config.rs
@@ -51,7 +51,6 @@ impl Config {
}
/// Deserializes from given path.
- #[allow(unused_must_use)]
pub fn new(path: &Path) -> ConfigResult {
match File::open(path) {
Ok(mut file) => {
@@ -79,6 +78,7 @@ impl Config {
}
/// Entry point of the configuration
+ #[cfg(not(test))]
pub fn from_cli_config(cli_config: Option<ConfigCli>) -> ConfigResult {
match cli_config {
Some(ConfigCli::Config { clean, .. }) if clean => Ok(Config::default()),
@@ -88,6 +88,12 @@ impl Config {
Some(_) | None => Ok(Config::from_default_path()?),
}
}
+
+ /// In order not to mess up tests from changing configurations
+ #[cfg(test)]
+ pub fn from_cli_config(_: Option<ConfigCli>) -> ConfigResult {
+ Ok(Config::default())
+ }
}
impl Display for ConfigError {
diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs
index 2020fc20..0afcab10 100644
--- a/src/common/input/handler.rs
+++ b/src/common/input/handler.rs
@@ -26,6 +26,7 @@ struct InputHandler {
send_pty_instructions: SenderWithContext<PtyInstruction>,
send_plugin_instructions: SenderWithContext<PluginInstruction>,
send_app_instructions: SenderWithContext<AppInstruction>,
+ should_exit: bool,
}
impl InputHandler {
@@ -48,6 +49,7 @@ impl InputHandler {
send_pty_instructions,
send_plugin_instructions,
send_app_instructions,
+ should_exit: false,
}
}
@@ -56,34 +58,30 @@ impl InputHandler {
fn handle_input(&mut self) {
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
err_ctx.add_call(ContextType::StdinHandler);
- self.send_pty_instructions.update(err_ctx);
- self.send_app_instructions.update(err_ctx);
- self.send_screen_instructions.update(err_ctx);
let keybinds = self.config.keybinds.clone();
- 'input_loop: loop {
- //@@@ I think this should actually just iterate over stdin directly
+ let alt_left_bracket = vec![27, 91];
+ loop {
+ if self.should_exit {
+ break;
+ }
let stdin_buffer = self.os_input.read_from_stdin();
for key_result in stdin_buffer.events_and_raw() {
match key_result {
Ok((event, raw_bytes)) => match event {
termion::event::Event::Key(key) => {
let key = cast_termion_key(key);
- // FIXME this explicit break is needed because the current test
- // framework relies on it to not create dead threads that loop
- // and eat up CPUs. Do not remove until the test framework has
- // been revised. Sorry about this (@categorille)
- let mut should_break = false;
- for action in
- Keybinds::key_to_actions(&key, raw_bytes, &self.mode, &keybinds)
- {
- should_break |= self.dispatch_action(action);
- }
- if should_break {
- break 'input_loop;
+ self.handle_key(&key, raw_bytes, &keybinds);
+ }
+ termion::event::Event::Unsupported(unsupported_key) => {
+ // we have to do this because of a bug in termion
+ // this should be a key event and not an unsupported event
+ if unsupported_key == alt_left_bracket {
+ let key = Key::Alt('[');
+ self.handle_key(&key, raw_bytes, &keybinds);
}
}
- termion::event::Event::Mouse(_) | termion::event::Event::Unsupported(_) => {
- // Mouse and unsupported events aren't implemented yet,
+ termion::event::Event::Mouse(_) => {
+ // Mouse events aren't implemented yet,
// use a NoOp untill then.
}
},
@@ -92,6 +90,14 @@ impl InputHandler {
}
}
}
+ fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>, keybinds: &Keybinds) {
+ for action in Keybinds::key_to_actions(&key, raw_bytes, &self.mode, &keybinds) {
+ let should_exit = self.dispatch_action(action);
+ if should_exit {
+ self.should_exit = true;
+ }
+ }
+ }
/// Dispatches an [`Action`].
///
@@ -144,9 +150,19 @@ impl InputHandler {
};
self.send_screen_instructions.send(screen_instr).unwrap();
}
- Action::SwitchFocus(_) => {
+ Action::SwitchFocus => {
+ self.send_screen_instructions
+ .send(ScreenInstruction::SwitchFocus)
+ .unwrap();
+ }
+ Action::FocusNextPane => {
+ self.send_screen_instructions
+ .send(ScreenInstruction::FocusNextPane)
+ .unwrap();
+ }
+ Action::FocusPreviousPane => {
self.send_screen_instructions
- .send(ScreenInstruction::MoveFocus)
+ .send(ScreenInstruction::FocusPreviousPane)
.unwrap();
}
Action::MoveFocus(direction) => {
diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs
index 653213a7..3b57dc02 100644
--- a/src/common/input/keybinds.rs
+++ b/src/common/input/keybinds.rs
@@ -129,6 +129,14 @@ impl Keybinds {
vec![Action::SwitchToMode(InputMode::Scroll)],
);
defaults.insert(Key::Ctrl('q'), vec![Action::Quit]);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::Locked => {
defaults.insert(
@@ -171,6 +179,14 @@ impl Keybinds {
defaults.insert(Key::Down, vec![Action::Resize(Direction::Down)]);
defaults.insert(Key::Up, vec![Action::Resize(Direction::Up)]);
defaults.insert(Key::Right, vec![Action::Resize(Direction::Right)]);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::Pane => {
defaults.insert(
@@ -211,7 +227,7 @@ impl Keybinds {
defaults.insert(Key::Up, vec![Action::MoveFocus(Direction::Up)]);
defaults.insert(Key::Right, vec![Action::MoveFocus(Direction::Right)]);
- defaults.insert(Key::Char('p'), vec![Action::SwitchFocus(Direction::Right)]);
+ defaults.insert(Key::Char('p'), vec![Action::SwitchFocus]);
defaults.insert(Key::Char('n'), vec![Action::NewPane(None)]);
defaults.insert(Key::Char('d'), vec![Action::NewPane(Some(Direction::Down))]);
defaults.insert(
@@ -220,6 +236,14 @@ impl Keybinds {
);
defaults.insert(Key::Char('x'), vec![Action::CloseFocus]);
defaults.insert(Key::Char('f'), vec![Action::ToggleFocusFullscreen]);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::Tab => {
defaults.insert(
@@ -278,6 +302,13 @@ impl Keybinds {
for i in '1'..='9' {
defaults.insert(Key::Char(i), vec![Action::GoToTab(i.to_digit(10).unwrap())]);
}
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::Scroll => {
defaults.insert(
@@ -310,6 +341,14 @@ impl Keybinds {
defaults.insert(Key::Down, vec![Action::ScrollDown]);
defaults.insert(Key::Up, vec![Action::ScrollUp]);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::RenameTab => {
defaults.insert(Key::Char('\n'), vec![Action::SwitchToMode(InputMode::Tab)]);
@@ -324,6 +363,14 @@ impl Keybinds {
Action::SwitchToMode(InputMode::Tab),
],
);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
}
ModeKeybinds(defaults)
diff --git a/src/common/install.rs b/src/common/install.rs
index 8b070848..86fdd269 100644
--- a/src/common/install.rs
+++ b/src/common/install.rs
@@ -1,12 +1,41 @@
+use std::{fs, path::Path};
+
+const VERSION: &str = env!("CARGO_PKG_VERSION");
+
#[macro_export]
macro_rules! asset_map {
($($src:literal => $dst:literal),+ $(,)?) => {
{
let mut assets = std::collections::HashMap::new();
$(
- assets.insert($dst, include_bytes!(concat!("../", $src)).to_vec());
+ assets.insert($dst, include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $src)).to_vec());
)+
assets
}
}
}
+
+pub fn populate_data_dir(data_dir: &Path) {
+ // First run installation of default plugins & layouts
+ let mut assets = asset_map! {
+ "assets/layouts/default.yaml" => "layouts/default.yaml",
+ "assets/layouts/strider.yaml" => "layouts/strider.yaml",
+ };
+ assets.extend(asset_map! {
+ "assets/plugins/status-bar.wasm" => "plugins/status-bar.wasm",
+ "assets/plugins/tab-bar.wasm" => "plugins/tab-bar.wasm",
+ "assets/plugins/strider.wasm" => "plugins/strider.wasm",
+ });
+ assets.insert("VERSION", VERSION.as_bytes().to_vec());
+
+ let last_version = fs::read_to_string(data_dir.join("VERSION")).unwrap_or_default();
+ let out_of_date = VERSION != last_version;
+
+ for (path, bytes) in assets {
+ let path = data_dir.join(path);
+ fs::create_dir_all(path.parent().unwrap()).unwrap();
+ if out_of_date || !path.exists() {
+ fs::write(path, bytes).expect("Failed to install default assets!");
+ }
+ }
+}
diff --git a/src/common/mod.rs b/src/common/mod.rs
index e9b74d58..860d12db 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -25,10 +25,15 @@ use crate::cli::CliArgs;
use crate::common::input::config::Config;
use crate::layout::Layout;
use crate::panes::PaneId;
+use async_std::task_local;
use command_is_executing::CommandIsExecuting;
use directories_next::ProjectDirs;
-use errors::{AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext};
+use errors::{
+ get_current_ctx, AppContext, ContextType, ErrorContext, PluginContext, PtyContext,
+ ScreenContext,
+};
use input::handler::input_loop;
+use install::populate_data_dir;
use os_input_output::OsApi;
use pty_bus::{PtyBus, PtyInstruction};
use screen::{Screen, ScreenInstruction};
@@ -72,32 +77,23 @@ enum SenderType<T: Clone> {
/// synchronously or asynchronously depending on the underlying [`SenderType`].
#[derive(Clone)]
pub struct SenderWithContext<T: Clone> {
- err_ctx: ErrorContext,
sender: SenderType<T>,
}
impl<T: Clone> SenderWithContext<T> {
- fn new(err_ctx: ErrorContext, sender: SenderType<T>) -> Self {
- Self { err_ctx, sender }
+ fn new(sender: SenderType<T>) -> Self {
+ Self { sender }
}
/// Sends an event, along with the current [`ErrorContext`], on this
/// [`SenderWithContext`]'s channel.
pub fn send(&self, event: T) -> Result<(), mpsc::SendError<(T, ErrorContext)>> {
+ let err_ctx = get_current_ctx();
match self.sender {
- SenderType::Sender(ref s) => s.send((event, self.err_ctx)),
- SenderType::SyncSender(ref s) => s.send((event, self.err_ctx)),
+ SenderType::Sender(ref s) => s.send((event, err_ctx)),
+ SenderType::SyncSender(ref s) => s.send((event, err_ctx)),
}
}
-
- /// Updates this [`SenderWithContext`]'s [`ErrorContext`]. This is the way one adds
- /// a call to the error context.
- ///
- /// Updating [`ErrorContext`]s works in this way so that these contexts are only ever
- /// allocated on the stack (which is thread-specific), and not on the heap.
- pub fn update(&mut self, new_ctx: ErrorContext) {
- self.err_ctx = new_ctx;
- }
}
unsafe impl<T: Clone> Send for SenderWithContext<T> {}
@@ -109,6 +105,12 @@ thread_local!(
static OPENCALLS: RefCell<ErrorContext> = RefCell::default()
);
+task_local! {
+ /// A key to some task local storage that holds a representation of the task's call
+ /// stack in the form of an [`ErrorContext`].
+ static ASYNCOPENCALLS: RefCell<ErrorContext> = RefCell::default()
+}
+
/// Instructions related to the entire application.
#[derive(Clone)]
pub enum AppInstruction {
@@ -140,25 +142,23 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let (send_screen_instructions, receive_screen_instructions): ChannelWithContext<
ScreenInstruction,
> = mpsc::channel();
- let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
- let mut send_screen_instructions =
- SenderWithContext::new(err_ctx, SenderType::Sender(send_screen_instructions));
+ let send_screen_instructions =
+ SenderWithContext::new(SenderType::Sender(send_screen_instructions));
let (send_pty_instructions, receive_pty_instructions): ChannelWithContext<PtyInstruction> =
mpsc::channel();
- let mut send_pty_instructions =
- SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions));
+ let send_pty_instructions = SenderWithContext::new(SenderType::Sender(send_pty_instructions));
let (send_plugin_instructions, receive_plugin_instructions): ChannelWithContext<
PluginInstruction,
> = mpsc::channel();
let send_plugin_instructions =
- SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions));
+ SenderWithContext::new(SenderType::Sender(send_plugin_instructions));
let (send_app_instructions, receive_app_instructions): SyncChannelWithContext<AppInstruction> =
mpsc::sync_channel(0);
let send_app_instructions =
- SenderWithContext::new(err_ctx, SenderType::SyncSender(send_app_instructions));
+ SenderWithContext::new(SenderType::SyncSender(send_app_instructions));
let mut pty_bus = PtyBus::new(
receive_pty_instructions,
@@ -168,12 +168,22 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
opts.debug,
);
+ // Determine and initialize the data directory
+ let project_dirs = ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
+ let data_dir = opts
+ .data_dir
+ .unwrap_or_else(|| project_dirs.data_dir().to_path_buf());
+ populate_data_dir(&data_dir);
+
// Don't use default layouts in tests, but do everywhere else
#[cfg(not(test))]
let default_layout = Some(PathBuf::from("default"));
#[cfg(test)]
let default_layout = None;
- let maybe_layout = opts.layout.or(default_layout).map(Layout::new);
+ let maybe_layout = opts
+ .layout
+ .or(default_layout)
+ .map(|p| Layout::new(&p, &data_dir));
#[cfg(not(test))]
std::panic::set_hook({
@@ -195,7 +205,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
- pty_bus.send_screen_instructions.update(err_ctx);
match event {
PtyInstruction::SpawnTerminal(file_to_open) => {
let pid = pty_bus.spawn_terminal(file_to_open);
@@ -272,14 +281,24 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event)));
- screen.send_app_instructions.update(err_ctx);
- screen.send_pty_instructions.update(err_ctx);
match event {
- ScreenInstruction::Pty(pid, vte_event) => {
- screen
- .get_active_tab_mut()
- .unwrap()
- .handle_pty_event(pid, vte_event);
+ ScreenInstruction::PtyBytes(pid, vte_bytes) => {
+ let active_tab = screen.get_active_tab_mut().unwrap();
+ if active_tab.has_terminal_pid(pid) {
+ // it's most likely that this event is directed at the active tab
+ // look there first
+ active_tab.handle_pty_bytes(pid, vte_bytes);
+ } else {
+ // if this event wasn't directed at the active tab, start looking
+ // in other tabs
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_terminal_pid(pid) {
+ tab.handle_pty_bytes(pid, vte_bytes);
+ break;
+ }
+ }
+ }
}
ScreenInstruction::Render => {
screen.render();
@@ -314,9 +333,15 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
ScreenInstruction::ResizeUp => {
screen.get_active_tab_mut().unwrap().resize_up();
}
- ScreenInstruction::MoveFocus => {
+ ScreenInstruction::SwitchFocus => {
screen.get_active_tab_mut().unwrap().move_focus();
}
+ ScreenInstruction::FocusNextPane => {
+ screen.get_active_tab_mut().unwrap().focus_next_pane();
+ }
+ ScreenInstruction::FocusPreviousPane => {
+ screen.get_active_tab_mut().unwrap().focus_previous_pane();
+ }
ScreenInstruction::MoveFocusLeft => {
screen.get_active_tab_mut().unwrap().move_focus_left();
}
@@ -415,9 +440,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let wasm_thread = thread::Builder::new()
.name("wasm".to_string())
.spawn({
- let mut send_pty_instructions = send_pty_instructions.clone();
- let mut send_screen_instructions = send_screen_instructions.clone();
- let mut send_app_instructions = send_app_instructions.clone();
+ let send_pty_instructions = send_pty_instructions.clone();
+ let send_screen_instructions = send_screen_instructions.clone();
+ let send_app_instructions = send_app_instructions.clone();
let store = Store::default();
let mut plugin_id = 0;
@@ -427,14 +452,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
- send_screen_instructions.update(err_ctx);
- send_pty_instructions.update(err_ctx);
- send_app_instructions.update(err_ctx);
match event {
PluginInstruction::Load(pid_tx, path) => {
- let project_dirs =
- ProjectDirs::from("org", "Zellij Contributors", "Zellij").unwrap();
- let plugin_dir = project_dirs.data_dir().join("plugins/");
+ let plugin_dir = data_dir.join("plugins/");
let wasm_bytes = fs::read(&path)
.or_else(|_| fs::read(&path.with_extension("wasm")))
.or_else(|_| fs::read(&plugin_dir.join(&path).with_extension("wasm")))
@@ -538,16 +558,14 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.name("ipc_server".to_string())
.spawn({
use std::io::Read;
- let mut send_pty_instructions = send_pty_instructions.clone();
- let mut send_screen_instructions = send_screen_instructions.clone();
+ let send_pty_instructions = send_pty_instructions.clone();
+ let send_screen_instructions = send_screen_instructions.clone();
move || {
std::fs::remove_file(ZELLIJ_IPC_PIPE).ok();
let listener = std::os::unix::net::UnixListener::bind(ZELLIJ_IPC_PIPE)
.expect("could not listen on ipc socket");
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
err_ctx.add_call(ContextType::IpcServer);
- send_pty_instructions.update(err_ctx);
- send_screen_instructions.update(err_ctx);
for stream in listener.incoming() {
match stream {
@@ -577,7 +595,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
}
ApiCommand::MoveFocus => {
send_screen_instructions
- .send(ScreenInstruction::MoveFocus)
+ .send(ScreenInstruction::FocusNextPane)
.unwrap();
}
}
@@ -619,8 +637,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.expect("failed to receive app instruction on channel");
err_ctx.add_call(ContextType::App(AppContext::from(&app_instruction)));
- send_screen_instructions.update(err_ctx);
- send_pty_instructions.update(err_ctx);
match app_instruction {
AppInstruction::Exit => {
break;
@@ -634,7 +650,11 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let _ = wasm_thread.join();
os_input.unset_raw_mode(0);
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
- let error = format!("{}\n{}", goto_start_of_last_line, backtrace);
+ let restore_snapshot = "\u{1b}[?1049l";
+ let error = format!(
+ "{}\n{}{}",
+ goto_start_of_last_line, restore_snapshot, backtrace
+ );
let _ = os_input
.get_stdout_writer()
.write(error.as_bytes())
diff --git a/src/common/os_input_output.rs b/src/common/os_input_output.rs
index de0c6a3b..35d77fdd 100644
--- a/src/common/os_input_output.rs
+++ b/src/common/os_input_output.rs
@@ -84,15 +84,10 @@ fn handle_command_exit(mut child: Child) {
}
for signal in signals.pending() {
- // FIXME: We need to handle more signals here!
- #[allow(clippy::single_match)]
- match signal {
- SIGINT => {
- child.kill().unwrap();
- child.wait().unwrap();
- break 'handle_exit;
- }
- _ => {}
+ if let SIGINT = signal {
+ child.kill().unwrap();
+ child.wait().unwrap();
+ break 'handle_exit;
}
}
}
@@ -242,7 +237,13 @@ impl OsApi for OsInputOutput {
Box::new(stdout)
}
fn kill(&mut self, pid: RawFd) -> Result<(), nix::Error> {
- kill(Pid::from_raw(pid), Some(Signal::SIGINT)).unwrap();
+ // TODO:
+ // Ideally, we should be using SIGINT rather than SIGKILL here, but there are cases in which
+ // the terminal we're trying to kill hangs on SIGINT and so all the app gets stuck
+ // that's why we're sending SIGKILL here
+ // A better solution would be to send SIGINT here and not wait for it, and then have
+ // a background thread do the waitpid stuff and send SIGKILL if the process is stuck
+ kill(Pid::from_raw(pid), Some(Signal::SIGKILL)).unwrap();
waitpid(Pid::from_raw(pid), None).unwrap();
Ok(())
}
diff --git a/src/common/pty_bus.rs b/src/common/pty_bus.rs
index 7e854257..2c132228 100644
--- a/src/common/pty_bus.rs
+++ b/src/common/pty_bus.rs
@@ -6,14 +6,13 @@ use ::std::os::unix::io::RawFd;
use ::std::pin::*;
use ::std::sync::mpsc::Receiver;
use ::std::time::{Duration, Instant};
-use ::vte;
use std::path::PathBuf;
-use super::{ScreenInstruction, SenderWithContext, OPENCALLS};
+use super::{ScreenInstruction, SenderWithContext};
use crate::os_input_output::OsApi;
use crate::utils::logging::debug_to_file;
use crate::{
- errors::{ContextType, ErrorContext},
+ errors::{get_current_ctx, ContextType, ErrorContext},
panes::PaneId,
};
use crate::{layout::Layout, wasm_vm::PluginInstruction};
@@ -44,7 +43,7 @@ impl Stream for ReadFromPid {
// indicates end of file
Poll::Ready(None)
} else {
- let res = Some(read_buffer[..=*res].to_vec());
+ let res = Some(read_buffer[..*res].to_vec());
Poll::Ready(res)
}
}
@@ -64,86 +63,7 @@ impl Stream for ReadFromPid {
}
}
-#[derive(Debug, Clone)]
-pub enum VteEvent {
- // TODO: try not to allocate Vecs
- Print(char),
- Execute(u8), // byte
- Hook(Vec<i64>, Vec<u8>, bool, char), // params, intermediates, ignore, char
- Put(u8), // byte
- Unhook,
- OscDispatch(Vec<Vec<u8>>, bool), // params, bell_terminated
- CsiDispatch(Vec<i64>, Vec<u8>, bool, char), // params, intermediates, ignore, char
- EscDispatch(Vec<u8>, bool, u8), // intermediates, ignore, byte
-}
-
-struct VteEventSender {
- id: RawFd,
- sender: SenderWithContext<ScreenInstruction>,
-}
-
-impl VteEventSender {
- pub fn new(id: RawFd, sender: SenderWithContext<ScreenInstruction>) -> Self {
- VteEventSender { id, sender }
- }
-}
-
-impl vte::Perform for VteEventSender {
- fn print(&mut self, c: char) {
- let _ = self
- .sender
- .send(ScreenInstruction::Pty(self.id, VteEvent::Print(c)));
- }
- fn execute(&mut self, byte: u8) {
- let _ = self
- .sender
- .send(ScreenInstruction::Pty(self.id, VteEvent::Execute(byte)));
- }
-
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
- let params = params.iter().copied().collect();
- let intermediates = intermediates.iter().copied().collect();
- let instruction =
- ScreenInstruction::Pty(self.id, VteEvent::Hook(params, intermediates, ignore, c));
- let _ = self.sender.send(instruction);
- }
-
- fn put(&mut self, byte: u8) {
- let _ = self
- .sender
- .send(ScreenInstruction::Pty(self.id, VteEvent::Put(byte)));
- }
-
- fn unhook(&mut self) {
- let _ = self
- .sender
- .send(ScreenInstruction::Pty(self.id, VteEvent::Unhook));
- }
-
- fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
- let params = params.iter().map(|p| p.to_vec()).collect();
- let instruction =
- ScreenInstruction::Pty(self.id, VteEvent::OscDispatch(params, bell_terminated));
- let _ = self.sender.send(instruction);
- }
-
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
- let params = params.iter().copied().collect();
- let intermediates = intermediates.iter().copied().collect();
- let instruction = ScreenInstruction::Pty(
- self.id,
- VteEvent::CsiDispatch(params, intermediates, ignore, c),
- );
- let _ = self.sender.send(instruction);
- }
-
- fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
- let intermediates = intermediates.iter().copied().collect();
- let instruction =
- ScreenInstruction::Pty(self.id, VteEvent::EscDispatch(intermediates, ignore, byte));
- let _ = self.sender.send(instruction);
- }
-}
+pub type VteBytes = Vec<u8>;
/// Instructions related to PTYs (pseudoterminals).
#[derive(Clone, Debug)]
@@ -169,17 +89,14 @@ pub struct PtyBus {
fn stream_terminal_bytes(
pid: RawFd,
- mut send_screen_instructions: SenderWithContext<ScreenInstruction>,
+ send_screen_instructions: SenderWithContext<ScreenInstruction>,
os_input: Box<dyn OsApi>,
debug: bool,
) -> JoinHandle<()> {
- let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
+ let mut err_ctx = get_current_ctx();
task::spawn({
async move {
err_ctx.add_call(ContextType::AsyncTask);
- send_screen_instructions.update(err_ctx);
- let mut vte_parser = vte::Parser::new();
- let mut vte_event_sender = VteEventSender::new(pid, send_screen_instructions.clone());
let mut terminal_bytes = ReadFromPid::new(&pid, os_input);
let mut last_byte_receive_time: Option<Instant> = None;
@@ -188,13 +105,13 @@ fn stream_terminal_bytes(
while let Some(bytes) = terminal_bytes.next().await {
let bytes_is_empty = bytes.is_empty();
- for byte in bytes {
- if debug {
- debug_to_file(byte, pid).unwrap();
+ if debug {
+ for byte in bytes.iter() {
+ debug_to_file(*byte, pid).unwrap();
}
- vte_parser.advance(&mut vte_event_sender, byte);
}
if !bytes_is_empty {
+ let _ = send_screen_instructions.send(ScreenInstruction::PtyBytes(pid, bytes));
// for UX reasons, if we got something on the wire, we only send the render notice if:
// 1. there aren't any more bytes on the wire afterwards
// 2. a certain period (currently 30ms) has elapsed since the last render
diff --git a/src/common/screen.rs b/src/common/screen.rs
index 121f21bd..01ac04fc 100644
--- a/src/common/screen.rs
+++ b/src/common/screen.rs
@@ -8,7 +8,7 @@ use std::sync::mpsc::Receiver;
use super::{AppInstruction, SenderWithContext};
use crate::os_input_output::OsApi;
use crate::panes::PositionAndSize;
-use crate::pty_bus::{PtyInstruction, VteEvent};
+use crate::pty_bus::{PtyInstruction, VteBytes};
use crate::tab::Tab;
use crate::{errors::ErrorContext, wasm_vm::PluginInstruction};
use crate::{layout::Layout, panes::PaneId};
@@ -18,7 +18,7 @@ use zellij_tile::data::{Event, ModeInfo, TabInfo};
/// Instructions that can be sent to the [`Screen`].
#[derive(Debug, Clone)]
pub enum ScreenInstruction {
- Pty(RawFd, VteEvent),
+ PtyBytes(RawFd, VteBytes),
Render,
NewPane(PaneId),
HorizontalSplit(PaneId),
@@ -28,7 +28,9 @@ pub enum ScreenInstruction {
ResizeRight,
ResizeDown,
ResizeUp,
- MoveFocus,
+ SwitchFocus,
+ FocusNextPane,
+ FocusPreviousPane,
MoveFocusLeft,
MoveFocusDown,
MoveFocusUp,
@@ -79,7 +81,9 @@ pub struct Screen {
}
impl Screen {
+ // FIXME: This lint needs actual fixing! Maybe by bundling the Senders
/// Creates and returns a new [`Screen`].
+ #[allow(clippy::too_many_arguments)]
pub fn new(
receive_screen_instructions: Receiver<(ScreenInstruction, ErrorContext)>,
send_pty_instructions: SenderWithContext<PtyInstruction>,
diff --git a/src/common/utils/logging.rs b/src/common/utils/logging.rs
index d08639aa..06827bfc 100644
--- a/src/common/utils/logging.rs
+++ b/src/common/utils/logging.rs
@@ -47,8 +47,7 @@ pub fn _debug_log_to_file_pid_3(message: String, pid: RawFd) -> io::Result<()> {
}
}
-#[allow(dead_code)]
-pub fn delete_log_file() -> io::Result<()> {
+pub fn _delete_log_file() -> io::Result<()> {
if fs::metadata(ZELLIJ_TMP_LOG_FILE).is_ok() {
fs::remove_file(ZELLIJ_TMP_LOG_FILE)
} else {
@@ -56,8 +55,7 @@ pub fn delete_log_file() -> io::Result<()> {
}
}
-#[allow(dead_code)]
-pub fn delete_log_dir() -> io::Result<()> {
+pub fn _delete_log_dir() -> io::Result<()> {
if fs::metadata(ZELLIJ_TMP_LOG_DIR).is_ok() {
fs::remove_dir_all(ZELLIJ_TMP_LOG_DIR)
} else {
diff --git a/src/common/utils/shared.rs b/src/common/utils/shared.rs
index 93892d4f..d5ba943b 100644
--- a/src/common/utils/shared.rs
+++ b/src/common/utils/shared.rs
@@ -18,9 +18,9 @@ pub fn adjust_to_size(s: &str, rows: usize, columns: usize) -> String {
if actual_len > columns {
let mut line = String::from(l);
line.truncate(columns);
- return line;
+ line
} else {
- return [l, &str::repeat(" ", columns - ansi_len(l))].concat();
+ [l, &str::repeat(" ", columns - ansi_len(l))].concat()
}
})
.chain(iter::repeat(str::repeat(" ", columns)))