zellij/zellij-client/src/stdin_handler.rs
Aram Drevekenin c89b416d76
feat(terminal): sixel support (#1557)
* work

* work

* work

* work

* work

* more work

* work

* work

* work

* hack around stdin repeater

* refactor(sixel): rename sixel structs

* feat(sixel): render text above images

* fix(sixel): reap images once they're past the end of the scrollbuffer

* fix(sixel): display images in the middle of the line

* fix(sixel): render crash

* fix(sixel): react to SIGWINCH

* fix(sixel): behave properly in alternate screen mode

* fix(sixel): reap images on terminal reset

* feat(sixel): handle DECSDM

* fix(terminal): properly respond to XTSMGRAPHICS and device attributes with Sixel

* Add comment

* fix(sixel): hack for unknown event overflow until we fix the api

* feat(input): query terminal for all OSC 4 colors and respond to them in a buggy way

* fix(sixel): do not render corrupted image

* feat(input): improve STDIN queries

* fix(client): mistake in clear terminal attributes string

* fix(ansi): report correct number of supported color registers

* fix(sixel): reap images that are completely covered

* style(comment): fix name

* test(sixel): infra

* test(sixel): cases and fixes

* fix(sixel): forward dcs bytes to sixel parser

* refactor(client): ansi stdin parser

* refactor(output): cleanup

* some refactorings

* fix test

* refactor(grid): sixel-grid / sixel-image-store

* refactor(grid): grid debug method

* refactor(grid): move various logic to sixel.rs

* refactor(grid): remove unused methods

* fix(sixel): work with multiple users

* refactor(pane): remove unused z_index

* style(fmt): prepend unused variable

* style(fmt): rustfmt

* fix(tests): various apis

* chore(dependencies): use published version of sixel crates

* style(fmt): rustfmt

* style(fmt): rustfmt

* style(lint): make clippy happy

* style(lint): make clippy happy... again

* style(lint): make clippy happy... again (chapter 2)

* style(comment): remove unused

* fix(colors): export COLORTERM and respond to XTVERSION

* fix(test): color register count

* fix(stdin): adjust STDIN sleep times
2022-07-08 17:19:42 +02:00

93 lines
3.4 KiB
Rust

use crate::os_input_output::ClientOsApi;
use crate::stdin_ansi_parser::StdinAnsiParser;
use crate::InputInstruction;
use std::sync::{Arc, Mutex};
use zellij_utils::channels::SenderWithContext;
use zellij_utils::termwiz::input::{InputEvent, InputParser, MouseButtons};
pub(crate) fn stdin_loop(
mut os_input: Box<dyn ClientOsApi>,
send_input_instructions: SenderWithContext<InputInstruction>,
stdin_ansi_parser: Arc<Mutex<StdinAnsiParser>>,
) {
let mut holding_mouse = false;
let mut input_parser = InputParser::new();
let mut current_buffer = vec![];
// on startup we send a query to the terminal emulator for stuff like the pixel size and colors
// we get a response through STDIN, so it makes sense to do this here
let terminal_emulator_query_string = stdin_ansi_parser
.lock()
.unwrap()
.terminal_emulator_query_string();
let _ = os_input
.get_stdout_writer()
.write(terminal_emulator_query_string.as_bytes())
.unwrap();
loop {
let buf = os_input.read_from_stdin();
{
// here we check if we need to parse specialized ANSI instructions sent over STDIN
// this happens either on startup (see above) or on SIGWINCH
//
// if we need to parse them, we do so with an internal timeout - anything else we
// receive on STDIN during that timeout is unceremoniously dropped
let mut stdin_ansi_parser = stdin_ansi_parser.lock().unwrap();
if stdin_ansi_parser.should_parse() {
let events = stdin_ansi_parser.parse(buf);
if !events.is_empty() {
let _ = send_input_instructions
.send(InputInstruction::AnsiStdinInstructions(events));
}
continue;
}
}
current_buffer.append(&mut buf.to_vec());
let maybe_more = false; // read_from_stdin should (hopefully) always empty the STDIN buffer completely
let mut events = vec![];
input_parser.parse(
&buf,
|input_event: InputEvent| {
events.push(input_event);
},
maybe_more,
);
let event_count = events.len();
for (i, input_event) in events.into_iter().enumerate() {
if holding_mouse && is_mouse_press_or_hold(&input_event) && i == event_count - 1 {
let mut poller = os_input.stdin_poller();
loop {
if poller.ready() {
break;
}
send_input_instructions
.send(InputInstruction::KeyEvent(
input_event.clone(),
current_buffer.clone(),
))
.unwrap();
}
}
holding_mouse = is_mouse_press_or_hold(&input_event);
send_input_instructions
.send(InputInstruction::KeyEvent(
input_event,
current_buffer.drain(..).collect(),
))
.unwrap();
}
}
}
fn is_mouse_press_or_hold(input_event: &InputEvent) -> bool {
if let InputEvent::Mouse(mouse_event) = input_event {
if mouse_event.mouse_buttons.contains(MouseButtons::LEFT)
|| mouse_event.mouse_buttons.contains(MouseButtons::RIGHT)
{
return true;
}
}
false
}