1
0
mirror of https://github.com/profanity-im/profanity.git synced 2024-06-23 21:45:30 +00:00
profanity/src/ui/core.c
Paul Fertser e1323655ee Fix race condition on resize
The current code is inherently racy: if screen update takes
considerable time (e.g. when working over network) and a user
performed a series of resizes the final event might get ignored and
the display will be left in inconsistent state.

Fix the race by unsetting the flag first so if the next WINCH signal
is received while display is resizing it'll be processed on the next
iteration.
2022-04-01 10:49:01 +03:00

1320 lines
35 KiB
C

/*
* core.c
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
*
*/
#include "config.h"
#ifdef HAVE_GIT_VERSION
#include "gitversion.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <glib.h>
#ifdef HAVE_LIBXSS
#include <X11/extensions/scrnsaver.h>
#endif
#ifdef HAVE_NCURSESW_NCURSES_H
#include <ncursesw/ncurses.h>
#elif HAVE_NCURSES_H
#include <ncurses.h>
#elif HAVE_CURSES_H
#include <curses.h>
#endif
#include "log.h"
#include "common.h"
#include "command/cmd_defs.h"
#include "command/cmd_ac.h"
#include "config/preferences.h"
#include "config/theme.h"
#include "ui/ui.h"
#include "ui/titlebar.h"
#include "ui/statusbar.h"
#include "ui/inputwin.h"
#include "ui/window.h"
#include "ui/window_list.h"
#include "xmpp/xmpp.h"
#include "xmpp/muc.h"
#include "xmpp/chat_session.h"
#include "xmpp/contact.h"
#include "xmpp/roster_list.h"
#include "xmpp/jid.h"
#ifdef HAVE_LIBOTR
#include "otr/otr.h"
#endif
static int inp_size;
static gboolean perform_resize = FALSE;
static GTimer* ui_idle_time;
#ifdef HAVE_LIBXSS
static Display* display;
#endif
static void _ui_draw_term_title(void);
void
ui_init(void)
{
log_info("Initialising UI");
initscr();
nonl();
cbreak();
noecho();
keypad(stdscr, TRUE);
ui_load_colours();
refresh();
create_title_bar();
status_bar_init();
status_bar_active(1, WIN_CONSOLE, "console");
create_input_window();
wins_init();
notifier_initialise();
cons_about();
#ifdef HAVE_LIBXSS
display = XOpenDisplay(0);
#endif
ui_idle_time = g_timer_new();
inp_size = 0;
ProfWin* window = wins_get_current();
win_update_virtual(window);
}
void
ui_sigwinch_handler(int sig)
{
perform_resize = TRUE;
}
void
ui_update(void)
{
ProfWin* current = wins_get_current();
if (current->layout->paged == 0) {
win_move_to_end(current);
}
win_update_virtual(current);
if (prefs_get_boolean(PREF_WINTITLE_SHOW)) {
_ui_draw_term_title();
}
title_bar_update_virtual();
status_bar_draw();
inp_put_back();
doupdate();
if (perform_resize) {
perform_resize = FALSE;
ui_resize();
}
}
unsigned long
ui_get_idle_time(void)
{
// if compiled with libxss, get the x sessions idle time
#ifdef HAVE_LIBXSS
XScreenSaverInfo* info = XScreenSaverAllocInfo();
if (info && display) {
XScreenSaverQueryInfo(display, DefaultRootWindow(display), info);
unsigned long result = info->idle;
XFree(info);
return result;
}
if (info) {
XFree(info);
}
// if no libxss or xss idle time failed, use profanity idle time
#endif
gdouble seconds_elapsed = g_timer_elapsed(ui_idle_time, NULL);
unsigned long ms_elapsed = seconds_elapsed * 1000.0;
return ms_elapsed;
}
void
ui_reset_idle_time(void)
{
g_timer_start(ui_idle_time);
}
void
ui_close(void)
{
notifier_uninit();
cons_clear_alerts();
wins_destroy();
inp_close();
status_bar_close();
endwin();
}
void
ui_resize(void)
{
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
erase();
resizeterm(w.ws_row, w.ws_col);
refresh();
log_debug("Resizing UI");
title_bar_resize();
wins_resize_all();
status_bar_resize();
inp_win_resize();
ProfWin* window = wins_get_current();
win_update_virtual(window);
}
void
ui_redraw(void)
{
title_bar_resize();
wins_resize_all();
status_bar_resize();
inp_win_resize();
}
void
ui_load_colours(void)
{
if (has_colors()) {
use_default_colors();
start_color();
theme_init_colours();
}
}
void
ui_contact_online(char* barejid, Resource* resource, GDateTime* last_activity)
{
char* show_console = prefs_get_string(PREF_STATUSES_CONSOLE);
char* show_chat_win = prefs_get_string(PREF_STATUSES_CHAT);
PContact contact = roster_get_contact(barejid);
// show nothing
if (g_strcmp0(p_contact_subscription(contact), "none") == 0) {
free(show_console);
free(show_chat_win);
return;
}
// show in console if "all"
if (g_strcmp0(show_console, "all") == 0) {
cons_show_contact_online(contact, resource, last_activity);
// show in console of "online" and presence online
} else if (g_strcmp0(show_console, "online") == 0 && resource->presence == RESOURCE_ONLINE) {
cons_show_contact_online(contact, resource, last_activity);
}
// show in chat win if "all"
if (g_strcmp0(show_chat_win, "all") == 0) {
ProfChatWin* chatwin = wins_get_chat(barejid);
if (chatwin) {
chatwin_contact_online(chatwin, resource, last_activity);
}
// show in char win if "online" and presence online
} else if (g_strcmp0(show_chat_win, "online") == 0 && resource->presence == RESOURCE_ONLINE) {
ProfChatWin* chatwin = wins_get_chat(barejid);
if (chatwin) {
chatwin_contact_online(chatwin, resource, last_activity);
}
}
free(show_console);
free(show_chat_win);
}
void
ui_contact_typing(const char* const barejid, const char* const resource)
{
ProfChatWin* chatwin = wins_get_chat(barejid);
ProfWin* window = (ProfWin*)chatwin;
ChatSession* session = chat_session_get(barejid);
// no chat window for user
if (chatwin == NULL) {
if (prefs_get_boolean(PREF_INTYPE_CONSOLE)) {
cons_show_typing(barejid);
}
// have chat window but not currently in it
} else if (!wins_is_current(window)) {
if (prefs_get_boolean(PREF_INTYPE_CONSOLE)) {
cons_show_typing(barejid);
}
// in chat window with user, no session or session with resource
} else if (!session || (session && g_strcmp0(session->resource, resource) == 0)) {
if (prefs_get_boolean(PREF_INTYPE)) {
title_bar_set_typing(TRUE);
int num = wins_get_num(window);
status_bar_active(num, WIN_CHAT, chatwin->barejid);
}
}
if (prefs_get_boolean(PREF_NOTIFY_TYPING)) {
gboolean is_current = FALSE;
if (window) {
is_current = wins_is_current(window);
}
if (!is_current || (is_current && prefs_get_boolean(PREF_NOTIFY_TYPING_CURRENT))) {
PContact contact = roster_get_contact(barejid);
char const* display_usr = NULL;
if (contact) {
if (p_contact_name(contact)) {
display_usr = p_contact_name(contact);
} else {
display_usr = barejid;
}
} else {
display_usr = barejid;
}
notify_typing(display_usr);
}
}
}
void
ui_roster_add(const char* const barejid, const char* const name)
{
if (name) {
cons_show("Roster item added: %s (%s)", barejid, name);
} else {
cons_show("Roster item added: %s", barejid);
}
rosterwin_roster();
}
void
ui_roster_remove(const char* const barejid)
{
cons_show("Roster item removed: %s", barejid);
rosterwin_roster();
}
void
ui_contact_already_in_group(const char* const contact, const char* const group)
{
cons_show("%s already in group %s", contact, group);
rosterwin_roster();
}
void
ui_contact_not_in_group(const char* const contact, const char* const group)
{
cons_show("%s is not currently in group %s", contact, group);
rosterwin_roster();
}
void
ui_group_added(const char* const contact, const char* const group)
{
cons_show("%s added to group %s", contact, group);
rosterwin_roster();
}
void
ui_group_removed(const char* const contact, const char* const group)
{
cons_show("%s removed from group %s", contact, group);
rosterwin_roster();
}
void
ui_handle_login_account_success(ProfAccount* account, gboolean secured)
{
if (account->theme) {
if (theme_load(account->theme, false)) {
ui_load_colours();
if (prefs_get_boolean(PREF_ROSTER)) {
ui_show_roster();
} else {
ui_hide_roster();
}
if (prefs_get_boolean(PREF_OCCUPANTS)) {
ui_show_all_room_rosters();
} else {
ui_hide_all_room_rosters();
}
ui_resize();
} else {
cons_show("Couldn't find account theme: %s", account->theme);
}
}
resource_presence_t resource_presence = accounts_get_login_presence(account->name);
contact_presence_t contact_presence = contact_presence_from_resource_presence(resource_presence);
cons_show_login_success(account, secured);
title_bar_set_presence(contact_presence);
title_bar_set_connected(TRUE);
title_bar_set_tls(secured);
status_bar_set_fulljid(connection_get_fulljid());
}
void
ui_update_presence(const resource_presence_t resource_presence,
const char* const message, const char* const show)
{
contact_presence_t contact_presence = contact_presence_from_resource_presence(resource_presence);
title_bar_set_presence(contact_presence);
gint priority = accounts_get_priority_for_presence_type(session_get_account_name(), resource_presence);
if (message) {
cons_show("Status set to %s (priority %d), \"%s\".", show, priority, message);
} else {
cons_show("Status set to %s (priority %d).", show, priority);
}
}
void
ui_handle_recipient_error(const char* const recipient, const char* const err_msg)
{
// always show in console
cons_show_error("Error from %s: %s", recipient, err_msg);
ProfChatWin* chatwin = wins_get_chat(recipient);
if (chatwin) {
win_println((ProfWin*)chatwin, THEME_ERROR, "!", "Error from %s: %s", recipient, err_msg);
return;
}
ProfMucWin* mucwin = wins_get_muc(recipient);
if (mucwin) {
win_println((ProfWin*)mucwin, THEME_ERROR, "!", "Error from %s: %s", recipient, err_msg);
return;
}
ProfPrivateWin* privatewin = wins_get_private(recipient);
if (privatewin) {
win_println((ProfWin*)privatewin, THEME_ERROR, "!", "Error from %s: %s", recipient, err_msg);
return;
}
}
void
ui_handle_otr_error(const char* const barejid, const char* const message)
{
ProfChatWin* chatwin = wins_get_chat(barejid);
if (chatwin) {
win_println((ProfWin*)chatwin, THEME_ERROR, "!", "%s", message);
} else {
cons_show_error("%s - %s", barejid, message);
}
}
void
ui_handle_error(const char* const err_msg)
{
GString* msg = g_string_new("");
g_string_printf(msg, "Error %s", err_msg);
cons_show_error(msg->str);
g_string_free(msg, TRUE);
}
void
ui_invalid_command_usage(const char* const cmd, void (*setting_func)(void))
{
GString* msg = g_string_new("");
g_string_printf(msg, "Invalid usage, see '/help %s' for details.", &cmd[1]);
if (setting_func) {
cons_show("");
(*setting_func)();
} else {
cons_show("");
cons_show(msg->str);
ProfWin* current = wins_get_current();
if (current->type == WIN_CHAT) {
win_println(current, THEME_DEFAULT, "-", "%s", msg->str);
}
}
g_string_free(msg, TRUE);
}
void
ui_disconnected(void)
{
wins_lost_connection();
title_bar_set_connected(FALSE);
title_bar_set_tls(FALSE);
title_bar_set_presence(CONTACT_OFFLINE);
status_bar_clear_fulljid();
ui_hide_roster();
}
void
ui_close_connected_win(int index)
{
ProfWin* window = wins_get_by_num(index);
if (window) {
if (window->type == WIN_MUC) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
presence_leave_chat_room(mucwin->roomjid);
muc_leave(mucwin->roomjid);
ui_leave_room(mucwin->roomjid);
} else if (window->type == WIN_CHAT) {
ProfChatWin* chatwin = (ProfChatWin*)window;
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
#ifdef HAVE_LIBOTR
if (chatwin->is_otr) {
otr_end_session(chatwin->barejid);
}
#endif
chat_state_gone(chatwin->barejid, chatwin->state);
chat_session_remove(chatwin->barejid);
}
}
}
int
ui_close_all_wins(void)
{
int count = 0;
jabber_conn_status_t conn_status = connection_get_status();
GList* win_nums = wins_get_nums();
GList* curr = win_nums;
while (curr) {
int num = GPOINTER_TO_INT(curr->data);
if ((num != 1) && (!ui_win_has_unsaved_form(num))) {
if (conn_status == JABBER_CONNECTED) {
ui_close_connected_win(num);
}
ui_close_win(num);
count++;
}
curr = g_list_next(curr);
}
g_list_free(curr);
g_list_free(win_nums);
return count;
}
int
ui_close_read_wins(void)
{
int count = 0;
jabber_conn_status_t conn_status = connection_get_status();
GList* win_nums = wins_get_nums();
GList* curr = win_nums;
while (curr) {
int num = GPOINTER_TO_INT(curr->data);
if ((num != 1) && (ui_win_unread(num) == 0) && (!ui_win_has_unsaved_form(num))) {
if (conn_status == JABBER_CONNECTED) {
ui_close_connected_win(num);
}
ui_close_win(num);
count++;
}
curr = g_list_next(curr);
}
g_list_free(curr);
g_list_free(win_nums);
return count;
}
void
ui_redraw_all_room_rosters(void)
{
GList* win_nums = wins_get_nums();
GList* curr = win_nums;
while (curr) {
int num = GPOINTER_TO_INT(curr->data);
ProfWin* window = wins_get_by_num(num);
if (window->type == WIN_MUC && win_has_active_subwin(window)) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
occupantswin_occupants(mucwin->roomjid);
}
curr = g_list_next(curr);
}
g_list_free(curr);
g_list_free(win_nums);
}
void
ui_hide_all_room_rosters(void)
{
GList* win_nums = wins_get_nums();
GList* curr = win_nums;
while (curr) {
int num = GPOINTER_TO_INT(curr->data);
ProfWin* window = wins_get_by_num(num);
if (window->type == WIN_MUC && win_has_active_subwin(window)) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
mucwin_hide_occupants(mucwin);
}
curr = g_list_next(curr);
}
g_list_free(curr);
g_list_free(win_nums);
}
void
ui_show_all_room_rosters(void)
{
GList* win_nums = wins_get_nums();
GList* curr = win_nums;
while (curr) {
int num = GPOINTER_TO_INT(curr->data);
ProfWin* window = wins_get_by_num(num);
if (window->type == WIN_MUC && !win_has_active_subwin(window)) {
ProfMucWin* mucwin = (ProfMucWin*)window;
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
mucwin_show_occupants(mucwin);
}
curr = g_list_next(curr);
}
g_list_free(curr);
g_list_free(win_nums);
}
gboolean
ui_win_has_unsaved_form(int num)
{
ProfWin* window = wins_get_by_num(num);
if (window->type == WIN_CONFIG) {
ProfConfWin* confwin = (ProfConfWin*)window;
assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
return confwin->form->modified;
} else {
return FALSE;
}
}
void
ui_focus_win(ProfWin* window)
{
assert(window != NULL);
if (wins_is_current(window)) {
return;
}
ProfWin* old_current = wins_get_current();
if (old_current->type == WIN_CONFIG) {
ProfConfWin* confwin = (ProfConfWin*)old_current;
cmd_ac_remove_form_fields(confwin->form);
}
if (window->type == WIN_CONFIG) {
ProfConfWin* confwin = (ProfConfWin*)window;
cmd_ac_add_form_fields(confwin->form);
}
// check for trackbar last position separator
switch (old_current->type) {
case WIN_CHAT:
{
ProfChatWin* chatwin = (ProfChatWin*)old_current;
win_remove_entry_message(old_current, chatwin->barejid);
break;
}
case WIN_MUC:
{
ProfMucWin* mucwin = (ProfMucWin*)old_current;
win_remove_entry_message(old_current, mucwin->roomjid);
break;
}
case WIN_PRIVATE:
{
ProfPrivateWin* privwin = (ProfPrivateWin*)old_current;
win_remove_entry_message(old_current, privwin->fulljid);
break;
}
default:
break;
}
int i = wins_get_num(window);
wins_set_current_by_num(i);
if (i == 1) {
title_bar_console();
rosterwin_roster();
} else {
title_bar_switch();
}
status_bar_current(i);
char* identifier = win_get_tab_identifier(window);
status_bar_active(i, window->type, identifier);
free(identifier);
}
void
ui_close_win(int index)
{
ProfWin* window = wins_get_by_num(index);
if (window && window->type == WIN_CONFIG) {
ProfConfWin* confwin = (ProfConfWin*)window;
if (confwin->form) {
cmd_ac_remove_form_fields(confwin->form);
}
}
wins_close_by_num(index);
title_bar_console();
status_bar_current(1);
status_bar_active(1, WIN_CONSOLE, "console");
}
void
ui_prune_wins(void)
{
jabber_conn_status_t conn_status = connection_get_status();
gboolean pruned = FALSE;
GSList* wins = wins_get_prune_wins();
if (wins) {
pruned = TRUE;
}
GSList* curr = wins;
while (curr) {
ProfWin* window = curr->data;
if (window->type == WIN_CHAT) {
if (conn_status == JABBER_CONNECTED) {
ProfChatWin* chatwin = (ProfChatWin*)window;
chat_session_remove(chatwin->barejid);
}
}
int num = wins_get_num(window);
ui_close_win(num);
curr = g_slist_next(curr);
}
if (wins) {
g_slist_free(wins);
}
wins_tidy();
if (pruned) {
cons_show("Windows pruned.");
} else {
cons_show("No prune needed.");
}
}
void
ui_print_system_msg_from_recipient(const char* const barejid, const char* message)
{
if (barejid == NULL || message == NULL)
return;
ProfChatWin* chatwin = wins_get_chat(barejid);
ProfWin* window = (ProfWin*)chatwin;
if (window == NULL) {
window = wins_new_chat(barejid);
if (window) {
chatwin = (ProfChatWin*)window;
int num = wins_get_num(window);
status_bar_active(num, WIN_CHAT, chatwin->barejid);
} else {
window = wins_get_console();
status_bar_active(1, WIN_CONSOLE, "console");
}
}
win_println(window, THEME_DEFAULT, "-", "*%s %s", barejid, message);
}
void
ui_room_join(const char* const roomjid, gboolean focus)
{
ProfMucWin* mucwin = wins_get_muc(roomjid);
if (mucwin == NULL) {
mucwin = mucwin_new(roomjid);
}
ProfWin* window = (ProfWin*)mucwin;
char* nick = muc_nick(roomjid);
win_print(window, THEME_ROOMINFO, "!", "-> You have joined the room as %s", nick);
if (prefs_get_boolean(PREF_MUC_PRIVILEGES)) {
char* role = muc_role_str(roomjid);
char* affiliation = muc_affiliation_str(roomjid);
if (role) {
win_append(window, THEME_ROOMINFO, ", role: %s", role);
}
if (affiliation) {
win_append(window, THEME_ROOMINFO, ", affiliation: %s", affiliation);
}
}
win_appendln(window, THEME_ROOMINFO, "");
if (focus) {
ui_focus_win(window);
} else {
int num = wins_get_num(window);
status_bar_active(num, WIN_MUC, mucwin->roomjid);
ProfWin* console = wins_get_console();
win_println(console, THEME_TYPING, "!", "-> Autojoined %s as %s (%d).", roomjid, nick, num);
}
GList* privwins = wins_get_private_chats(roomjid);
GList* curr = privwins;
while (curr) {
ProfPrivateWin* privwin = curr->data;
privwin_room_joined(privwin);
curr = g_list_next(curr);
}
g_list_free(privwins);
}
void
ui_switch_to_room(const char* const roomjid)
{
ProfWin* window = (ProfWin*)wins_get_muc(roomjid);
ui_focus_win(window);
}
void
ui_room_destroy(const char* const roomjid)
{
ProfWin* window = (ProfWin*)wins_get_muc(roomjid);
GList* privwins = wins_get_private_chats(roomjid);
if (window == NULL) {
log_error("Received room destroy result, but no window open for %s.", roomjid);
} else {
int num = wins_get_num(window);
ui_close_win(num);
cons_show("Room destroyed: %s", roomjid);
}
GList* curr = privwins;
while (curr) {
ProfPrivateWin* privwin = curr->data;
privwin_room_destroyed(privwin);
curr = g_list_next(curr);
}
g_list_free(privwins);
}
void
ui_leave_room(const char* const roomjid)
{
ProfWin* window = (ProfWin*)wins_get_muc(roomjid);
GList* privwins = wins_get_private_chats(roomjid);
if (window) {
int num = wins_get_num(window);
ui_close_win(num);
}
GList* curr = privwins;
while (curr) {
ProfPrivateWin* privwin = curr->data;
privwin_room_left(privwin);
curr = g_list_next(curr);
}
g_list_free(privwins);
}
void
ui_room_destroyed(const char* const roomjid, const char* const reason, const char* const new_jid,
const char* const password)
{
ProfWin* window = (ProfWin*)wins_get_muc(roomjid);
GList* privwins = wins_get_private_chats(roomjid);
if (window == NULL) {
log_error("Received room destroy, but no window open for %s.", roomjid);
} else {
int num = wins_get_num(window);
ui_close_win(num);
ProfWin* console = wins_get_console();
if (reason) {
win_println(console, THEME_TYPING, "!", "<- Room destroyed: %s, reason: %s", roomjid, reason);
} else {
win_println(console, THEME_TYPING, "!", "<- Room destroyed: %s", roomjid);
}
if (new_jid) {
if (password) {
win_println(console, THEME_TYPING, "!", "Replacement room: %s, password: %s", new_jid, password);
} else {
win_println(console, THEME_TYPING, "!", "Replacement room: %s", new_jid);
}
}
}
GList* curr = privwins;
while (curr) {
ProfPrivateWin* privwin = curr->data;
privwin_room_destroyed(privwin);
curr = g_list_next(curr);
}
g_list_free(privwins);
}
void
ui_room_kicked(const char* const roomjid, const char* const actor, const char* const reason)
{
ProfWin* window = (ProfWin*)wins_get_muc(roomjid);
GList* privwins = wins_get_private_chats(roomjid);
if (window == NULL) {
log_error("Received kick, but no window open for %s.", roomjid);
} else {
int num = wins_get_num(window);
ui_close_win(num);
GString* message = g_string_new("Kicked from ");
g_string_append(message, roomjid);
if (actor) {
g_string_append(message, " by ");
g_string_append(message, actor);
}
if (reason) {
g_string_append(message, ", reason: ");
g_string_append(message, reason);
}
ProfWin* console = wins_get_console();
win_println(console, THEME_TYPING, "!", "<- %s", message->str);
g_string_free(message, TRUE);
}
GList* curr = privwins;
while (curr) {
ProfPrivateWin* privwin = curr->data;
privwin_room_kicked(privwin, actor, reason);
curr = g_list_next(curr);
}
g_list_free(privwins);
}
void
ui_room_banned(const char* const roomjid, const char* const actor, const char* const reason)
{
ProfWin* window = (ProfWin*)wins_get_muc(roomjid);
GList* privwins = wins_get_private_chats(roomjid);
if (window == NULL) {
log_error("Received ban, but no window open for %s.", roomjid);
} else {
int num = wins_get_num(window);
ui_close_win(num);
GString* message = g_string_new("Banned from ");
g_string_append(message, roomjid);
if (actor) {
g_string_append(message, " by ");
g_string_append(message, actor);
}
if (reason) {
g_string_append(message, ", reason: ");
g_string_append(message, reason);
}
ProfWin* console = wins_get_console();
win_println(console, THEME_TYPING, "!", "<- %s", message->str);
g_string_free(message, TRUE);
}
GList* curr = privwins;
while (curr) {
ProfPrivateWin* privwin = curr->data;
privwin_room_banned(privwin, actor, reason);
curr = g_list_next(curr);
}
g_list_free(privwins);
}
int
ui_win_unread(int index)
{
ProfWin* window = wins_get_by_num(index);
if (window) {
return win_unread(window);
} else {
return 0;
}
}
gboolean
ui_win_has_attention(int index)
{
gboolean ret = FALSE;
ProfWin* window = wins_get_by_num(index);
if (window) {
ret = win_has_attention(window);
}
return ret;
}
char*
ui_ask_password(gboolean confirm)
{
if (!confirm) {
status_bar_set_prompt("Enter password:");
} else {
status_bar_set_prompt("Confirm password:");
}
return inp_get_password();
}
char*
ui_get_line(void)
{
status_bar_draw();
return inp_get_line();
}
char*
ui_ask_pgp_passphrase(const char* hint, int prev_fail)
{
ProfWin* current = wins_get_current();
win_println(current, THEME_DEFAULT, "-", "");
if (prev_fail) {
win_println(current, THEME_DEFAULT, "!", "Incorrect passphrase");
}
if (hint) {
win_println(current, THEME_DEFAULT, "!", "Enter PGP key passphrase for %s", hint);
} else {
win_println(current, THEME_DEFAULT, "!", "Enter PGP key passphrase");
}
ui_update();
status_bar_set_prompt("Enter password:");
return inp_get_password();
}
void
ui_contact_offline(char* barejid, char* resource, char* status)
{
char* show_console = prefs_get_string(PREF_STATUSES_CONSOLE);
char* show_chat_win = prefs_get_string(PREF_STATUSES_CHAT);
Jid* jid = jid_create_from_bare_and_resource(barejid, resource);
PContact contact = roster_get_contact(barejid);
if (p_contact_subscription(contact)) {
if (strcmp(p_contact_subscription(contact), "none") != 0) {
// show in console if "all"
if (g_strcmp0(show_console, "all") == 0) {
cons_show_contact_offline(contact, resource, status);
// show in console of "online"
} else if (g_strcmp0(show_console, "online") == 0) {
cons_show_contact_offline(contact, resource, status);
}
// show in chat win if "all"
if (g_strcmp0(show_chat_win, "all") == 0) {
ProfChatWin* chatwin = wins_get_chat(barejid);
if (chatwin) {
chatwin_contact_offline(chatwin, resource, status);
}
// show in chat win if "online" and presence online
} else if (g_strcmp0(show_chat_win, "online") == 0) {
ProfChatWin* chatwin = wins_get_chat(barejid);
if (chatwin) {
chatwin_contact_offline(chatwin, resource, status);
}
}
}
}
ProfChatWin* chatwin = wins_get_chat(barejid);
if (chatwin && chatwin->resource_override && (g_strcmp0(resource, chatwin->resource_override) == 0)) {
FREE_SET_NULL(chatwin->resource_override);
}
g_free(show_console);
g_free(show_chat_win);
jid_destroy(jid);
}
void
ui_clear_win_title(void)
{
fputs("\e]0;\a", stdout);
fflush(stdout);
}
void
ui_goodbye_title(void)
{
fputs("\e]0;Thanks for using Profanity\a", stdout);
fflush(stdout);
}
static void
_ui_draw_term_title(void)
{
jabber_conn_status_t status = connection_get_status();
if (status == JABBER_CONNECTED) {
const char* const jid = connection_get_fulljid();
gint unread = wins_get_total_unread();
if (unread != 0) {
fprintf(stdout, "\e]0;Profanity (%d) - %s\a", unread, jid);
} else {
fprintf(stdout, "\e]0;Profanity - %s\a", jid);
}
} else {
fputs("\e]0;Profanity\a", stdout);
}
fflush(stdout);
}
void
ui_handle_room_configuration_form_error(const char* const roomjid, const char* const message)
{
ProfWin* window = NULL;
GString* message_str = g_string_new("");
if (roomjid) {
window = (ProfWin*)wins_get_muc(roomjid);
g_string_printf(message_str, "Could not get room configuration for %s", roomjid);
} else {
window = wins_get_console();
g_string_printf(message_str, "Could not get room configuration");
}
if (message) {
g_string_append(message_str, ": ");
g_string_append(message_str, message);
}
win_println(window, THEME_ERROR, "-", "%s", message_str->str);
g_string_free(message_str, TRUE);
}
void
ui_handle_room_config_submit_result(const char* const roomjid)
{
if (roomjid) {
ProfWin* form_window = NULL;
ProfWin* muc_window = (ProfWin*)wins_get_muc(roomjid);
GString* form_recipient = g_string_new(roomjid);
g_string_append(form_recipient, " config");
form_window = (ProfWin*)wins_get_conf(form_recipient->str);
g_string_free(form_recipient, TRUE);
if (form_window) {
int num = wins_get_num(form_window);
wins_close_by_num(num);
}
if (muc_window) {
ui_focus_win((ProfWin*)muc_window);
win_println(muc_window, THEME_ROOMINFO, "!", "Room configuration successful");
} else {
ProfWin* console = wins_get_console();
ui_focus_win(console);
cons_show("Room configuration successful: %s", roomjid);
}
} else {
cons_show("Room configuration successful");
}
}
void
ui_handle_room_config_submit_result_error(const char* const roomjid, const char* const message)
{
ProfWin* console = wins_get_console();
if (roomjid) {
ProfWin* muc_window = NULL;
ProfWin* form_window = NULL;
muc_window = (ProfWin*)wins_get_muc(roomjid);
GString* form_recipient = g_string_new(roomjid);
g_string_append(form_recipient, " config");
form_window = (ProfWin*)wins_get_conf(form_recipient->str);
g_string_free(form_recipient, TRUE);
if (form_window) {
if (message) {
win_println(form_window, THEME_ERROR, "!", "Configuration error: %s", message);
} else {
win_println(form_window, THEME_ERROR, "!", "Configuration error");
}
} else if (muc_window) {
if (message) {
win_println(muc_window, THEME_ERROR, "!", "Configuration error: %s", message);
} else {
win_println(muc_window, THEME_ERROR, "!", "Configuration error");
}
} else {
if (message) {
win_println(console, THEME_ERROR, "!", "Configuration error for %s: %s", roomjid, message);
} else {
win_println(console, THEME_ERROR, "!", "Configuration error for %s", roomjid);
}
}
} else {
win_println(console, THEME_ERROR, "!", "Configuration error");
}
}
void
ui_show_lines(ProfWin* window, gchar** lines)
{
if (lines) {
for (int i = 0; lines[i] != NULL; i++) {
win_println(window, THEME_DEFAULT, "-", "%s", lines[i]);
}
}
}
void
ui_show_roster(void)
{
ProfWin* window = wins_get_console();
if (window && !win_has_active_subwin(window)) {
wins_show_subwin(window);
rosterwin_roster();
}
}
void
ui_hide_roster(void)
{
ProfWin* window = wins_get_console();
if (window && win_has_active_subwin(window)) {
wins_hide_subwin(window);
}
}
void
ui_handle_software_version_error(const char* const roomjid, const char* const message)
{
GString* message_str = g_string_new("");
ProfWin* window = wins_get_console();
g_string_printf(message_str, "Could not get software version");
if (message) {
g_string_append(message_str, ": ");
g_string_append(message_str, message);
}
win_println(window, THEME_ERROR, "-", "%s", message_str->str);
g_string_free(message_str, TRUE);
}
void
ui_show_software_version(const char* const jid, const char* const presence,
const char* const name, const char* const version, const char* const os)
{
Jid* jidp = jid_create(jid);
ProfWin* window = NULL;
ProfWin* chatwin = (ProfWin*)wins_get_chat(jidp->barejid);
ProfWin* mucwin = (ProfWin*)wins_get_muc(jidp->barejid);
ProfWin* privwin = (ProfWin*)wins_get_private(jidp->fulljid);
ProfWin* console = wins_get_console();
jid_destroy(jidp);
if (chatwin) {
if (wins_is_current(chatwin)) {
window = chatwin;
} else {
window = console;
}
} else if (privwin) {
if (wins_is_current(privwin)) {
window = privwin;
} else {
window = console;
}
} else if (mucwin) {
if (wins_is_current(mucwin)) {
window = mucwin;
} else {
window = console;
}
} else {
window = console;
}
if (name || version || os) {
win_println(window, THEME_DEFAULT, "-", "");
theme_item_t presence_colour = theme_main_presence_attrs(presence);
win_print(window, presence_colour, "-", "%s", jid);
win_appendln(window, THEME_DEFAULT, ":");
}
if (name) {
win_println(window, THEME_DEFAULT, "-", "Name : %s", name);
}
if (version) {
win_println(window, THEME_DEFAULT, "-", "Version : %s", version);
}
if (os) {
win_println(window, THEME_DEFAULT, "-", "OS : %s", os);
}
}