Move most key handling to the update() + event system

This commit is contained in:
Brooks J Rady 2021-03-23 23:57:18 +00:00
parent ac55e59047
commit 23df8e447a
8 changed files with 82 additions and 126 deletions

View File

@ -10,6 +10,41 @@ register_tile!(State);
impl ZellijTile for State {
fn load(&mut 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) {
@ -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) {

View File

@ -1,11 +1,11 @@
//! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size,
//! 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::panes::{PaneId, PositionAndSize, TerminalPane};
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::{os_input_output::OsApi, utils::shared::pad_to_size};
use std::os::unix::io::RawFd;
@ -14,6 +14,7 @@ use std::{
collections::{BTreeMap, HashSet},
};
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 MIN_TERMINAL_HEIGHT: usize = 2;
@ -553,12 +554,11 @@ impl Tab {
.expect("failed to drain terminal");
}
Some(PaneId::Plugin(pid)) => {
self.send_plugin_instructions
.send(PluginInstruction::Input(
PluginInputType::Normal(pid),
input_bytes,
))
.unwrap();
for key in parse_keys(&input_bytes) {
self.send_plugin_instructions
.send(PluginInstruction::Update(Some(pid), Event::KeyPress(key)))
.unwrap()
}
}
_ => {}
}

View File

@ -281,7 +281,6 @@ pub enum PluginContext {
Update,
Render,
Input,
GlobalInput,
Unload,
Quit,
Tabs,
@ -291,10 +290,9 @@ impl From<&PluginInstruction> for PluginContext {
fn from(plugin_instruction: &PluginInstruction) -> Self {
match *plugin_instruction {
PluginInstruction::Load(..) => PluginContext::Load,
PluginInstruction::Update(_) => PluginContext::Update,
PluginInstruction::Update(..) => PluginContext::Update,
PluginInstruction::Render(..) => PluginContext::Render,
PluginInstruction::Input(..) => PluginContext::Input,
PluginInstruction::GlobalInput(_) => PluginContext::GlobalInput,
PluginInstruction::Unload(_) => PluginContext::Unload,
PluginInstruction::Quit => PluginContext::Quit,
PluginInstruction::UpdateTabs(..) => PluginContext::Tabs,

View File

@ -10,8 +10,8 @@ use crate::screen::ScreenInstruction;
use crate::wasm_vm::{NaughtyEventType, PluginInputType, PluginInstruction};
use crate::CommandIsExecuting;
use termion::input::TermReadEventsAndRaw;
use zellij_tile::data::{Event, Help, InputMode, Key};
use termion::input::{TermRead, TermReadEventsAndRaw};
use zellij_tile::data::{Help, InputMode, Key};
use super::keybinds::key_to_actions;
@ -61,19 +61,15 @@ impl InputHandler {
'input_loop: loop {
//@@@ I think this should actually just iterate over stdin directly
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() {
match key_result {
Ok((event, raw_bytes)) => match event {
termion::event::Event::Key(key) => {
let key = cast_termion_key(key);
// FIXME: This is a bit of a hack to get resizing to work!
drop(
self.send_plugin_instructions
.send(PluginInstruction::Update(Event::KeyPress(key))),
self.send_screen_instructions
.send(ScreenInstruction::Render),
);
// FIXME this explicit break is needed because the current test
// framework relies on it to not create dead threads that loop
@ -327,6 +323,10 @@ pub fn input_loop(
.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
// as an alternative that doesn't touch zellij-tile can be developed...
fn cast_termion_key(event: termion::event::Key) -> Key {

View File

@ -17,6 +17,7 @@ use std::{collections::HashMap, fs};
use std::{
collections::HashSet,
io::Write,
str::FromStr,
sync::{Arc, Mutex},
};
@ -40,7 +41,7 @@ use wasm_vm::{
};
use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
use wasmer_wasi::{Pipe, WasiState};
use zellij_tile::data::{InputMode, Key};
use zellij_tile::data::{EventType, InputMode, Key};
#[derive(Serialize, Deserialize, Debug)]
pub enum ApiCommand {
@ -549,16 +550,21 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
pid_tx.send(plugin_id).unwrap();
plugin_id += 1;
}
PluginInstruction::Update(event) => {
for (instance, plugin_env) in plugin_map.values() {
let update = instance.exports.get_function("update").unwrap();
wasi_write_string(
&plugin_env.wasi_env,
&serde_json::to_string(&event).unwrap(),
);
update.call(&[]).unwrap();
PluginInstruction::Update(pid, event) => {
for (&i, (instance, plugin_env)) in &plugin_map {
let subs = plugin_env.subscriptions.lock().unwrap();
// FIXME: This is very janky... Maybe I should write my own macro for Event -> EventType?
let event_type = EventType::from_str(&event.to_string()).unwrap();
if pid.is_none() || pid == Some(i) && subs.contains(&event_type) {
let update = instance.exports.get_function("update").unwrap();
wasi_write_string(
&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) => {
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!
PluginInstruction::Input(input_type, input_bytes) => {
match input_type {
PluginInputType::Normal(pid) => {
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
let handle_key =
instance.exports.get_function("handle_key").unwrap();
if let PluginInputType::Event(event) = input_type {
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(
@ -601,45 +611,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
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));
}
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::Quit => break,
}

View File

@ -30,10 +30,9 @@ pub enum PluginInputType {
#[derive(Clone, Debug)]
pub enum PluginInstruction {
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
Input(PluginInputType, Vec<u8>), // plugin id, input bytes
GlobalInput(Vec<u8>), // input bytes
Input(PluginInputType, Vec<u8>), // plugin id, input bytes
Unload(u32),
UpdateTabs(Vec<TabInfo>), // num tabs, active tab
Quit,

View File

@ -1,5 +1,5 @@
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)]
pub enum Key {
@ -24,7 +24,7 @@ pub enum Key {
}
#[derive(Debug, Clone, EnumDiscriminants, ToString, Serialize, Deserialize)]
#[strum_discriminants(derive(Hash, Serialize, Deserialize))]
#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
#[strum_discriminants(name(EventType))]
pub enum Event {
ModeUpdate(Help), // FIXME: Rename the `Help` struct

View File

@ -10,8 +10,6 @@ pub trait ZellijTile {
fn update(&mut self, event: Event) {}
fn render(&mut self, rows: usize, cols: usize) {}
// 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 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]
pub fn update_tabs() {
STATE.with(|state| {