zellij/zellij-client/assets/input.js
2025-07-30 11:54:40 +02:00

135 lines
4.8 KiB
JavaScript

/**
* Input handling for terminal events
*/
import { encode_kitty_key } from "./keyboard.js";
import { isMac } from "./utils.js";
/**
* Set up all input handlers for the terminal
* @param {Terminal} term - The terminal instance
* @param {function} sendFunction - Function to send data through WebSocket
*/
export function setupInputHandlers(term, sendFunction) {
// Mouse tracking state
let prev_col = 0;
let prev_row = 0;
// Custom key event handler
term.attachCustomKeyEventHandler((ev) => {
if (ev.type === "keydown") {
if (ev.key == "V" && ev.ctrlKey && ev.shiftKey) {
// pass ctrl-shift-v onwards so that paste is interpreted by xterm.js
return;
}
if (isMac() && ev.key == "v" && ev.metaKey) {
// pass cmd-v onwards so that paste is interpreted by xterm.js
return;
}
let modifiers_count = 0;
let shift_keycode = 16;
let alt_keycode = 17;
let ctrl_keycode = 18;
if (ev.altKey) {
modifiers_count += 1;
}
if (ev.ctrlKey) {
modifiers_count += 1;
}
if (ev.shiftKey) {
modifiers_count += 1;
}
if (ev.metaKey) {
modifiers_count += 1;
}
if (
(modifiers_count > 1 || ev.metaKey) &&
ev.keyCode != shift_keycode &&
ev.keyCode != alt_keycode &&
ev.keyCode != ctrl_keycode
) {
ev.preventDefault();
encode_kitty_key(ev, sendFunction);
return false;
}
// workarounds for https://github.com/xtermjs/xterm.js/blob/41e8ae395937011d6bf6c7cb618b851791aed395/src/common/input/Keyboard.ts#L158
if (ev.key == "ArrowLeft" && ev.altKey) {
ev.preventDefault();
sendFunction("\x1b[1;3D");
return false;
}
if (ev.key == "ArrowRight" && ev.altKey) {
ev.preventDefault();
sendFunction("\x1b[1;3C");
return false;
}
if (ev.key == "ArrowUp" && ev.altKey) {
ev.preventDefault();
sendFunction("\x1b[1;3A");
return false;
}
if (ev.key == "ArrowDown" && ev.altKey) {
ev.preventDefault();
sendFunction("\x1b[1;3B");
return false;
}
if (
(ev.key == "=" && ev.altKey) ||
(ev.key == "+" && ev.altKey) ||
(ev.key == "-" && ev.altKey)
) {
// these are not properly handled by xterm.js, so we bypass it and encode them as kitty to make things easier
ev.preventDefault();
encode_kitty_key(ev, sendFunction);
return false;
}
}
return true;
});
// Mouse movement handler
let terminal_element = document.getElementById("terminal");
terminal_element.addEventListener("mousemove", function (event) {
window.term.focus();
// this is a hack around: https://github.com/xtermjs/xterm.js/issues/1062
// in short, xterm.js doesn't listen to mousemove at all and so even though
// we send it a request for AnyEvent mouse handling, we don't get motion events in return
// here we use some internal functions in a hopefully non-destructive way to calculate the
// columns/rows to send from the x/y coordinates - it's safe to always send these because Zellij
// always requests mouse AnyEvent handling
if (event.buttons == 0) {
// this means no mouse buttons are pressed and this is just a mouse movement
let { col, row } = term._core._mouseService.getMouseReportCoords(
event,
terminal_element
);
if (prev_col != col || prev_row != row) {
sendFunction(`\x1b[<35;${col + 1};${row + 1}M`);
}
prev_col = col;
prev_row = row;
}
});
// Context menu handler
document.addEventListener("contextmenu", function (event) {
if (event.altKey) {
// this is so that when the user does an alt-right-click to ungroup panes, the context menu will not appear
event.preventDefault();
}
});
// Terminal data handlers
term.onData((data) => {
sendFunction(data);
});
term.onBinary((data) => {
const buffer = new Uint8Array(data.length);
for (let i = 0; i < data.length; ++i) {
buffer[i] = data.charCodeAt(i) & 255;
}
sendFunction(buffer);
});
}