fix(agent): restore raw mode
This commit is contained in:
1 parent
59d9bc7830
commit
ce5b70be9c
3 files changed
+64
No files matched your search
+39
@@ -145,6 +145,7 @@ typedef struct {
|
||||
bool more_valid;
|
||||
agent_bash_job *bash_jobs;
|
||||
int next_bash_job_id;
|
||||
bool raw_mode_needs_restore;
|
||||
} agent_worker;
|
||||
|
||||
static unsigned agent_next_prefill_label(void);
|
||||
@@ -6586,6 +6587,7 @@ struct agent_bash_job {
|
||||
bool running;
|
||||
bool timed_out;
|
||||
struct agent_bash_job *next;
|
||||
agent_worker *worker; /* back-pointer for terminal state restoration */
|
||||
};
|
||||
|
||||
static int agent_bash_display_lines(const agent_bash_job *job) {
|
||||
@@ -6674,6 +6676,13 @@ static void agent_bash_finalize(agent_bash_job *job, int status) {
|
||||
else if (WIFSIGNALED(status)) job->exit_status = 128 + WTERMSIG(status);
|
||||
else job->exit_status = -1;
|
||||
job->running = false;
|
||||
/* A child process (especially sh) may have changed the terminal mode
|
||||
* from raw to cooked. Notify the UI thread to restore raw mode. */
|
||||
if (job->worker) {
|
||||
pthread_mutex_lock(&job->worker->mu);
|
||||
job->worker->raw_mode_needs_restore = true;
|
||||
pthread_mutex_unlock(&job->worker->mu);
|
||||
}
|
||||
}
|
||||
|
||||
/* Drain available output, notice process exit, and enforce timeout. This is
|
||||
@@ -6741,6 +6750,17 @@ static agent_bash_job *agent_bash_start(agent_worker *w, const char *cmd,
|
||||
if (pid == 0) {
|
||||
setpgid(0, 0);
|
||||
close(tmpfd);
|
||||
/* Redirect stdin to /dev/null so the shell does not attempt to
|
||||
* change the terminal mode (cooked/raw) on the controlling
|
||||
* terminal. If the shell sets cooked mode while linenoise has
|
||||
* raw mode active, the terminal becomes unresponsive to user
|
||||
* input until the process exits and linenoise re-enables raw
|
||||
* mode. */
|
||||
int null_fd = open("/dev/null", O_RDONLY);
|
||||
if (null_fd >= 0) {
|
||||
dup2(null_fd, STDIN_FILENO);
|
||||
close(null_fd);
|
||||
}
|
||||
close(pipefd[0]);
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
dup2(pipefd[1], STDERR_FILENO);
|
||||
@@ -6767,6 +6787,7 @@ static agent_bash_job *agent_bash_start(agent_worker *w, const char *cmd,
|
||||
job->timeout_sec = timeout_sec;
|
||||
job->exit_status = -1;
|
||||
job->running = true;
|
||||
job->worker = w;
|
||||
job->next = w->bash_jobs;
|
||||
w->bash_jobs = job;
|
||||
return job;
|
||||
@@ -8030,6 +8051,19 @@ static int set_nonblock(int fd, bool on, int *old_flags) {
|
||||
return fcntl(fd, F_SETFL, next);
|
||||
}
|
||||
|
||||
/* Check and clear the raw_mode_needs_restore flag under the worker mutex.
|
||||
* Returns true if the UI thread should call linenoiseRestoreRawMode(). */
|
||||
static bool worker_check_raw_mode_restore(agent_worker *w) {
|
||||
bool needs = false;
|
||||
pthread_mutex_lock(&w->mu);
|
||||
if (w->raw_mode_needs_restore) {
|
||||
w->raw_mode_needs_restore = false;
|
||||
needs = true;
|
||||
}
|
||||
pthread_mutex_unlock(&w->mu);
|
||||
return needs;
|
||||
}
|
||||
|
||||
static void drain_wake_fd(int fd) {
|
||||
char buf[128];
|
||||
for (;;) {
|
||||
@@ -9704,6 +9738,11 @@ static int run_agent(ds4_engine *engine, agent_config *cfg) {
|
||||
bool force_status_redraw_after_restart = false;
|
||||
char *restore_line = NULL;
|
||||
while (running) {
|
||||
/* If a bash child process changed the terminal mode (e.g., from raw
|
||||
* to cooked), restore raw mode so linenoise continues to work. */
|
||||
if (worker_check_raw_mode_restore(&worker)) {
|
||||
linenoiseRestoreRawMode();
|
||||
}
|
||||
struct pollfd pfd[2] = {
|
||||
{.fd = STDIN_FILENO, .events = POLLIN},
|
||||
{.fd = worker.wake_fd[0], .events = POLLIN},
|
||||
|
||||
+23
@@ -624,6 +624,29 @@ static void disableRawMode(int fd) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-enable raw mode if a child process changed the terminal state. This is
|
||||
* useful when the application spawns external processes that may reset the
|
||||
* terminal to cooked mode. The caller should ensure that no concurrent
|
||||
* linenoise operations are in progress (e.g., not inside a refresh cycle). */
|
||||
void linenoiseRestoreRawMode(void) {
|
||||
if (getenv("LINENOISE_ASSUME_TTY")) return;
|
||||
if (!isatty(STDIN_FILENO)) return;
|
||||
/* Re-fetch the original terminal attributes (they may have been changed
|
||||
* by a child process) and re-apply raw mode. */
|
||||
struct termios raw;
|
||||
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) return;
|
||||
raw = orig_termios;
|
||||
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||
raw.c_oflag &= ~(OPOST);
|
||||
raw.c_cflag |= (CS8);
|
||||
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
|
||||
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||
rawmode = 1;
|
||||
/* Re-enable bracketed paste mode. */
|
||||
if (write(rawmode_output, "\x1b[?2004h", 8) == -1) {}
|
||||
}
|
||||
|
||||
/* Use the ESC [6n escape sequence to query the horizontal cursor position
|
||||
* and return it. On error -1 is returned, on success the position of the
|
||||
* cursor. */
|
||||
|
||||
@@ -139,6 +139,8 @@ void linenoiseSetMultiLine(int ml);
|
||||
void linenoisePrintKeyCodes(void);
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
/* Re-enable raw mode if a child process changed the terminal state. */
|
||||
void linenoiseRestoreRawMode(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user