Compare commits
8 Commits
fix/delay-
...
86d924eaac
| Author | SHA1 | Date | |
|---|---|---|---|
|
86d924eaac
|
|||
|
bb0af8b7c8
|
|||
|
a51afa53f6
|
|||
|
b1ffc750a2
|
|||
|
2da2449777
|
|||
|
394df6d391
|
|||
|
b282b12497
|
|||
|
|
562bd28cff
|
@@ -341,7 +341,7 @@ sv_ev_room_message(ProfMessage* message)
|
||||
status_bar_active(num, WIN_MUC, mucwin->roomjid);
|
||||
|
||||
if ((g_strcmp0(mynick, message->from_jid->resourcepart) != 0) && (prefs_get_boolean(PREF_BEEP))) {
|
||||
beep();
|
||||
ui_beep();
|
||||
}
|
||||
|
||||
// not currently on groupchat window
|
||||
@@ -349,7 +349,7 @@ sv_ev_room_message(ProfMessage* message)
|
||||
status_bar_new(num, WIN_MUC, mucwin->roomjid);
|
||||
|
||||
if ((g_strcmp0(mynick, message->from_jid->resourcepart) != 0) && (prefs_get_boolean(PREF_FLASH))) {
|
||||
flash();
|
||||
ui_flash();
|
||||
}
|
||||
|
||||
cons_show_incoming_room_message(message->from_jid->resourcepart, mucwin->roomjid, num, mention, triggers, mucwin->unread, window);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-unix.h>
|
||||
|
||||
#include "profanity.h"
|
||||
#include "common.h"
|
||||
@@ -182,6 +183,16 @@ sigterm_handler(int sig)
|
||||
force_quit = TRUE;
|
||||
}
|
||||
|
||||
// Universal "I'm stuck in the editor" escape hatch. Dispatched in the main
|
||||
// loop by GLib (g_unix_signal_add), so it is safe to call into editor.c
|
||||
// directly. Trigger from another terminal: `pkill -USR1 profanity`.
|
||||
static gboolean
|
||||
_emergency_resume_handler(gpointer data)
|
||||
{
|
||||
editor_emergency_kill();
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_init(char* log_level, char* config_file, char* log_file, char* theme_name)
|
||||
{
|
||||
@@ -193,6 +204,7 @@ _init(char* log_level, char* config_file, char* log_file, char* theme_name)
|
||||
signal(SIGWINCH, ui_sigwinch_handler);
|
||||
signal(SIGTERM, sigterm_handler);
|
||||
signal(SIGHUP, sigterm_handler);
|
||||
g_unix_signal_add(SIGUSR1, _emergency_resume_handler, NULL);
|
||||
if (pthread_mutex_init(&lock, NULL) != 0) {
|
||||
log_error("Mutex init failed");
|
||||
exit(1);
|
||||
|
||||
@@ -32,9 +32,22 @@ typedef struct EditorContext
|
||||
void* user_data;
|
||||
} EditorContext;
|
||||
|
||||
// Set TRUE for the lifetime of an editor session (from launch_editor up to
|
||||
// _editor_exit_cb / fork-error cleanup). The async editor model lost the old
|
||||
// editor_task.active check from the pthread implementation; without this
|
||||
// guard a second launch_editor (e.g. via a plugin dispatched through
|
||||
// g_main_context_iteration) would call ui_suspend again and fork a second
|
||||
// editor that fights the first one for the terminal, then prematurely
|
||||
// ui_resume when either exits.
|
||||
static gboolean editor_active = FALSE;
|
||||
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;
|
||||
@@ -70,6 +83,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 +136,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 +148,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,9 +159,11 @@ 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 intentionally NOT reset: keeping SIG_IGN inherited via
|
||||
// fork+exec avoids hanging on a STOPPED editor (g_child_watch lacks
|
||||
// WUNTRACED). Cost: vim's :stop is a no-op in /editor. See
|
||||
// editor_emergency_kill for other hung-editor scenarios.
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTSTP, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
if (editor_argv && editor_argv[0]) {
|
||||
@@ -151,7 +173,20 @@ 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] emergency resume: sending SIGTERM to editor pid %d", editor_pid);
|
||||
kill(editor_pid, SIGTERM);
|
||||
// g_child_watch dispatches _editor_exit_cb when the child exits, which
|
||||
// already restores the UI — no further action required here.
|
||||
}
|
||||
|
||||
@@ -16,4 +16,9 @@
|
||||
|
||||
gboolean launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* data), void* user_data);
|
||||
|
||||
// Universal escape hatch: if an editor session is active, send SIGTERM to the
|
||||
// child. The existing g_child_watch fires _editor_exit_cb on exit, which
|
||||
// already restores the UI. Wired to SIGUSR1 in profanity.c.
|
||||
void editor_emergency_kill(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -327,7 +327,7 @@ chatwin_incoming_msg(ProfChatWin* chatwin, ProfMessage* message, gboolean win_cr
|
||||
cons_show_incoming_message(display_name, num, chatwin->unread, window);
|
||||
|
||||
if (prefs_get_boolean(PREF_FLASH)) {
|
||||
flash();
|
||||
ui_flash();
|
||||
}
|
||||
|
||||
chatwin->unread++;
|
||||
@@ -364,7 +364,7 @@ chatwin_incoming_msg(ProfChatWin* chatwin, ProfMessage* message, gboolean win_cr
|
||||
wins_add_quotes_ac(window, message->plain, FALSE);
|
||||
|
||||
if (prefs_get_boolean(PREF_BEEP)) {
|
||||
beep();
|
||||
ui_beep();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
|
||||
static int inp_size;
|
||||
static gboolean perform_resize = FALSE;
|
||||
static gboolean ui_suspended = FALSE;
|
||||
static GTimer* ui_idle_time;
|
||||
static WINDOW* main_scr;
|
||||
|
||||
@@ -63,6 +64,16 @@ static Display* display;
|
||||
|
||||
static void _ui_draw_term_title(void);
|
||||
|
||||
// Skip drawing while an external editor is active (ui_suspended) or after the
|
||||
// screen has been closed (endwin). The flag is authoritative and does not rely
|
||||
// on isendwin() staying TRUE, which a stray doupdate() during the session could
|
||||
// otherwise clear.
|
||||
static gboolean
|
||||
_ui_redraw_suspended(void)
|
||||
{
|
||||
return ui_suspended || isendwin();
|
||||
}
|
||||
|
||||
static void
|
||||
_ui_close(void)
|
||||
{
|
||||
@@ -120,8 +131,7 @@ ui_sigwinch_handler(int sig)
|
||||
void
|
||||
ui_update(void)
|
||||
{
|
||||
// UI is suspended
|
||||
if (isendwin()) {
|
||||
if (_ui_redraw_suspended()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,7 +148,7 @@ ui_update(void)
|
||||
title_bar_update_virtual();
|
||||
status_bar_draw();
|
||||
inp_put_back();
|
||||
doupdate();
|
||||
prof_doupdate();
|
||||
|
||||
if (perform_resize) {
|
||||
perform_resize = FALSE;
|
||||
@@ -180,21 +190,48 @@ void
|
||||
ui_suspend(void)
|
||||
{
|
||||
inp_suspend();
|
||||
// Flush before flipping the flag (prof_doupdate skips while suspended).
|
||||
prof_doupdate();
|
||||
ui_suspended = TRUE;
|
||||
endwin();
|
||||
}
|
||||
|
||||
void
|
||||
ui_resume(void)
|
||||
{
|
||||
ui_suspended = FALSE;
|
||||
refresh();
|
||||
inp_resume();
|
||||
}
|
||||
|
||||
void
|
||||
ui_beep(void)
|
||||
{
|
||||
if (!isendwin()) {
|
||||
beep();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ui_flash(void)
|
||||
{
|
||||
if (!isendwin()) {
|
||||
flash();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
prof_doupdate(void)
|
||||
{
|
||||
if (!_ui_redraw_suspended()) {
|
||||
doupdate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ui_resize(void)
|
||||
{
|
||||
// UI is suspended
|
||||
if (isendwin()) {
|
||||
if (_ui_redraw_suspended()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -216,8 +253,7 @@ ui_resize(void)
|
||||
void
|
||||
ui_redraw(void)
|
||||
{
|
||||
// UI is suspended
|
||||
if (isendwin()) {
|
||||
if (_ui_redraw_suspended()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,13 @@ inp_readline(void)
|
||||
{
|
||||
// UI is suspended
|
||||
if (is_suspended || isendwin()) {
|
||||
// Mirror the normal-path unlock/sleep/lock so worker threads
|
||||
// (http_upload / http_download / ai_client) can acquire `lock`
|
||||
// during the editor session — otherwise the main thread holds
|
||||
// it continuously and every in-flight transfer freezes.
|
||||
pthread_mutex_unlock(&lock);
|
||||
g_usleep(100000); // 100ms
|
||||
pthread_mutex_lock(&lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -311,11 +317,15 @@ inp_get_line(void)
|
||||
werase(inp_win);
|
||||
wmove(inp_win, 0, 0);
|
||||
_inp_win_update_virtual();
|
||||
doupdate();
|
||||
prof_doupdate();
|
||||
char* line = NULL;
|
||||
while (!line) {
|
||||
line = inp_readline();
|
||||
ui_update();
|
||||
// Let GLib sources (notably the editor child watch) dispatch so
|
||||
// ui_resume can run if the prompt was raised mid-editor-session.
|
||||
while (g_main_context_iteration(NULL, FALSE))
|
||||
;
|
||||
}
|
||||
status_bar_clear_prompt();
|
||||
return line;
|
||||
@@ -338,12 +348,16 @@ inp_get_password(void)
|
||||
werase(inp_win);
|
||||
wmove(inp_win, 0, 0);
|
||||
_inp_win_update_virtual();
|
||||
doupdate();
|
||||
prof_doupdate();
|
||||
char* password = NULL;
|
||||
get_password = TRUE;
|
||||
while (!password) {
|
||||
password = inp_readline();
|
||||
ui_update();
|
||||
// Let GLib sources (notably the editor child watch) dispatch so
|
||||
// ui_resume can run if the prompt was raised mid-editor-session.
|
||||
while (g_main_context_iteration(NULL, FALSE))
|
||||
;
|
||||
}
|
||||
get_password = FALSE;
|
||||
status_bar_clear_prompt();
|
||||
@@ -459,7 +473,7 @@ _inp_write(char* line, int offset)
|
||||
_inp_win_handle_scroll();
|
||||
|
||||
_inp_win_update_virtual();
|
||||
doupdate();
|
||||
prof_doupdate();
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -57,7 +57,7 @@ privwin_incoming_msg(ProfPrivateWin* privatewin, ProfMessage* message)
|
||||
privatewin->unread++;
|
||||
|
||||
if (prefs_get_boolean(PREF_FLASH)) {
|
||||
flash();
|
||||
ui_flash();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ privwin_incoming_msg(ProfPrivateWin* privatewin, ProfMessage* message)
|
||||
wins_add_quotes_ac(window, message->plain, TRUE);
|
||||
|
||||
if (prefs_get_boolean(PREF_BEEP)) {
|
||||
beep();
|
||||
ui_beep();
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
|
||||
@@ -43,6 +43,10 @@ void ui_load_colours(void);
|
||||
void ui_update(void);
|
||||
void ui_redraw(void);
|
||||
void ui_resize(void);
|
||||
// Centralised physical-paint primitive: flushes the virtual screen unless the
|
||||
// UI is suspended (external editor active) or the screen has been closed. Use
|
||||
// this instead of raw doupdate() in any path reachable while suspended.
|
||||
void prof_doupdate(void);
|
||||
void ui_focus_win(ProfWin* window);
|
||||
void ui_sigwinch_handler(int sig);
|
||||
void ui_handle_otr_error(const char* const barejid, const char* const message);
|
||||
@@ -84,6 +88,8 @@ void ui_handle_recipient_error(const char* const recipient, const char* const er
|
||||
void ui_handle_error(const char* const err_msg);
|
||||
void ui_clear_win_title(void);
|
||||
void ui_goodbye_title(void);
|
||||
void ui_beep(void);
|
||||
void ui_flash(void);
|
||||
void ui_handle_room_configuration_form_error(const char* const roomjid, const char* const message);
|
||||
void ui_handle_room_config_submit_result(const char* const roomjid);
|
||||
void ui_handle_room_config_submit_result_error(const char* const roomjid, const char* const message);
|
||||
|
||||
@@ -85,6 +85,10 @@ void
|
||||
ui_resize(void)
|
||||
{
|
||||
}
|
||||
void
|
||||
prof_doupdate(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ui_focus_win(ProfWin* win)
|
||||
@@ -1475,3 +1479,13 @@ ui_is_suspended(void)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
ui_beep(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ui_flash(void)
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user