ssh kitten: Allow pressing Ctrl-C to abort ssh before the connection is completed
Fixing this involved adding a new mode to kitty where it handles ctrl-c/z/q by sending signals to the tty foreground process group instead of delegating to the kernel to do that. Since the pipe may be full we have no way of knowing when the kernel will get around to reading the signal byte. So send the signal ourselves. Fixes #5271
This commit is contained in:
parent
bd9e1f58fe
commit
7215c6d6be
@ -71,6 +71,9 @@ Detailed list of changes
|
||||
- ssh kitten: A new option :code:`--symlink-strategy` to control how symlinks
|
||||
are copied to the remote machine (:iss:`5249`)
|
||||
|
||||
- ssh kitten: Allow pressing Ctrl-C to abort ssh before the connection is
|
||||
completed (:iss:`5271`)
|
||||
|
||||
- Bash integration: Fix declare not creating global variables in .bashrc (:iss:`5254`)
|
||||
|
||||
|
||||
|
||||
@ -39,7 +39,10 @@ from kitty.utils import (
|
||||
set_echo as turn_off_echo
|
||||
)
|
||||
|
||||
from ..tui.operations import restore_colors, save_colors
|
||||
from ..tui.operations import (
|
||||
RESTORE_PRIVATE_MODE_VALUES, SAVE_PRIVATE_MODE_VALUES, Mode,
|
||||
restore_colors, save_colors, set_mode
|
||||
)
|
||||
from ..tui.utils import kitty_opts, running_in_tmux
|
||||
from .config import init_config
|
||||
from .copy import CopyInstruction
|
||||
@ -547,10 +550,13 @@ def connection_sharing_args(kitty_pid: int) -> List[str]:
|
||||
def restore_terminal_state() -> Iterator[bool]:
|
||||
with open(os.ctermid()) as f:
|
||||
val = termios.tcgetattr(f.fileno())
|
||||
print(end=SAVE_PRIVATE_MODE_VALUES)
|
||||
print(end=set_mode(Mode.HANDLE_TERMIOS_SIGNALS), flush=True)
|
||||
try:
|
||||
yield bool(val[3] & termios.ECHO)
|
||||
finally:
|
||||
termios.tcsetattr(f.fileno(), termios.TCSAFLUSH, val)
|
||||
print(end=RESTORE_PRIVATE_MODE_VALUES, flush=True)
|
||||
|
||||
|
||||
def dcs_to_kitty(payload: Union[bytes, str], type: str = 'ssh') -> bytes:
|
||||
|
||||
@ -47,6 +47,7 @@ class Mode(Enum):
|
||||
ALTERNATE_SCREEN = 1049, '?'
|
||||
BRACKETED_PASTE = 2004, '?'
|
||||
PENDING_UPDATE = 2026, '?'
|
||||
HANDLE_TERMIOS_SIGNALS = 19997, '?'
|
||||
|
||||
|
||||
def cmd(f: F) -> F:
|
||||
|
||||
@ -448,3 +448,24 @@ class Child:
|
||||
with suppress(Exception):
|
||||
return environ_of_process(pid)
|
||||
return {}
|
||||
|
||||
def send_signal_for_key(self, key_num: int) -> bool:
|
||||
import signal
|
||||
import termios
|
||||
if self.child_fd is None:
|
||||
return False
|
||||
t = termios.tcgetattr(self.child_fd)
|
||||
if not t[3] & termios.ISIG:
|
||||
return False
|
||||
cc = t[-1]
|
||||
if key_num == cc[termios.VINTR]:
|
||||
s = signal.SIGINT
|
||||
elif key_num == cc[termios.VSUSP]:
|
||||
s = signal.SIGTSTP
|
||||
elif key_num == cc[termios.VQUIT]:
|
||||
s = signal.SIGQUIT
|
||||
else:
|
||||
return False
|
||||
pgrp = os.tcgetpgrp(self.child_fd)
|
||||
os.killpg(pgrp, s)
|
||||
return True
|
||||
|
||||
@ -218,6 +218,9 @@ on_key_input(GLFWkeyevent *ev) {
|
||||
schedule_write_to_child(w->id, 1, text, strlen(text));
|
||||
debug("sent key as text to child\n");
|
||||
} else if (size > 0) {
|
||||
if (size == 1 && screen->modes.mHANDLE_TERMIOS_SIGNALS) {
|
||||
if (screen_send_signal_for_key(screen, *encoded_key)) return;
|
||||
}
|
||||
schedule_write_to_child(w->id, 1, encoded_key, size);
|
||||
debug("sent encoded key to child\n");
|
||||
} else {
|
||||
|
||||
@ -84,3 +84,6 @@
|
||||
|
||||
// Pending updates mode
|
||||
#define PENDING_UPDATE (2026 << 5)
|
||||
|
||||
// Handle Ctrl-C/Ctrl-Z mode
|
||||
#define HANDLE_TERMIOS_SIGNALS (19997 << 5)
|
||||
|
||||
@ -984,6 +984,7 @@ set_mode_from_const(Screen *self, unsigned int mode, bool val) {
|
||||
SIMPLE_MODE(DECARM)
|
||||
SIMPLE_MODE(BRACKETED_PASTE)
|
||||
SIMPLE_MODE(FOCUS_TRACKING)
|
||||
SIMPLE_MODE(HANDLE_TERMIOS_SIGNALS)
|
||||
MOUSE_MODE(MOUSE_BUTTON_TRACKING, mouse_tracking_mode, BUTTON_MODE)
|
||||
MOUSE_MODE(MOUSE_MOTION_TRACKING, mouse_tracking_mode, MOTION_MODE)
|
||||
MOUSE_MODE(MOUSE_MOVE_TRACKING, mouse_tracking_mode, ANY_MODE)
|
||||
@ -2085,6 +2086,20 @@ screen_handle_cmd(Screen *self, PyObject *cmd) {
|
||||
CALLBACK("handle_remote_cmd", "O", cmd);
|
||||
}
|
||||
|
||||
bool
|
||||
screen_send_signal_for_key(Screen *self, char key) {
|
||||
int ret = 0;
|
||||
if (self->callbacks != Py_None) {
|
||||
int cchar = key;
|
||||
PyObject *callback_ret = PyObject_CallMethod(self->callbacks, "send_signal_for_key", "c", cchar);
|
||||
if (callback_ret) {
|
||||
ret = PyObject_IsTrue(callback_ret);
|
||||
Py_DECREF(callback_ret);
|
||||
} else { PyErr_Print(); }
|
||||
}
|
||||
return ret != 0;
|
||||
}
|
||||
|
||||
void
|
||||
screen_push_colors(Screen *self, unsigned int idx) {
|
||||
if (colorprofile_push_colors(self->color_profile, idx)) self->color_profile->dirty = true;
|
||||
|
||||
@ -14,7 +14,7 @@ typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } Scr
|
||||
|
||||
typedef struct {
|
||||
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM, mDECCKM,
|
||||
mBRACKETED_PASTE, mFOCUS_TRACKING, mDECSACE;
|
||||
mBRACKETED_PASTE, mFOCUS_TRACKING, mDECSACE, mHANDLE_TERMIOS_SIGNALS;
|
||||
MouseTrackingMode mouse_tracking_mode;
|
||||
MouseTrackingProtocol mouse_tracking_protocol;
|
||||
bool eight_bit_controls; // S8C1T
|
||||
@ -263,6 +263,7 @@ void screen_report_key_encoding_flags(Screen *self);
|
||||
bool screen_detect_url(Screen *screen, unsigned int x, unsigned int y);
|
||||
int screen_cursor_at_a_shell_prompt(const Screen *);
|
||||
bool screen_fake_move_cursor_to_position(Screen *, index_type x, index_type y);
|
||||
bool screen_send_signal_for_key(Screen *, char key);
|
||||
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
|
||||
DECLARE_CH_SCREEN_HANDLER(bell)
|
||||
DECLARE_CH_SCREEN_HANDLER(backspace)
|
||||
|
||||
@ -857,6 +857,13 @@ class Window:
|
||||
'--ssh-connection-data', json.dumps(conn_data)
|
||||
)
|
||||
|
||||
def send_signal_for_key(self, key_num: int) -> bool:
|
||||
try:
|
||||
return self.child.send_signal_for_key(key_num)
|
||||
except OSError as err:
|
||||
log_error(f'Failed to send signal for key to child with err: {err}')
|
||||
return False
|
||||
|
||||
def focus_changed(self, focused: bool) -> None:
|
||||
if self.destroyed:
|
||||
return
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user