summaryrefslogtreecommitdiffstats
path: root/zellij-client/src/stdin_handler.rs
blob: 2cd307ea7d136a980032ebb65eacb2aebd148904 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use crate::os_input_output::ClientOsApi;
use crate::InputInstruction;
use std::collections::HashMap;
use terminfo::{capability as cap, Database as TerminfoDatabase};
use termion::input::TermReadEventsAndRaw;
use zellij_utils::channels::SenderWithContext;
use zellij_utils::input::mouse::MouseEvent;
use zellij_utils::termion;

fn keys_to_adjust() -> HashMap<Vec<u8>, Vec<u8>> {
    let mut keys_to_adjust = HashMap::new();
    if let Ok(terminfo_db) = TerminfoDatabase::from_env() {
        // TODO: there might be more adjustments we can do here, but I held off on them because I'm
        // not sure they're a thing in these modern times. It should be pretty straightforward to
        // implement them if they are...
        if let Some(adjusted_home_key) = terminfo_db
            .get::<cap::KeyHome>()
            .and_then(|k| k.expand().to_vec().ok())
        {
            keys_to_adjust.insert(vec![27, 91, 72], adjusted_home_key);
        }
        if let Some(adjusted_end_key) = terminfo_db
            .get::<cap::KeyEnd>()
            .and_then(|k| k.expand().to_vec().ok())
        {
            keys_to_adjust.insert(vec![27, 91, 70], adjusted_end_key);
        }
    }
    keys_to_adjust
}

fn bracketed_paste_end_position(stdin_buffer: &[u8]) -> Option<usize> {
    let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201~
    let mut bp_position = 0;
    let mut position = None;
    for (i, byte) in stdin_buffer.iter().enumerate() {
        if Some(byte) == bracketed_paste_end.get(bp_position) {
            position = Some(i);
            bp_position += 1;
            if bp_position == bracketed_paste_end.len() {
                break;
            }
        } else {
            bp_position = 0;
            position = None;
        }
    }
    if bp_position == bracketed_paste_end.len() {
        position
    } else {
        None
    }
}

pub(crate) fn stdin_loop(
    os_input: Box<dyn ClientOsApi>,
    send_input_instructions: SenderWithContext<InputInstruction>,
) {
    let mut pasting = false;
    let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~
    let adjusted_keys = keys_to_adjust();
    loop {
        let mut stdin_buffer = os_input.read_from_stdin();
        if pasting
            || (stdin_buffer.len() > bracketed_paste_start.len()
                && stdin_buffer
                    .iter()
                    .take(bracketed_paste_start.len())
                    .eq(bracketed_paste_start.iter()))
        {
            match bracketed_paste_end_position(&stdin_buffer) {
                Some(paste_end_position) => {
                    let starts_with_bracketed_paste_start = stdin_buffer
                        .iter()
                        .take(bracketed_paste_start.len())
                        .eq(bracketed_paste_start.iter());

                    let ends_with_bracketed_paste_end = true;

                    let mut pasted_input: Vec<u8> =
                        stdin_buffer.drain(..=paste_end_position).collect();
                    if starts_with_bracketed_paste_start {
                        drop(pasted_input.drain(..6)); // bracketed paste start
                    }
                    drop(pasted_input.drain(pasted_input.len() - 6..)); // bracketed paste end

                    send_input_instructions
                        .send(InputInstruction::PastedText((
                            starts_with_bracketed_paste_start,
                            pasted_input,
                            ends_with_bracketed_paste_end,
                        )))
                        .unwrap();
                    pasting = false;
                }
                None => {
                    send_input_instructions
                        .send(InputInstruction::PastedText((true, stdin_buffer, false)))
                        .unwrap();
                    pasting = true;
                    continue;
                }
            }
        }
        if stdin_buffer.is_empty() {
            continue;
        }
        for key_result in stdin_buffer.events_and_raw() {
            let (key_event, raw_bytes) = key_result.unwrap();
            let raw_bytes = adjusted_keys.get(&raw_bytes).cloned().unwrap_or(raw_bytes);
            if let termion::event::Event::Mouse(me) = key_event {
                let mouse_event = zellij_utils::input::mouse::MouseEvent::from(me);
                if let MouseEvent::Hold(_) = mouse_event {
                    // as long as the user is holding the mouse down (no other stdin, eg.
                    // MouseRelease) we need to keep sending this instruction to the app,
                    // because the app itself doesn't have an event loop in the proper
                    // place
                    let mut poller = os_input.stdin_poller();
                    send_input_instructions
                        .send(InputInstruction::KeyEvent(
                            key_event.clone(),
                            raw_bytes.clone(),
                        ))
                        .unwrap();
                    loop {
                        let ready = poller.ready();
                        if ready {
                            break;
                        }
                        send_input_instructions
                            .send(InputInstruction::KeyEvent(
                                key_event.clone(),
                                raw_bytes.clone(),
                            ))
                            .unwrap();
                    }
                    continue;
                }
            }
            send_input_instructions
                .send(InputInstruction::KeyEvent(key_event, raw_bytes))
                .unwrap();
        }
    }
}