Move most key handling to the update() + event system
This commit is contained in:
parent
ac55e59047
commit
23df8e447a
8 changed files with 82 additions and 126 deletions
|
|
@ -10,6 +10,41 @@ register_tile!(State);
|
||||||
impl ZellijTile for State {
|
impl ZellijTile for State {
|
||||||
fn load(&mut self) {
|
fn load(&mut self) {
|
||||||
refresh_directory(self);
|
refresh_directory(self);
|
||||||
|
subscribe(&[EventType::KeyPress]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, event: Event) {
|
||||||
|
if let Event::KeyPress(key) = event {
|
||||||
|
match key {
|
||||||
|
Key::Up | Key::Char('k') => {
|
||||||
|
*self.selected_mut() = self.selected().saturating_sub(1);
|
||||||
|
}
|
||||||
|
Key::Down | Key::Char('j') => {
|
||||||
|
let next = self.selected().saturating_add(1);
|
||||||
|
*self.selected_mut() = min(self.files.len() - 1, next);
|
||||||
|
}
|
||||||
|
Key::Right | Key::Char('\n') | Key::Char('l') => {
|
||||||
|
match self.files[self.selected()].clone() {
|
||||||
|
FsEntry::Dir(p, _) => {
|
||||||
|
self.path = p;
|
||||||
|
refresh_directory(self);
|
||||||
|
}
|
||||||
|
FsEntry::File(p, _) => open_file(&p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::Left | Key::Char('h') => {
|
||||||
|
self.path.pop();
|
||||||
|
refresh_directory(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::Char('.') => {
|
||||||
|
self.toggle_hidden_files();
|
||||||
|
refresh_directory(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, rows: usize, cols: usize) {
|
fn render(&mut self, rows: usize, cols: usize) {
|
||||||
|
|
@ -38,38 +73,6 @@ impl ZellijTile for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key(&mut self, key: Key) {
|
|
||||||
match key {
|
|
||||||
Key::Up | Key::Char('k') => {
|
|
||||||
*self.selected_mut() = self.selected().saturating_sub(1);
|
|
||||||
}
|
|
||||||
Key::Down | Key::Char('j') => {
|
|
||||||
let next = self.selected().saturating_add(1);
|
|
||||||
*self.selected_mut() = min(self.files.len() - 1, next);
|
|
||||||
}
|
|
||||||
Key::Right | Key::Char('\n') | Key::Char('l') => {
|
|
||||||
match self.files[self.selected()].clone() {
|
|
||||||
FsEntry::Dir(p, _) => {
|
|
||||||
self.path = p;
|
|
||||||
refresh_directory(self);
|
|
||||||
}
|
|
||||||
FsEntry::File(p, _) => open_file(&p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::Left | Key::Char('h') => {
|
|
||||||
self.path.pop();
|
|
||||||
refresh_directory(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
Key::Char('.') => {
|
|
||||||
self.toggle_hidden_files();
|
|
||||||
refresh_directory(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_directory(state: &mut State) {
|
fn refresh_directory(state: &mut State) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
//! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size,
|
//! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size,
|
||||||
//! as well as how they should be resized
|
//! as well as how they should be resized
|
||||||
|
|
||||||
use crate::common::{AppInstruction, SenderWithContext};
|
use crate::common::{input::handler::parse_keys, AppInstruction, SenderWithContext};
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
use crate::panes::{PaneId, PositionAndSize, TerminalPane};
|
use crate::panes::{PaneId, PositionAndSize, TerminalPane};
|
||||||
use crate::pty_bus::{PtyInstruction, VteEvent};
|
use crate::pty_bus::{PtyInstruction, VteEvent};
|
||||||
use crate::wasm_vm::{PluginInputType, PluginInstruction};
|
use crate::wasm_vm::PluginInstruction;
|
||||||
use crate::{boundaries::Boundaries, panes::PluginPane};
|
use crate::{boundaries::Boundaries, panes::PluginPane};
|
||||||
use crate::{os_input_output::OsApi, utils::shared::pad_to_size};
|
use crate::{os_input_output::OsApi, utils::shared::pad_to_size};
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
|
|
@ -14,6 +14,7 @@ use std::{
|
||||||
collections::{BTreeMap, HashSet},
|
collections::{BTreeMap, HashSet},
|
||||||
};
|
};
|
||||||
use std::{io::Write, sync::mpsc::channel};
|
use std::{io::Write, sync::mpsc::channel};
|
||||||
|
use zellij_tile::data::Event;
|
||||||
|
|
||||||
const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
|
const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this
|
||||||
const MIN_TERMINAL_HEIGHT: usize = 2;
|
const MIN_TERMINAL_HEIGHT: usize = 2;
|
||||||
|
|
@ -553,12 +554,11 @@ impl Tab {
|
||||||
.expect("failed to drain terminal");
|
.expect("failed to drain terminal");
|
||||||
}
|
}
|
||||||
Some(PaneId::Plugin(pid)) => {
|
Some(PaneId::Plugin(pid)) => {
|
||||||
self.send_plugin_instructions
|
for key in parse_keys(&input_bytes) {
|
||||||
.send(PluginInstruction::Input(
|
self.send_plugin_instructions
|
||||||
PluginInputType::Normal(pid),
|
.send(PluginInstruction::Update(Some(pid), Event::KeyPress(key)))
|
||||||
input_bytes,
|
.unwrap()
|
||||||
))
|
}
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,6 @@ pub enum PluginContext {
|
||||||
Update,
|
Update,
|
||||||
Render,
|
Render,
|
||||||
Input,
|
Input,
|
||||||
GlobalInput,
|
|
||||||
Unload,
|
Unload,
|
||||||
Quit,
|
Quit,
|
||||||
Tabs,
|
Tabs,
|
||||||
|
|
@ -291,10 +290,9 @@ impl From<&PluginInstruction> for PluginContext {
|
||||||
fn from(plugin_instruction: &PluginInstruction) -> Self {
|
fn from(plugin_instruction: &PluginInstruction) -> Self {
|
||||||
match *plugin_instruction {
|
match *plugin_instruction {
|
||||||
PluginInstruction::Load(..) => PluginContext::Load,
|
PluginInstruction::Load(..) => PluginContext::Load,
|
||||||
PluginInstruction::Update(_) => PluginContext::Update,
|
PluginInstruction::Update(..) => PluginContext::Update,
|
||||||
PluginInstruction::Render(..) => PluginContext::Render,
|
PluginInstruction::Render(..) => PluginContext::Render,
|
||||||
PluginInstruction::Input(..) => PluginContext::Input,
|
PluginInstruction::Input(..) => PluginContext::Input,
|
||||||
PluginInstruction::GlobalInput(_) => PluginContext::GlobalInput,
|
|
||||||
PluginInstruction::Unload(_) => PluginContext::Unload,
|
PluginInstruction::Unload(_) => PluginContext::Unload,
|
||||||
PluginInstruction::Quit => PluginContext::Quit,
|
PluginInstruction::Quit => PluginContext::Quit,
|
||||||
PluginInstruction::UpdateTabs(..) => PluginContext::Tabs,
|
PluginInstruction::UpdateTabs(..) => PluginContext::Tabs,
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use crate::screen::ScreenInstruction;
|
||||||
use crate::wasm_vm::{NaughtyEventType, PluginInputType, PluginInstruction};
|
use crate::wasm_vm::{NaughtyEventType, PluginInputType, PluginInstruction};
|
||||||
use crate::CommandIsExecuting;
|
use crate::CommandIsExecuting;
|
||||||
|
|
||||||
use termion::input::TermReadEventsAndRaw;
|
use termion::input::{TermRead, TermReadEventsAndRaw};
|
||||||
use zellij_tile::data::{Event, Help, InputMode, Key};
|
use zellij_tile::data::{Help, InputMode, Key};
|
||||||
|
|
||||||
use super::keybinds::key_to_actions;
|
use super::keybinds::key_to_actions;
|
||||||
|
|
||||||
|
|
@ -61,19 +61,15 @@ impl InputHandler {
|
||||||
'input_loop: loop {
|
'input_loop: loop {
|
||||||
//@@@ I think this should actually just iterate over stdin directly
|
//@@@ I think this should actually just iterate over stdin directly
|
||||||
let stdin_buffer = self.os_input.read_from_stdin();
|
let stdin_buffer = self.os_input.read_from_stdin();
|
||||||
// FIXME: Kill me someday (soon)
|
|
||||||
drop(
|
|
||||||
self.send_plugin_instructions
|
|
||||||
.send(PluginInstruction::GlobalInput(stdin_buffer.clone())),
|
|
||||||
);
|
|
||||||
for key_result in stdin_buffer.events_and_raw() {
|
for key_result in stdin_buffer.events_and_raw() {
|
||||||
match key_result {
|
match key_result {
|
||||||
Ok((event, raw_bytes)) => match event {
|
Ok((event, raw_bytes)) => match event {
|
||||||
termion::event::Event::Key(key) => {
|
termion::event::Event::Key(key) => {
|
||||||
let key = cast_termion_key(key);
|
let key = cast_termion_key(key);
|
||||||
|
// FIXME: This is a bit of a hack to get resizing to work!
|
||||||
drop(
|
drop(
|
||||||
self.send_plugin_instructions
|
self.send_screen_instructions
|
||||||
.send(PluginInstruction::Update(Event::KeyPress(key))),
|
.send(ScreenInstruction::Render),
|
||||||
);
|
);
|
||||||
// FIXME this explicit break is needed because the current test
|
// FIXME this explicit break is needed because the current test
|
||||||
// framework relies on it to not create dead threads that loop
|
// framework relies on it to not create dead threads that loop
|
||||||
|
|
@ -327,6 +323,10 @@ pub fn input_loop(
|
||||||
.handle_input();
|
.handle_input();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> {
|
||||||
|
input_bytes.keys().flatten().map(cast_termion_key).collect()
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This is an absolutely cursed function that should be destroyed as soon
|
// FIXME: This is an absolutely cursed function that should be destroyed as soon
|
||||||
// as an alternative that doesn't touch zellij-tile can be developed...
|
// as an alternative that doesn't touch zellij-tile can be developed...
|
||||||
fn cast_termion_key(event: termion::event::Key) -> Key {
|
fn cast_termion_key(event: termion::event::Key) -> Key {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use std::{collections::HashMap, fs};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
io::Write,
|
io::Write,
|
||||||
|
str::FromStr,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ use wasm_vm::{
|
||||||
};
|
};
|
||||||
use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
|
use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
|
||||||
use wasmer_wasi::{Pipe, WasiState};
|
use wasmer_wasi::{Pipe, WasiState};
|
||||||
use zellij_tile::data::{InputMode, Key};
|
use zellij_tile::data::{EventType, InputMode, Key};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum ApiCommand {
|
pub enum ApiCommand {
|
||||||
|
|
@ -549,16 +550,21 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
pid_tx.send(plugin_id).unwrap();
|
pid_tx.send(plugin_id).unwrap();
|
||||||
plugin_id += 1;
|
plugin_id += 1;
|
||||||
}
|
}
|
||||||
PluginInstruction::Update(event) => {
|
PluginInstruction::Update(pid, event) => {
|
||||||
for (instance, plugin_env) in plugin_map.values() {
|
for (&i, (instance, plugin_env)) in &plugin_map {
|
||||||
let update = instance.exports.get_function("update").unwrap();
|
let subs = plugin_env.subscriptions.lock().unwrap();
|
||||||
|
// FIXME: This is very janky... Maybe I should write my own macro for Event -> EventType?
|
||||||
wasi_write_string(
|
let event_type = EventType::from_str(&event.to_string()).unwrap();
|
||||||
&plugin_env.wasi_env,
|
if pid.is_none() || pid == Some(i) && subs.contains(&event_type) {
|
||||||
&serde_json::to_string(&event).unwrap(),
|
let update = instance.exports.get_function("update").unwrap();
|
||||||
);
|
wasi_write_string(
|
||||||
update.call(&[]).unwrap();
|
&plugin_env.wasi_env,
|
||||||
|
&serde_json::to_string(&event).unwrap(),
|
||||||
|
);
|
||||||
|
update.call(&[]).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||||
}
|
}
|
||||||
PluginInstruction::Render(buf_tx, pid, rows, cols) => {
|
PluginInstruction::Render(buf_tx, pid, rows, cols) => {
|
||||||
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
||||||
|
|
@ -587,11 +593,15 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
}
|
}
|
||||||
// FIXME: Deduplicate this with the callback below!
|
// FIXME: Deduplicate this with the callback below!
|
||||||
PluginInstruction::Input(input_type, input_bytes) => {
|
PluginInstruction::Input(input_type, input_bytes) => {
|
||||||
match input_type {
|
if let PluginInputType::Event(event) = input_type {
|
||||||
PluginInputType::Normal(pid) => {
|
for (instance, plugin_env) in plugin_map.values() {
|
||||||
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
if !plugin_env.events.contains(&event) {
|
||||||
let handle_key =
|
continue;
|
||||||
instance.exports.get_function("handle_key").unwrap();
|
}
|
||||||
|
let handle_key = instance
|
||||||
|
.exports
|
||||||
|
.get_function(handler_map.get(&event).unwrap())
|
||||||
|
.unwrap();
|
||||||
for key in input_bytes.keys().flatten() {
|
for key in input_bytes.keys().flatten() {
|
||||||
let key = cast_termion_key(key);
|
let key = cast_termion_key(key);
|
||||||
wasi_write_string(
|
wasi_write_string(
|
||||||
|
|
@ -601,45 +611,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
handle_key.call(&[]).unwrap();
|
handle_key.call(&[]).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PluginInputType::Event(event) => {
|
|
||||||
for (instance, plugin_env) in plugin_map.values() {
|
|
||||||
if !plugin_env.events.contains(&event) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let handle_key = instance
|
|
||||||
.exports
|
|
||||||
.get_function(handler_map.get(&event).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
for key in input_bytes.keys().flatten() {
|
|
||||||
let key = cast_termion_key(key);
|
|
||||||
wasi_write_string(
|
|
||||||
&plugin_env.wasi_env,
|
|
||||||
&serde_json::to_string(&key).unwrap(),
|
|
||||||
);
|
|
||||||
handle_key.call(&[]).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||||
}
|
}
|
||||||
PluginInstruction::GlobalInput(input_bytes) => {
|
|
||||||
// FIXME: Set up an event subscription system, and timed callbacks
|
|
||||||
for (instance, plugin_env) in plugin_map.values() {
|
|
||||||
let handler =
|
|
||||||
instance.exports.get_function("handle_global_key").unwrap();
|
|
||||||
for key in input_bytes.keys().flatten() {
|
|
||||||
let key = cast_termion_key(key);
|
|
||||||
wasi_write_string(
|
|
||||||
&plugin_env.wasi_env,
|
|
||||||
&serde_json::to_string(&key).unwrap(),
|
|
||||||
);
|
|
||||||
handler.call(&[]).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
|
||||||
}
|
|
||||||
PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
|
PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
|
||||||
PluginInstruction::Quit => break,
|
PluginInstruction::Quit => break,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,9 @@ pub enum PluginInputType {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PluginInstruction {
|
pub enum PluginInstruction {
|
||||||
Load(Sender<u32>, PathBuf, Vec<NaughtyEventType>),
|
Load(Sender<u32>, PathBuf, Vec<NaughtyEventType>),
|
||||||
Update(Event),
|
Update(Option<u32>, Event), // Focused plugin / broadcast, event data
|
||||||
Render(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
|
Render(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
|
||||||
Input(PluginInputType, Vec<u8>), // plugin id, input bytes
|
Input(PluginInputType, Vec<u8>), // plugin id, input bytes
|
||||||
GlobalInput(Vec<u8>), // input bytes
|
|
||||||
Unload(u32),
|
Unload(u32),
|
||||||
UpdateTabs(Vec<TabInfo>), // num tabs, active tab
|
UpdateTabs(Vec<TabInfo>), // num tabs, active tab
|
||||||
Quit,
|
Quit,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum_macros::{EnumDiscriminants, EnumIter, ToString};
|
use strum_macros::{EnumDiscriminants, EnumIter, EnumString, ToString};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum Key {
|
pub enum Key {
|
||||||
|
|
@ -24,7 +24,7 @@ pub enum Key {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, EnumDiscriminants, ToString, Serialize, Deserialize)]
|
#[derive(Debug, Clone, EnumDiscriminants, ToString, Serialize, Deserialize)]
|
||||||
#[strum_discriminants(derive(Hash, Serialize, Deserialize))]
|
#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
|
||||||
#[strum_discriminants(name(EventType))]
|
#[strum_discriminants(name(EventType))]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
ModeUpdate(Help), // FIXME: Rename the `Help` struct
|
ModeUpdate(Help), // FIXME: Rename the `Help` struct
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ pub trait ZellijTile {
|
||||||
fn update(&mut self, event: Event) {}
|
fn update(&mut self, event: Event) {}
|
||||||
fn render(&mut self, rows: usize, cols: usize) {}
|
fn render(&mut self, rows: usize, cols: usize) {}
|
||||||
// FIXME: Everything below this line should be purged
|
// FIXME: Everything below this line should be purged
|
||||||
fn handle_key(&mut self, key: Key) {}
|
|
||||||
fn handle_global_key(&mut self, key: Key) {}
|
|
||||||
fn update_tabs(&mut self) {}
|
fn update_tabs(&mut self) {}
|
||||||
fn handle_tab_rename_keypress(&mut self, key: Key) {}
|
fn handle_tab_rename_keypress(&mut self, key: Key) {}
|
||||||
}
|
}
|
||||||
|
|
@ -45,22 +43,6 @@ macro_rules! register_tile {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn handle_key() {
|
|
||||||
STATE.with(|state| {
|
|
||||||
state.borrow_mut().handle_key($crate::shim::get_key());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn handle_global_key() {
|
|
||||||
STATE.with(|state| {
|
|
||||||
state
|
|
||||||
.borrow_mut()
|
|
||||||
.handle_global_key($crate::shim::get_key());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn update_tabs() {
|
pub fn update_tabs() {
|
||||||
STATE.with(|state| {
|
STATE.with(|state| {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue