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
|
- ssh kitten: A new option :code:`--symlink-strategy` to control how symlinks
|
||||||
are copied to the remote machine (:iss:`5249`)
|
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`)
|
- 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
|
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 ..tui.utils import kitty_opts, running_in_tmux
|
||||||
from .config import init_config
|
from .config import init_config
|
||||||
from .copy import CopyInstruction
|
from .copy import CopyInstruction
|
||||||
@ -547,10 +550,13 @@ def connection_sharing_args(kitty_pid: int) -> List[str]:
|
|||||||
def restore_terminal_state() -> Iterator[bool]:
|
def restore_terminal_state() -> Iterator[bool]:
|
||||||
with open(os.ctermid()) as f:
|
with open(os.ctermid()) as f:
|
||||||
val = termios.tcgetattr(f.fileno())
|
val = termios.tcgetattr(f.fileno())
|
||||||
|
print(end=SAVE_PRIVATE_MODE_VALUES)
|
||||||
|
print(end=set_mode(Mode.HANDLE_TERMIOS_SIGNALS), flush=True)
|
||||||
try:
|
try:
|
||||||
yield bool(val[3] & termios.ECHO)
|
yield bool(val[3] & termios.ECHO)
|
||||||
finally:
|
finally:
|
||||||
termios.tcsetattr(f.fileno(), termios.TCSAFLUSH, val)
|
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:
|
def dcs_to_kitty(payload: Union[bytes, str], type: str = 'ssh') -> bytes:
|
||||||
|
|||||||
@ -47,6 +47,7 @@ class Mode(Enum):
|
|||||||
ALTERNATE_SCREEN = 1049, '?'
|
ALTERNATE_SCREEN = 1049, '?'
|
||||||
BRACKETED_PASTE = 2004, '?'
|
BRACKETED_PASTE = 2004, '?'
|
||||||
PENDING_UPDATE = 2026, '?'
|
PENDING_UPDATE = 2026, '?'
|
||||||
|
HANDLE_TERMIOS_SIGNALS = 19997, '?'
|
||||||
|
|
||||||
|
|
||||||
def cmd(f: F) -> F:
|
def cmd(f: F) -> F:
|
||||||
|
|||||||
@ -448,3 +448,24 @@ class Child:
|
|||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
return environ_of_process(pid)
|
return environ_of_process(pid)
|
||||||
return {}
|
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));
|
schedule_write_to_child(w->id, 1, text, strlen(text));
|
||||||
debug("sent key as text to child\n");
|
debug("sent key as text to child\n");
|
||||||
} else if (size > 0) {
|
} 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);
|
schedule_write_to_child(w->id, 1, encoded_key, size);
|
||||||
debug("sent encoded key to child\n");
|
debug("sent encoded key to child\n");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -84,3 +84,6 @@
|
|||||||
|
|
||||||
// Pending updates mode
|
// Pending updates mode
|
||||||
#define PENDING_UPDATE (2026 << 5)
|
#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(DECARM)
|
||||||
SIMPLE_MODE(BRACKETED_PASTE)
|
SIMPLE_MODE(BRACKETED_PASTE)
|
||||||
SIMPLE_MODE(FOCUS_TRACKING)
|
SIMPLE_MODE(FOCUS_TRACKING)
|
||||||
|
SIMPLE_MODE(HANDLE_TERMIOS_SIGNALS)
|
||||||
MOUSE_MODE(MOUSE_BUTTON_TRACKING, mouse_tracking_mode, BUTTON_MODE)
|
MOUSE_MODE(MOUSE_BUTTON_TRACKING, mouse_tracking_mode, BUTTON_MODE)
|
||||||
MOUSE_MODE(MOUSE_MOTION_TRACKING, mouse_tracking_mode, MOTION_MODE)
|
MOUSE_MODE(MOUSE_MOTION_TRACKING, mouse_tracking_mode, MOTION_MODE)
|
||||||
MOUSE_MODE(MOUSE_MOVE_TRACKING, mouse_tracking_mode, ANY_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);
|
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
|
void
|
||||||
screen_push_colors(Screen *self, unsigned int idx) {
|
screen_push_colors(Screen *self, unsigned int idx) {
|
||||||
if (colorprofile_push_colors(self->color_profile, idx)) self->color_profile->dirty = true;
|
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 {
|
typedef struct {
|
||||||
bool mLNM, mIRM, mDECTCEM, mDECSCNM, mDECOM, mDECAWM, mDECCOLM, mDECARM, mDECCKM,
|
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;
|
MouseTrackingMode mouse_tracking_mode;
|
||||||
MouseTrackingProtocol mouse_tracking_protocol;
|
MouseTrackingProtocol mouse_tracking_protocol;
|
||||||
bool eight_bit_controls; // S8C1T
|
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);
|
bool screen_detect_url(Screen *screen, unsigned int x, unsigned int y);
|
||||||
int screen_cursor_at_a_shell_prompt(const Screen *);
|
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_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);
|
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
|
||||||
DECLARE_CH_SCREEN_HANDLER(bell)
|
DECLARE_CH_SCREEN_HANDLER(bell)
|
||||||
DECLARE_CH_SCREEN_HANDLER(backspace)
|
DECLARE_CH_SCREEN_HANDLER(backspace)
|
||||||
|
|||||||
@ -857,6 +857,13 @@ class Window:
|
|||||||
'--ssh-connection-data', json.dumps(conn_data)
|
'--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:
|
def focus_changed(self, focused: bool) -> None:
|
||||||
if self.destroyed:
|
if self.destroyed:
|
||||||
return
|
return
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user