diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index efcea89f..25649a95 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -109,19 +109,26 @@ static gboolean _cmd_execute_alias(ProfWin* window, const char* const inp, gbool static gboolean _download_install_plugin(ProfWin* window, gchar* url, gchar* path); +static void +_vcard_editor_finished_cb(gchar* message, void* user_data) +{ + if (message) { + char** field = user_data; + if (*field) { + free(*field); + } + *field = g_strdup(message); + cons_show("Field updated. Remember to call /me save to apply changes."); + } +} + static gboolean _update_vcard_field(char** field, char* value) { if (!value) { - gchar* editor_value; - if (get_message_from_editor(*field, &editor_value)) { + if (launch_editor(*field, _vcard_editor_finished_cb, field)) { return TRUE; } - - if (*field) { - free(*field); - } - *field = editor_value; } else { if (*field) { free(*field); @@ -1640,6 +1647,51 @@ cmd_help(ProfWin* window, const char* const command, gchar** args) return TRUE; } +static void +_cmd_editor_finished_cb(gchar* message, void* user_data) +{ + if (message) { + rl_insert_text(message); + rl_point = rl_end; + rl_forced_update_display(); + } +} + +typedef struct +{ + ProfWin* window; + char* roomjid; +} EditorFinishedContext; + +static void +_cmd_room_editor_finished_cb(gchar* message, void* user_data) +{ + EditorFinishedContext* ctx = user_data; + if (message && ctx->roomjid) { + message_send_groupchat_subject(ctx->roomjid, message); + } + if (ctx) { + g_free(ctx->roomjid); + g_free(ctx); + } +} + +static void +_cmd_correct_editor_finished_cb(gchar* message, void* user_data) +{ + EditorFinishedContext* ctx = user_data; + if (message && ctx->window) { + if (ctx->window->type == WIN_CHAT) { + ProfChatWin* chatwin = (ProfChatWin*)ctx->window; + cl_ev_send_msg_correct(chatwin, message, FALSE, TRUE); + } else if (ctx->window->type == WIN_MUC) { + ProfMucWin* mucwin = (ProfMucWin*)ctx->window; + cl_ev_send_muc_msg_corrected(mucwin, message, FALSE, TRUE); + } + } + g_free(ctx); +} + gboolean cmd_about(ProfWin* window, const char* const command, gchar** args) { @@ -4033,18 +4085,13 @@ cmd_subject(ProfWin* window, const char* const command, gchar** args) } if (g_strcmp0(args[0], "editor") == 0) { - gchar* message = NULL; char* subject = muc_subject(mucwin->roomjid); - if (get_message_from_editor(subject, &message)) { - return TRUE; - } + EditorFinishedContext* ctx = g_new0(EditorFinishedContext, 1); + ctx->roomjid = g_strdup(mucwin->roomjid); + + launch_editor(subject, _cmd_room_editor_finished_cb, ctx); - if (message) { - message_send_groupchat_subject(mucwin->roomjid, message); - } else { - cons_bad_cmd_usage(command); - } return TRUE; } @@ -9567,16 +9614,7 @@ cmd_change_password(ProfWin* window, const char* const command, gchar** args) gboolean cmd_editor(ProfWin* window, const char* const command, gchar** args) { - auto_gchar gchar* message = NULL; - - if (get_message_from_editor(NULL, &message)) { - return TRUE; - } - - rl_insert_text(message); - ui_resize(); - rl_point = rl_end; - rl_forced_update_display(); + launch_editor(NULL, _cmd_editor_finished_cb, NULL); return TRUE; } @@ -9590,20 +9628,10 @@ cmd_correct_editor(ProfWin* window, const char* const command, gchar** args) gchar* initial_message = win_get_last_sent_message(window); - auto_gchar gchar* message = NULL; - if (get_message_from_editor(initial_message, &message)) { - return TRUE; - } + EditorFinishedContext* ctx = g_new0(EditorFinishedContext, 1); + ctx->window = window; - if (window->type == WIN_CHAT) { - ProfChatWin* chatwin = (ProfChatWin*)window; - - cl_ev_send_msg_correct(chatwin, message, FALSE, TRUE); - } else if (window->type == WIN_MUC) { - ProfMucWin* mucwin = (ProfMucWin*)window; - - cl_ev_send_muc_msg_corrected(mucwin, message, FALSE, TRUE); - } + launch_editor(initial_message, _cmd_correct_editor_finished_cb, ctx); return TRUE; } diff --git a/src/tools/editor.c b/src/tools/editor.c index 03d94ed7..bf9e9c4c 100644 --- a/src/tools/editor.c +++ b/src/tools/editor.c @@ -20,14 +20,52 @@ #include "log.h" #include "common.h" #include "xmpp/xmpp.h" +#include "ui/ui.h" + +typedef struct EditorContext +{ + gchar* filename; + void (*callback)(gchar* content, void* user_data); + void* user_data; +} EditorContext; + +static void +_editor_exit_cb(GPid pid, gint status, gpointer data) +{ + EditorContext* ctx = data; + gchar* contents = NULL; + GError* error = NULL; + + ui_resume(); + ui_resize(); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + if (g_file_get_contents(ctx->filename, &contents, NULL, &error)) { + g_strchomp(contents); + ctx->callback(contents, ctx->user_data); + g_free(contents); + } else { + log_error("[Editor] could not read from %s: %s", ctx->filename, error->message); + 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)); + } + + if (remove(ctx->filename) != 0) { + log_error("[Editor] error during file deletion of %s", ctx->filename); + } + + g_free(ctx->filename); + g_free(ctx); + g_spawn_close_pid(pid); +} // Returns true if an error occurred gboolean -get_message_from_editor(gchar* message, gchar** returned_message) +launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* data), void* user_data) { - /* Make sure that there's no junk in the return-pointer in error cases */ - *returned_message = NULL; - auto_gchar gchar* filename = NULL; auto_gerror GError* glib_error = NULL; const char* jid = connection_get_barejid(); @@ -37,64 +75,65 @@ get_message_from_editor(gchar* message, gchar** returned_message) log_debug("[Editor] could not get JID"); auto_gchar gchar* data_dir = files_get_data_path(DIR_EDITOR); if (!create_dir(data_dir)) { + cons_show_error("Could not create editor directory."); return TRUE; } filename = g_strdup_printf("%s/compose.md", data_dir); } if (!filename) { log_error("[Editor] something went wrong while creating compose file"); + cons_show_error("Could not create compose file."); return TRUE; } gsize messagelen = 0; - if (message != NULL) { - messagelen = strlen(message); + if (initial_content != NULL) { + messagelen = strlen(initial_content); } - if (!g_file_set_contents(filename, message, messagelen, &glib_error)) { + if (!g_file_set_contents(filename, initial_content, messagelen, &glib_error)) { log_error("[Editor] could not write to %s: %s", filename, PROF_GERROR_MESSAGE(glib_error)); + cons_show_error("Could not write to compose file: %s", PROF_GERROR_MESSAGE(glib_error)); return TRUE; } auto_gchar gchar* editor = prefs_get_string(PREF_COMPOSE_EDITOR); - auto_gchar gchar* editor_with_filename = g_strdup_printf("%s %s", editor, filename); - auto_gcharv gchar** editor_argv = g_strsplit(editor_with_filename, " ", 0); + gchar** editor_argv = NULL; + GError* error = NULL; - if (!editor_argv || !editor_argv[0]) { - log_error("[Editor] Failed to parse editor command: %s", editor); + auto_gchar gchar* full_cmd = g_strdup_printf("%s %s", editor, filename); + if (!g_shell_parse_argv(full_cmd, NULL, &editor_argv, &error)) { + log_error("[Editor] Failed to parse editor command: %s", error->message); + cons_show_error("Failed to parse editor command: %s", error->message); + g_error_free(error); return TRUE; } - // Fork / exec - pid_t pid = fork(); - if (pid == 0) { - if (editor_argv && editor_argv[0]) { - int x = execvp(editor_argv[0], editor_argv); - if (x == -1) - log_error("[Editor] Failed to exec %s", editor); - } - _exit(EXIT_FAILURE); - } else { - if (pid == -1) { - return TRUE; - } - waitpid(pid, NULL, 0); + EditorContext* ctx = g_new0(EditorContext, 1); + ctx->filename = g_steal_pointer(&filename); + ctx->callback = callback; + ctx->user_data = user_data; - gchar* contents; - gsize length; - if (!g_file_get_contents(filename, &contents, &length, &glib_error)) { - log_error("[Editor] could not read from %s: %s", filename, PROF_GERROR_MESSAGE(glib_error)); - return TRUE; - } - /* Remove all trailing new-line characters */ - g_strchomp(contents); - *returned_message = contents; - if (remove(filename) != 0) { - log_error("[Editor] error during file deletion of %s", filename); - } else { - log_debug("[Editor] deleted file: %s", filename); - } + ui_suspend(); + + pid_t pid = fork(); + if (pid == -1) { + log_error("[Editor] Failed to fork: %s", strerror(errno)); + ui_resume(); + ui_resize(); + cons_show_error("Failed to start editor: %s", strerror(errno)); + g_strfreev(editor_argv); + g_free(ctx->filename); + g_free(ctx); + return TRUE; + } else if (pid == 0) { + // Child process: Inherits TTY from parent + execvp(editor_argv[0], editor_argv); + _exit(EXIT_FAILURE); } + // Parent process: Watch the child asynchronously + g_child_watch_add((GPid)pid, _editor_exit_cb, ctx); + g_strfreev(editor_argv); return FALSE; } diff --git a/src/tools/editor.h b/src/tools/editor.h index f38d2c62..f7994125 100644 --- a/src/tools/editor.h +++ b/src/tools/editor.h @@ -13,6 +13,6 @@ #include -gboolean get_message_from_editor(gchar* message, gchar** returned_message); +gboolean launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* data), void* user_data); #endif diff --git a/src/ui/core.c b/src/ui/core.c index ac287383..4e8a7606 100644 --- a/src/ui/core.c +++ b/src/ui/core.c @@ -120,6 +120,11 @@ ui_sigwinch_handler(int sig) void ui_update(void) { + // UI is suspended + if (isendwin()) { + return; + } + ProfWin* current = wins_get_current(); if (current->layout->paged == 0) { win_move_to_end(current); @@ -186,6 +191,11 @@ ui_resume(void) void ui_resize(void) { + // UI is suspended + if (isendwin()) { + return; + } + struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); erase(); @@ -204,6 +214,11 @@ ui_resize(void) void ui_redraw(void) { + // UI is suspended + if (isendwin()) { + return; + } + title_bar_resize(); wins_resize_all(); status_bar_resize(); diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c index 60c58894..f53b7889 100644 --- a/src/ui/inputwin.c +++ b/src/ui/inputwin.c @@ -171,6 +171,12 @@ _inp_slashguard_check(void) char* inp_readline(void) { + // UI is suspended + if (isendwin()) { + g_usleep(100000); // 100ms + return NULL; + } + p_rl_timeout.tv_sec = inp_timeout / 1000; p_rl_timeout.tv_usec = inp_timeout % 1000 * 1000; FD_ZERO(&fds); @@ -996,6 +1002,16 @@ _inp_rl_down_arrow_handler(int count, int key) return 0; } +static void +_editor_finished_cb(gchar* message, void* user_data) +{ + if (message) { + rl_replace_line(message, 0); + rl_point = rl_end; + rl_forced_update_display(); + } +} + static int _inp_rl_send_to_editor(int count, int key) { @@ -1003,16 +1019,7 @@ _inp_rl_send_to_editor(int count, int key) return 0; } - auto_gchar gchar* message = NULL; - - if (get_message_from_editor(rl_line_buffer, &message)) { - return 0; - } - - rl_replace_line(message, 0); - ui_resize(); - rl_point = rl_end; - rl_forced_update_display(); + launch_editor(rl_line_buffer, _editor_finished_cb, NULL); return 0; }