fix(editor): prevent UI redraw conflicts during editor suspend/resume
All checks were successful
CI Code / Check spelling (push) Successful in 16s
CI Code / Check coding style (push) Successful in 31s
CI Code / Code Coverage (push) Successful in 3m36s
CI Code / Linux (debian) (push) Successful in 4m43s
CI Code / Linux (ubuntu) (push) Successful in 4m54s
CI Code / Linux (arch) (push) Successful in 6m46s
All checks were successful
CI Code / Check spelling (push) Successful in 16s
CI Code / Check coding style (push) Successful in 31s
CI Code / Code Coverage (push) Successful in 3m36s
CI Code / Linux (debian) (push) Successful in 4m43s
CI Code / Linux (ubuntu) (push) Successful in 4m54s
CI Code / Linux (arch) (push) Successful in 6m46s
Guard against re-entrant editor launches, abort editor on Ctrl-Z, and centralize suspend-aware redraws through prof_doupdate() to prevent terminal corruption when SIGTSTP is received while editor is active. Removes SIGUSR1 editor escape in favor of standard pkill <editor> recovery.
This commit is contained in:
@@ -32,9 +32,15 @@ typedef struct EditorContext
|
||||
void* user_data;
|
||||
} EditorContext;
|
||||
|
||||
static gboolean editor_active = FALSE; // re-entrancy guard
|
||||
static pid_t editor_pid = 0;
|
||||
|
||||
static void
|
||||
_editor_exit_cb(GPid pid, gint status, gpointer data)
|
||||
{
|
||||
editor_active = FALSE;
|
||||
editor_pid = 0;
|
||||
|
||||
EditorContext* ctx = data;
|
||||
gchar* contents = NULL;
|
||||
GError* error = NULL;
|
||||
@@ -52,8 +58,10 @@ _editor_exit_cb(GPid pid, gint status, gpointer data)
|
||||
cons_show_error("Could not read edited content: %s", error->message);
|
||||
g_error_free(error);
|
||||
}
|
||||
} else {
|
||||
cons_show_error("Editor exited with error status %d", WEXITSTATUS(status));
|
||||
} else if (WIFEXITED(status)) {
|
||||
cons_show_error("Editor exited with status %d", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
cons_show("Editor session cancelled.");
|
||||
}
|
||||
|
||||
if (remove(ctx->filename) != 0) {
|
||||
@@ -70,6 +78,11 @@ _editor_exit_cb(GPid pid, gint status, gpointer data)
|
||||
gboolean
|
||||
launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* data), void* user_data)
|
||||
{
|
||||
if (editor_active) {
|
||||
cons_show_error("An editor session is already active.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
auto_gchar gchar* filename = NULL;
|
||||
auto_gerror GError* glib_error = NULL;
|
||||
const char* jid = connection_get_barejid();
|
||||
@@ -118,6 +131,7 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
|
||||
ctx->callback = callback;
|
||||
ctx->user_data = user_data;
|
||||
|
||||
editor_active = TRUE;
|
||||
ui_suspend();
|
||||
|
||||
// Force GLib to install its SIGCHLD handler before forking. Creating a
|
||||
@@ -129,6 +143,7 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
log_error("[Editor] Failed to fork: %s", strerror(errno));
|
||||
editor_active = FALSE;
|
||||
ui_resume();
|
||||
ui_resize();
|
||||
cons_show_error("Failed to start editor: %s", strerror(errno));
|
||||
@@ -139,7 +154,8 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
|
||||
} else if (pid == 0) {
|
||||
// Child process: Inherits TTY from parent
|
||||
|
||||
// Reset signal handlers that profanity sets so the editor doesn't inherit them
|
||||
// SIGTSTP=SIG_DFL lets vim's :stop / Ctrl-Z work; profanity catches
|
||||
// the STOPPED state via editor_check_stopped() and drops to the shell.
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTSTP, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
@@ -151,7 +167,39 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
|
||||
}
|
||||
|
||||
// Parent process: Watch the child asynchronously
|
||||
editor_pid = pid;
|
||||
g_child_watch_add((GPid)pid, _editor_exit_cb, ctx);
|
||||
g_strfreev(editor_argv);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
editor_emergency_kill(void)
|
||||
{
|
||||
if (!editor_active || editor_pid <= 0) {
|
||||
return;
|
||||
}
|
||||
log_warning("[Editor] aborting editor pid %d", editor_pid);
|
||||
kill(editor_pid, SIGCONT); // wake if STOPPED; no-op otherwise
|
||||
kill(editor_pid, SIGTERM);
|
||||
// _editor_exit_cb will restore the UI when the child reaps.
|
||||
}
|
||||
|
||||
gboolean
|
||||
editor_is_active(void)
|
||||
{
|
||||
return editor_active;
|
||||
}
|
||||
|
||||
gboolean
|
||||
editor_check_stopped(void)
|
||||
{
|
||||
// Poll for STOPPED without reaping (g_child_watch handles exit via its
|
||||
// own WNOHANG). Lets prof_run drop the group to the shell on Ctrl-Z.
|
||||
if (!editor_active || editor_pid <= 0) {
|
||||
return FALSE;
|
||||
}
|
||||
int status;
|
||||
pid_t r = waitpid(editor_pid, &status, WNOHANG | WUNTRACED);
|
||||
return (r == editor_pid && WIFSTOPPED(status));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user