diff --git a/.gitignore b/.gitignore index 39e9b5a5e..fc6251545 100644 --- a/.gitignore +++ b/.gitignore @@ -1,72 +1,9 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ - -# IDE -.codelite/ -profanity.mk -profanity.project -profanity.workspace -compile_commands.json -.tern-port -.tern-project -.cproject -.project -.settings/ -.vscode/ -*.plist/ - -# autotools -.libs/ -/Makefile -/Makefile.in -_configs.sed -aclocal.m4 -autom4te.cache/ -build-aux/ -config.log -config.status -configure -configure*~ -libprofanity.la -libtool -m4/ -**/.deps/ -**.dirstamp -src/config.h -src/config.h.in -src/config.h.in~ -src/gitversion.h -src/stamp-h1 -src/plugins/profapi.lo - # out-of-tree build folders build*/ -# binaries -profanity -**/*.o - -# test output -tests/functionaltests/functionaltests -tests/functionaltests/functionaltests.log -tests/functionaltests/functionaltests.trs -tests/unittests/unittests -tests/unittests/unittests.log -tests/unittests/unittests.trs -test-suite.log - # valgrind output profval* -# local scripts -clean-test.sh -gen_docs.sh -gitpushall.sh - # website files main_fragment.html toc_fragment.html @@ -88,18 +25,16 @@ apidocs/c/doxygen_sqlite3.db # Temp Vim files **/*.swp -# Virtual envs -python2/ -python3/ +# GDB +.gdbinit +# Misc .DS_Store -.gdbinit *.bak *.orig *.patch *.rej breaks - *.tar.* *.zip *.log* diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 4d7a821bf..25649a953 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -109,6 +109,35 @@ 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) { + if (launch_editor(*field, _vcard_editor_finished_cb, field)) { + return TRUE; + } + } else { + if (*field) { + free(*field); + } + *field = strdup(value); + } + return FALSE; +} + /** * @brief Processes a line of input and determines if profanity should continue. * @@ -384,7 +413,10 @@ cmd_connect(ProfWin* window, const char* const command, gchar** args) // use eval_password if set } else if (account->eval_password) { + ui_suspend(); gboolean res = account_eval_password(account); + ui_resume(); + ui_resize(); if (res) { conn_status = cl_ev_connect_account(account); free(account->password); @@ -1615,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) { @@ -4008,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; } @@ -9542,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; } @@ -9565,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; } @@ -10110,21 +10163,8 @@ cmd_vcard_set(ProfWin* window, const char* const command, gchar** args) switch (element->type) { case VCARD_NICKNAME: - if (!value) { - gchar* editor_value; - if (get_message_from_editor(element->nickname, &editor_value)) { - return TRUE; - } - - if (element->nickname) { - free(element->nickname); - } - element->nickname = editor_value; - } else { - if (element->nickname) { - free(element->nickname); - } - element->nickname = strdup(value); + if (_update_vcard_field(&element->nickname, value)) { + return TRUE; } break; case VCARD_BIRTHDAY: @@ -10140,130 +10180,38 @@ cmd_vcard_set(ProfWin* window, const char* const command, gchar** args) element->birthday = g_date_time_new_local(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 0, 0, 0); break; case VCARD_TELEPHONE: - if (!value) { - gchar* editor_value; - if (get_message_from_editor(element->telephone.number, &editor_value)) { - return TRUE; - } - - if (element->telephone.number) { - free(element->telephone.number); - } - element->telephone.number = editor_value; - } else { - if (element->telephone.number) { - free(element->telephone.number); - } - element->telephone.number = strdup(value); + if (_update_vcard_field(&element->telephone.number, value)) { + return TRUE; } - break; case VCARD_EMAIL: - if (!value) { - gchar* editor_value; - if (get_message_from_editor(element->email.userid, &editor_value)) { - return TRUE; - } - - if (element->email.userid) { - free(element->email.userid); - } - element->email.userid = editor_value; - } else { - if (element->email.userid) { - free(element->email.userid); - } - element->email.userid = strdup(value); + if (_update_vcard_field(&element->email.userid, value)) { + return TRUE; } break; case VCARD_JID: - if (!value) { - gchar* editor_value; - if (get_message_from_editor(element->jid, &editor_value)) { - return TRUE; - } - - if (element->jid) { - free(element->jid); - } - element->jid = editor_value; - } else { - if (element->jid) { - free(element->jid); - } - element->jid = strdup(value); + if (_update_vcard_field(&element->jid, value)) { + return TRUE; } break; case VCARD_TITLE: - if (!value) { - gchar* editor_value; - if (get_message_from_editor(element->title, &editor_value)) { - return TRUE; - } - - if (element->title) { - free(element->title); - } - element->title = editor_value; - } else { - if (element->title) { - free(element->title); - } - element->title = strdup(value); + if (_update_vcard_field(&element->title, value)) { + return TRUE; } break; case VCARD_ROLE: - if (!value) { - gchar* editor_value; - if (get_message_from_editor(element->role, &editor_value)) { - return TRUE; - } - - if (element->role) { - free(element->role); - } - element->role = editor_value; - } else { - if (element->role) { - free(element->role); - } - element->role = strdup(value); + if (_update_vcard_field(&element->role, value)) { + return TRUE; } break; case VCARD_NOTE: - if (!value) { - gchar* editor_value; - if (get_message_from_editor(element->note, &editor_value)) { - return TRUE; - } - - if (element->note) { - free(element->note); - } - element->note = editor_value; - } else { - if (element->note) { - free(element->note); - } - element->note = strdup(value); + if (_update_vcard_field(&element->note, value)) { + return TRUE; } break; case VCARD_URL: - if (!value) { - gchar* editor_value; - if (get_message_from_editor(element->url, &editor_value)) { - return TRUE; - } - - if (element->url) { - free(element->url); - } - element->url = editor_value; - } else { - if (element->url) { - free(element->url); - } - element->url = strdup(value); + if (_update_vcard_field(&element->url, value)) { + return TRUE; } break; default: @@ -10271,123 +10219,32 @@ cmd_vcard_set(ProfWin* window, const char* const command, gchar** args) } } else if (value) { if (g_strcmp0(value, "pobox") == 0 && element->type == VCARD_ADDRESS) { - if (!value2) { - gchar* editor_value; - if (get_message_from_editor(element->address.pobox, &editor_value)) { - return TRUE; - } - - if (element->address.pobox) { - free(element->address.pobox); - } - element->address.pobox = editor_value; - } else { - if (element->address.pobox) { - free(element->address.pobox); - } - element->address.pobox = strdup(value2); + if (_update_vcard_field(&element->address.pobox, value2)) { + return TRUE; } } else if (g_strcmp0(value, "extaddr") == 0 && element->type == VCARD_ADDRESS) { - if (!value2) { - gchar* editor_value; - if (get_message_from_editor(element->address.extaddr, &editor_value)) { - return TRUE; - } - - if (element->address.extaddr) { - free(element->address.extaddr); - } - element->address.extaddr = editor_value; - } else { - if (element->address.extaddr) { - free(element->address.extaddr); - } - element->address.extaddr = strdup(value2); + if (_update_vcard_field(&element->address.extaddr, value2)) { + return TRUE; } } else if (g_strcmp0(value, "street") == 0 && element->type == VCARD_ADDRESS) { - if (!value2) { - gchar* editor_value; - if (get_message_from_editor(element->address.street, &editor_value)) { - return TRUE; - } - - if (element->address.street) { - free(element->address.street); - } - element->address.street = editor_value; - } else { - if (element->address.street) { - free(element->address.street); - } - element->address.street = strdup(value2); + if (_update_vcard_field(&element->address.street, value2)) { + return TRUE; } } else if (g_strcmp0(value, "locality") == 0 && element->type == VCARD_ADDRESS) { - if (!value2) { - gchar* editor_value; - if (get_message_from_editor(element->address.locality, &editor_value)) { - return TRUE; - } - - if (element->address.locality) { - free(element->address.locality); - } - element->address.locality = editor_value; - } else { - if (element->address.locality) { - free(element->address.locality); - } - element->address.locality = strdup(value2); + if (_update_vcard_field(&element->address.locality, value2)) { + return TRUE; } } else if (g_strcmp0(value, "region") == 0 && element->type == VCARD_ADDRESS) { - if (!value2) { - gchar* editor_value; - if (get_message_from_editor(element->address.region, &editor_value)) { - return TRUE; - } - - if (element->address.region) { - free(element->address.region); - } - element->address.region = editor_value; - } else { - if (element->address.region) { - free(element->address.region); - } - element->address.region = strdup(value2); + if (_update_vcard_field(&element->address.region, value2)) { + return TRUE; } } else if (g_strcmp0(value, "pocode") == 0 && element->type == VCARD_ADDRESS) { - if (!value2) { - gchar* editor_value; - if (get_message_from_editor(element->address.pcode, &editor_value)) { - return TRUE; - } - - if (element->address.pcode) { - free(element->address.pcode); - } - element->address.pcode = editor_value; - } else { - if (element->address.pcode) { - free(element->address.pcode); - } - element->address.pcode = strdup(value2); + if (_update_vcard_field(&element->address.pcode, value2)) { + return TRUE; } } else if (g_strcmp0(value, "country") == 0 && element->type == VCARD_ADDRESS) { - if (!value2) { - gchar* editor_value; - if (get_message_from_editor(element->address.country, &editor_value)) { - return TRUE; - } - - if (element->address.country) { - free(element->address.country); - } - element->address.country = editor_value; - } else { - if (element->address.country) { - free(element->address.country); - } - element->address.country = strdup(value2); + if (_update_vcard_field(&element->address.country, value2)) { + return TRUE; } } else if (g_strcmp0(value, "type") == 0 && element->type == VCARD_ADDRESS) { if (g_strcmp0(value2, "domestic") == 0) { diff --git a/src/config/account.c b/src/config/account.c index cf9650f61..76f96bfb2 100644 --- a/src/config/account.c +++ b/src/config/account.c @@ -138,13 +138,23 @@ account_eval_password(ProfAccount* account) gchar* stdout_buf = NULL; GError* error = NULL; gint exit_status = 0; + gchar** argv = NULL; - if (!g_spawn_command_line_sync(account->eval_password, &stdout_buf, NULL, &exit_status, &error)) { + if (!g_shell_parse_argv(account->eval_password, NULL, &argv, &error)) { + log_error("Failed to parse `eval_password` command: %s", error->message); + g_error_free(error); + return FALSE; + } + + if (!g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, &stdout_buf, NULL, &exit_status, &error)) { log_error("Failed to execute `eval_password` command: %s", error->message); + g_strfreev(argv); g_error_free(error); return FALSE; } + g_strfreev(argv); + if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0) { account->password = stdout_buf; g_strstrip(account->password); diff --git a/src/config/accounts.c b/src/config/accounts.c index 6bd77e7bb..3938f4a3f 100644 --- a/src/config/accounts.c +++ b/src/config/accounts.c @@ -75,6 +75,17 @@ _accounts_save(const char* account_name) auto_gchar gchar* sanitized = _sanitize_account_name(account_name); if (_accounts_has_group(sanitized)) { + // Remove keys from file that are no longer in memory + gsize nkeys_disk; + auto_gcharv gchar** keys_disk = g_key_file_get_keys(current.keyfile, sanitized, &nkeys_disk, NULL); + if (keys_disk) { + for (gsize j = 0; j < nkeys_disk; ++j) { + if (!g_key_file_has_key(accounts_prof_keyfile.keyfile, sanitized, keys_disk[j], NULL)) { + g_key_file_remove_key(current.keyfile, sanitized, keys_disk[j], NULL); + } + } + } + gsize nkeys; auto_gcharv gchar** keys = g_key_file_get_keys(accounts_prof_keyfile.keyfile, sanitized, &nkeys, NULL); if (keys) { diff --git a/src/database.c b/src/database.c index 54e413109..7f35ddec0 100644 --- a/src/database.c +++ b/src/database.c @@ -449,7 +449,6 @@ _add_to_db(ProfMessage* message, char* type, const Jid* const from_jid, const Ji return; } - char* err_msg; auto_gchar gchar* date_fmt = prof_date_time_format_iso8601(message->timestamp); const char* enc = _get_message_enc_str(message->enc); diff --git a/src/tools/editor.c b/src/tools/editor.c index 03d94ed75..bf9e9c4cc 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 + EditorContext* ctx = g_new0(EditorContext, 1); + ctx->filename = g_steal_pointer(&filename); + ctx->callback = callback; + ctx->user_data = user_data; + + ui_suspend(); + 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); - } + 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); - } else { - if (pid == -1) { - return TRUE; - } - waitpid(pid, NULL, 0); - - 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); - } } + // 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 f38d2c626..f79941254 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 bec9f534b..4e8a76068 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); @@ -171,9 +176,26 @@ ui_reset_idle_time(void) g_timer_start(ui_idle_time); } +void +ui_suspend(void) +{ + endwin(); +} + +void +ui_resume(void) +{ + refresh(); +} + void ui_resize(void) { + // UI is suspended + if (isendwin()) { + return; + } + struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); erase(); @@ -192,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 60c588943..f53b7889b 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; } diff --git a/src/ui/ui.h b/src/ui/ui.h index 4ab2ac539..31b0a961d 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -36,6 +36,8 @@ // core UI void ui_init(void); +void ui_suspend(void); +void ui_resume(void); void ui_load_colours(void); void ui_update(void); void ui_redraw(void); diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c index 803288251..2f46dd708 100644 --- a/tests/unittests/ui/stub_ui.c +++ b/tests/unittests/ui/stub_ui.c @@ -62,6 +62,14 @@ ui_init(void) { } void +ui_suspend(void) +{ +} +void +ui_resume(void) +{ +} +void ui_load_colours(void) { }