diff --git a/Makefile b/Makefile index f487696..61aef89 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ endif # Check if LLVM Address Sanitizer is enabled ASAN_ENABLED := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi) ifneq ($(ASAN_ENABLED), disabled) - CFLAGS += -fsanitize=address -fno-omit-frame-pointer -mllvm -asan-use-private-alias=1 -Wno-unused-command-line-argument + CFLAGS += -fsanitize=address -fno-omit-frame-pointer endif # Check on wich system we are running diff --git a/README.md b/README.md index a92ca76..58c7ea7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client. -[![Toxic Screenshot](https://i.imgur.com/5S577z6.png "Toxic Audio Conference")](https://i.imgur.com/5S577z6.png) +[![Toxic Screenshot](https://i.imgur.com/TwYA8L0.png "Toxic Home Screen")](https://i.imgur.com/TwYA8L0.png) ## Installation [See the install instructions](/INSTALL.md) diff --git a/apidoc/python/source/conf.py b/apidoc/python/source/conf.py index 2bfc8ea..eea9233 100644 --- a/apidoc/python/source/conf.py +++ b/apidoc/python/source/conf.py @@ -55,9 +55,9 @@ author = 'Jakob Kreuze' # built documents. # # The short X.Y version. -version = '0.9.1' +version = '0.10.0' # The full version, including alpha/beta/rc tags. -release = '0.9.1' +release = '0.10.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/cfg/global_vars.mk b/cfg/global_vars.mk index 1b40fbe..0f9d1d4 100644 --- a/cfg/global_vars.mk +++ b/cfg/global_vars.mk @@ -1,5 +1,5 @@ # Version -TOXIC_VERSION = 0.9.1 +TOXIC_VERSION = 0.10.0 REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") ifneq (, $(findstring error, $(REV))) VERSION = $(TOXIC_VERSION) diff --git a/src/chat.c b/src/chat.c index 4a087c9..75b5575 100644 --- a/src/chat.c +++ b/src/chat.c @@ -745,17 +745,17 @@ static void chat_onConferenceInvite(ToxWindow *self, Tox *m, int32_t friendnumbe Friends.list[friendnumber].conference_invite.length = length; Friends.list[friendnumber].conference_invite.type = type; - sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL); - char name[TOX_MAX_NAME_LENGTH]; get_nick_truncate(m, name, friendnumber); const char *description = type == TOX_CONFERENCE_TYPE_AV ? "an audio conference" : "a conference"; if (self->active_box != -1) { - box_silent_notify2(self, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box, "invites you to join %s", description); + box_notify2(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, self->active_box, + "invites you to join %s", description); } else { - box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join %s", description); + box_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, &self->active_box, name, + "invites you to join %s", description); } line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to %s.", name, description); @@ -1181,7 +1181,7 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) } wclear(ctx->linewin); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); + wmove(self->window, y2, 0); reset_buf(ctx); } @@ -1194,7 +1194,8 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) static void chat_onDraw(ToxWindow *self, Tox *m) { - int x2, y2; + int x2; + int y2; getmaxyx(self->window, y2, x2); if (y2 <= 0 || x2 <= 0) { @@ -1209,15 +1210,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m) wclear(ctx->linewin); - curs_set(1); - if (ctx->len > 0) { - mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); + mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]); } + curs_set(1); + /* Draw status bar */ StatusBar *statusbar = self->stb; - mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2); wmove(statusbar->topline, 0, 0); /* Draw name, status and note in statusbar */ @@ -1227,38 +1227,63 @@ static void chat_onDraw(ToxWindow *self, Tox *m) switch (status) { case TOX_USER_STATUS_NONE: - colour = GREEN; + colour = GREEN_BLUE; break; case TOX_USER_STATUS_AWAY: - colour = YELLOW; + colour = YELLOW_BLUE; break; case TOX_USER_STATUS_BUSY: - colour = RED; + colour = RED_BLUE; break; } + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, " ["); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); - wprintw(statusbar->topline, " %s", ONLINE_CHAR); + wprintw(statusbar->topline, "%s", ONLINE_CHAR); wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); - if (Friends.list[self->num].is_typing) { - wattron(statusbar->topline, COLOR_PAIR(YELLOW)); + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, "] "); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + pthread_mutex_lock(&Winthread.lock); + const bool is_typing = Friends.list[self->num].is_typing; + pthread_mutex_unlock(&Winthread.lock); + + if (is_typing) { + wattron(statusbar->topline, A_BOLD | COLOR_PAIR(YELLOW_BLUE)); + } else { + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); } - wattron(statusbar->topline, A_BOLD); - wprintw(statusbar->topline, " %s ", statusbar->nick); - wattroff(statusbar->topline, A_BOLD); + wprintw(statusbar->topline, "%s", statusbar->nick); - if (Friends.list[self->num].is_typing) { - wattroff(statusbar->topline, COLOR_PAIR(YELLOW)); + if (is_typing) { + wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(YELLOW_BLUE)); + } else { + wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(WHITE_BLUE)); } } else { - wprintw(statusbar->topline, " %s", OFFLINE_CHAR); - wattron(statusbar->topline, A_BOLD); - wprintw(statusbar->topline, " %s ", statusbar->nick); - wattroff(statusbar->topline, A_BOLD); + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, " ["); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + wprintw(statusbar->topline, "%s", OFFLINE_CHAR); + wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, "] "); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + wprintw(statusbar->topline, "%s", statusbar->nick); + wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); } /* Reset statusbar->statusmsg on window resize */ @@ -1287,30 +1312,51 @@ static void chat_onDraw(ToxWindow *self, Tox *m) } if (statusbar->statusmsg[0]) { - wprintw(statusbar->topline, ": %s ", statusbar->statusmsg); + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, " | "); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + wprintw(statusbar->topline, "%s ", statusbar->statusmsg); + } else { + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); } - wclrtoeol(statusbar->topline); + int s_y; + int s_x; + getyx(statusbar->topline, s_y, s_x); + + mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x - (KEY_IDENT_DIGITS * 2) - 3); + wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3); + + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wprintw(statusbar->topline, "{"); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); - size_t i; + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); - for (i = 0; i < KEY_IDENT_DIGITS; ++i) { + for (size_t i = 0; i < KEY_IDENT_DIGITS; ++i) { wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff); } - wprintw(statusbar->topline, "}\n"); + wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); - mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, "} "); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); - int y, x; + int y; + int x; getyx(self->window, y, x); UNUSED_VAR(x); int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); - wmove(self->window, y + 1, new_x); + wmove(self->window, y, new_x); + + draw_window_bar(self); wnoutrefresh(self->window); @@ -1357,7 +1403,9 @@ static void chat_init_log(ToxWindow *self, Tox *m, const char *self_nick) static void chat_onInit(ToxWindow *self, Tox *m) { curs_set(1); - int x2, y2; + + int x2; + int y2; getmaxyx(self->window, y2, x2); if (y2 <= 0 || x2 <= 0) { @@ -1390,9 +1438,10 @@ static void chat_onInit(ToxWindow *self, Tox *m) /* Init subwindows */ ChatContext *ctx = self->chatwin; - statusbar->topline = subwin(self->window, 2, x2, 0, 0); - ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); - ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); + statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0); + ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); + self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); + ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0); ctx->hst = calloc(1, sizeof(struct history)); ctx->log = calloc(1, sizeof(struct chatlog)); diff --git a/src/conference.c b/src/conference.c index 8c2198e..ebb1ab1 100644 --- a/src/conference.c +++ b/src/conference.c @@ -307,9 +307,9 @@ void redraw_conference_win(ToxWindow *self) refresh(); clear(); - int x2, y2; - getmaxyx(stdscr, y2, x2); - y2 -= 2; + int x2; + int y2; + getmaxyx(self->window, y2, x2); if (y2 <= 0 || x2 <= 0) { return; @@ -322,20 +322,22 @@ void redraw_conference_win(ToxWindow *self) delwin(ctx->linewin); delwin(ctx->history); + delwin(self->window_bar); delwin(self->window); self->window = newwin(y2, x2, 0, 0); ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); + self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); if (self->show_peerlist) { - ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0); - ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); + ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0); + ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); } else { - ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); + ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); } scrollok(ctx->history, 0); - + wmove(self->window, y2 - CURS_Y_OFFSET, 0); } static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum, @@ -362,12 +364,12 @@ static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t con /* Only play sound if mentioned by someone else */ if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) { - sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL); - if (self->active_box != -1) { - box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg); + box_notify2(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_message, + self->active_box, "%s %s", nick, msg); } else { - box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg); + box_notify(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_message, + &self->active_box, self->name, "%s %s", nick, msg); } nick_clr = RED; @@ -963,7 +965,7 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) } wclear(ctx->linewin); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); + wmove(self->window, y2, 0); reset_buf(ctx); } @@ -1049,15 +1051,16 @@ static void conference_onDraw(ToxWindow *self, Tox *m) curs_set(1); if (ctx->len > 0) { - mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); + mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]); } wclear(ctx->sidebar); - mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); if (self->show_peerlist) { + wattron(ctx->sidebar, COLOR_PAIR(BLUE)); mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT); mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE); + wattroff(ctx->sidebar, COLOR_PAIR(BLUE)); pthread_mutex_lock(&Winthread.lock); const uint32_t num_peers = chat->num_peers; @@ -1117,8 +1120,11 @@ static void conference_onDraw(ToxWindow *self, Tox *m) wattroff(ctx->sidebar, A_BOLD); ++line; + wattron(ctx->sidebar, COLOR_PAIR(BLUE)); mvwaddch(ctx->sidebar, line, 0, ACS_LTEE); mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1); + wattroff(ctx->sidebar, COLOR_PAIR(BLUE)); + ++line; for (uint32_t i = 0; @@ -1135,7 +1141,9 @@ static void conference_onDraw(ToxWindow *self, Tox *m) UNUSED_VAR(x); int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); - wmove(self->window, y + 1, new_x); + wmove(self->window, y, new_x); + + draw_window_bar(self); wnoutrefresh(self->window); @@ -1155,9 +1163,10 @@ static void conference_onInit(ToxWindow *self, Tox *m) ChatContext *ctx = self->chatwin; - ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0); + ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0); + self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); - ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); + ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); ctx->hst = calloc(1, sizeof(struct history)); ctx->log = calloc(1, sizeof(struct chatlog)); diff --git a/src/friendlist.c b/src/friendlist.c index 8d6180d..36326d5 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -1022,7 +1022,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2) wmove(self->window, y2 - 1, 1); wattron(self->window, A_BOLD); - wprintw(self->window, "Key: "); + wprintw(self->window, "Public key: "); wattroff(self->window, A_BOLD); int i; @@ -1057,6 +1057,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) wprintw(self->window, "key for help\n\n"); wattroff(self->window, COLOR_PAIR(CYAN)); + draw_window_bar(self); + if (blocklist_view == 1) { blocklist_onDraw(self, m, y2, x2); return; @@ -1247,7 +1249,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) wmove(self->window, y2 - 1, 1); wattron(self->window, A_BOLD); - wprintw(self->window, "Key: "); + wprintw(self->window, "Public key: "); wattroff(self->window, A_BOLD); int i; @@ -1265,6 +1267,21 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) } } +void friendlist_onInit(ToxWindow *self, Tox *m) +{ + UNUSED_VAR(m); + + int x2; + int y2; + getmaxyx(self->window, y2, x2); + + if (y2 <= 0 || x2 <= 0) { + exit_toxic_err("failed in friendlist_onInit", FATALERR_CURSES); + } + + self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - 2, 0); +} + void disable_chatwin(uint32_t f_num) { Friends.list[f_num].chatwin = -1; @@ -1343,6 +1360,7 @@ ToxWindow *new_friendlist(void) ret->type = WINDOW_TYPE_FRIEND_LIST; + ret->onInit = &friendlist_onInit; ret->onKey = &friendlist_onKey; ret->onDraw = &friendlist_onDraw; ret->onFriendAdded = &friendlist_onFriendAdded; diff --git a/src/friendlist.h b/src/friendlist.h index 4068dae..dd57bcb 100644 --- a/src/friendlist.h +++ b/src/friendlist.h @@ -82,6 +82,7 @@ typedef struct { } FriendsList; ToxWindow *new_friendlist(void); +void friendlist_onInit(ToxWindow *self, Tox *m); void disable_chatwin(uint32_t f_num); int get_friendnum(uint8_t *name); int load_blocklist(char *data); diff --git a/src/line_info.c b/src/line_info.c index 701411a..832555a 100644 --- a/src/line_info.c +++ b/src/line_info.c @@ -63,10 +63,10 @@ void line_info_reset_start(ToxWindow *self, struct history *hst) getmaxyx(self->window, y2, x2); UNUSED_VAR(x2); - int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? 2 : 0; - int max_y = y2 - CHATBOX_HEIGHT - top_offst; + int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; + int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst; - int curlines = 0; + uint16_t curlines = 0; do { curlines += line->format_lines; @@ -74,6 +74,8 @@ void line_info_reset_start(ToxWindow *self, struct history *hst) } while (line->prev && curlines + line->format_lines <= max_y); hst->line_start = line; + + self->scroll_pause = false; } void line_info_cleanup(struct history *hst) @@ -131,20 +133,32 @@ static struct line_info *line_info_ret_queue(struct history *hst) /* Prints a maximum of `n` chars from `s` to `win`. * - * Return true if the string contains a newline or tab byte. + * Return 1 if the string contains a newline byte. + * Return 0 if string does not contain a newline byte. + * Return -1 if printing was aborted. */ -static bool print_n_chars(WINDOW *win, const char *s, size_t n) +static int print_n_chars(WINDOW *win, const char *s, size_t n, int max_y) { bool newline = false; char ch; for (size_t i = 0; i < n && (ch = s[i]); ++i) { - if (win) { - wprintw(win, "%c", ch); - } - if (ch == '\n') { newline = true; + + int x; + int y; + UNUSED_VAR(x); + getyx(win, y, x); + + // make sure cursor will wrap correctly after newline to prevent display bugs + if (y + 1 >= max_y) { + return -1; + } + } + + if (win) { + wprintw(win, "%c", ch); } } @@ -201,24 +215,42 @@ static unsigned int newline_count(const char *s) * * If `win` is null nothing will be printed to the window. This is useful to set the * `format_lines` field on initialization. + * + * Return 0 on success. + * Return -1 if not all characters in line's message were printed to screen. */ -static void print_wrap(WINDOW *win, struct line_info *line, int max_x) +static int print_wrap(WINDOW *win, struct line_info *line, int max_x, int max_y) { + int x; + int y; + UNUSED_VAR(y); + const char *msg = line->msg; uint16_t length = line->msg_len; uint16_t lines = 0; const int x_start = line->len - line->msg_len - 1; // manually keep track of x position because ncurses sucks int x_limit = max_x - x_start; - if (x_limit <= 0) { + if (x_limit <= 1) { fprintf(stderr, "Warning: x_limit <= 0 in print_wrap(): %d\n", x_limit); - return; + return -1; } while (msg) { + getyx(win, y, x); + + // next line would print past window limit so we abort; we don't want to update format_lines + if (x > x_start) { + return -1; + } + if (length < x_limit) { - if (print_n_chars(win, msg, length)) { + int p_ret = print_n_chars(win, msg, length, max_y); + + if (p_ret == 1) { lines += newline_count(msg); + } else if (p_ret == -1) { + return -1; } ++lines; @@ -228,8 +260,11 @@ static void print_wrap(WINDOW *win, struct line_info *line, int max_x) int newline_idx = newline_index(msg, x_limit - 1); if (newline_idx >= 0) { - print_n_chars(win, msg, newline_idx + 1); - msg += newline_idx + 1; + if (print_n_chars(win, msg, newline_idx + 1, max_y) == -1) { + return -1; + } + + msg += (newline_idx + 1); length -= (newline_idx + 1); x_limit = max_x; // if we find a newline we stop adding column padding for rest of message ++lines; @@ -239,7 +274,10 @@ static void print_wrap(WINDOW *win, struct line_info *line, int max_x) int space_idx = rspace_index(msg, x_limit - 1); if (space_idx >= 1) { - print_n_chars(win, msg, space_idx); + if (print_n_chars(win, msg, space_idx, max_y) == -1) { + return -1; + } + msg += space_idx + 1; length -= (space_idx + 1); @@ -247,7 +285,10 @@ static void print_wrap(WINDOW *win, struct line_info *line, int max_x) waddch(win, '\n'); } } else { - print_n_chars(win, msg, x_limit); + if (print_n_chars(win, msg, x_limit, max_y) == -1) { + return -1; + } + msg += x_limit; length -= x_limit; } @@ -263,10 +304,6 @@ static void print_wrap(WINDOW *win, struct line_info *line, int max_x) } if (win && line->noread_flag) { - int x; - int y; - UNUSED_VAR(y); - getyx(win, y, x); if (x >= max_x - 1 || x == x_start) { @@ -279,17 +316,22 @@ static void print_wrap(WINDOW *win, struct line_info *line, int max_x) } line->format_lines = lines; + + return 0; } -static void line_info_init_line(WINDOW *win, struct line_info *line) +static void line_info_init_line(ToxWindow *self, struct line_info *line) { - int max_y; - int max_x; - UNUSED_VAR(max_y); + int y2; + int x2; + UNUSED_VAR(y2); - getmaxyx(win, max_y, max_x); + getmaxyx(self->window, y2, x2); - print_wrap(NULL, line, max_x); + const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT; + const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2; + + print_wrap(NULL, line, max_x, max_y); } /* creates new line_info line and puts it in the queue. @@ -358,7 +400,7 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const break; case PROMPT: - ++len; + len += 2; break; default: @@ -398,7 +440,7 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const new_line->noread_flag = false; new_line->timestamp = get_unix_time(); - line_info_init_line(self->chatwin->history, new_line); + line_info_init_line(self, new_line); hst->queue[hst->queue_size++] = new_line; @@ -424,29 +466,8 @@ static void line_info_check_queue(ToxWindow *self) hst->line_end = line; hst->line_end->id = line->id; - int y; - int y2; - int x; - int x2; - getmaxyx(self->window, y2, x2); - getyx(self->chatwin->history, y, x); - - UNUSED_VAR(x); - - if (x2 <= SIDEBAR_WIDTH) { - return; - } - - int lines = line->format_lines; - int max_y = y2 - CHATBOX_HEIGHT; - - /* move line_start forward proportionate to the number of new lines */ - if (y + lines > max_y) { - while (lines > 0 && hst->line_start->next) { - lines -= hst->line_start->next->format_lines; - hst->line_start = hst->line_start->next; - ++hst->start_id; - } + if (!self->scroll_pause) { + line_info_reset_start(self, hst); } } @@ -482,16 +503,31 @@ void line_info_print(ToxWindow *self) if (self->type == WINDOW_TYPE_CONFERENCE) { wmove(win, 0, 0); } else { - wmove(win, 2, 0); + wmove(win, TOP_BAR_HEIGHT, 0); } struct line_info *line = hst->line_start->next; + if (!line) { + return; + } + + const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT; const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2; + uint16_t numlines = line->format_lines; + int print_ret = 0; - int numlines = 0; + while (line && numlines++ <= max_y && print_ret == 0) { + int y; + int x; + UNUSED_VAR(y); + + getyx(win, y, x); + + if (x > 0) { // Prevents us from printing off the screen + break; + } - while (line && numlines++ <= y2) { uint8_t type = line->type; switch (type) { @@ -529,7 +565,7 @@ void line_info_print(ToxWindow *self) wattron(win, COLOR_PAIR(RED)); } - print_wrap(win, line, max_x); + print_ret = print_wrap(win, line, max_x, max_y); if (line->msg[0] == '>') { wattroff(win, COLOR_PAIR(GREEN)); @@ -559,7 +595,7 @@ void line_info_print(ToxWindow *self) wattron(win, COLOR_PAIR(YELLOW)); wprintw(win, "%s %s ", user_settings->line_normal, line->name1); - print_wrap(win, line, max_x); + print_ret = print_wrap(win, line, max_x, max_y); wattroff(win, COLOR_PAIR(YELLOW)); if (type == OUT_ACTION && !line->read_flag) { @@ -586,7 +622,7 @@ void line_info_print(ToxWindow *self) wattron(win, COLOR_PAIR(line->colour)); } - print_wrap(win, line, max_x); + print_ret = print_wrap(win, line, max_x, max_y); waddch(win, '\n'); if (line->bold) { @@ -605,7 +641,7 @@ void line_info_print(ToxWindow *self) wattroff(win, COLOR_PAIR(GREEN)); if (line->msg[0]) { - print_wrap(win, line, max_x); + print_ret = print_wrap(win, line, max_x, max_y); } waddch(win, '\n'); @@ -623,7 +659,7 @@ void line_info_print(ToxWindow *self) wprintw(win, "%s ", line->name1); wattroff(win, A_BOLD); - print_wrap(win, line, max_x); + print_ret = print_wrap(win, line, max_x, max_y); waddch(win, '\n'); wattroff(win, COLOR_PAIR(line->colour)); @@ -642,7 +678,7 @@ void line_info_print(ToxWindow *self) wprintw(win, "%s ", line->name1); wattroff(win, A_BOLD); - print_wrap(win, line, max_x); + print_ret = print_wrap(win, line, max_x, max_y); waddch(win, '\n'); wattroff(win, COLOR_PAIR(line->colour)); @@ -660,7 +696,7 @@ void line_info_print(ToxWindow *self) wprintw(win, "%s", line->name1); wattroff(win, A_BOLD); - print_wrap(win, line, max_x); + print_ret = print_wrap(win, line, max_x, max_y); wattron(win, A_BOLD); wprintw(win, "%s\n", line->name2); @@ -679,6 +715,38 @@ void line_info_print(ToxWindow *self) } } +/* + * Return true if all lines starting from `line` can fit on the screen. + */ +static bool line_info_screen_fit(ToxWindow *self, struct line_info *line) +{ + if (!line) { + return true; + } + + int x2; + int y2; + getmaxyx(self->chatwin->history, y2, x2); + + UNUSED_VAR(x2); + + const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; + const int max_y = y2 - top_offset; + + uint16_t lines = line->format_lines; + + while (line) { + if (lines > max_y) { + return false; + } + + lines += line->format_lines; + line = line->next; + } + + return true; +} + /* puts msg in specified line_info msg buffer */ void line_info_set(ToxWindow *self, uint32_t id, char *msg) { @@ -697,49 +765,74 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg) } } -static void line_info_scroll_up(struct history *hst) +static void line_info_scroll_up(ToxWindow *self, struct history *hst) { if (hst->line_start->prev) { hst->line_start = hst->line_start->prev; - } else { - sound_notify(NULL, notif_error, NT_ALWAYS, NULL); + self->scroll_pause = true; } } -static void line_info_scroll_down(struct history *hst) +static void line_info_scroll_down(ToxWindow *self, struct history *hst) { - if (hst->line_start->next) { - hst->line_start = hst->line_start->next; + struct line_info *next = hst->line_start->next; + + if (next && self->scroll_pause) { + if (line_info_screen_fit(self, next->next)) { + line_info_reset_start(self, hst); + } else { + hst->line_start = next; + } } else { - sound_notify(NULL, notif_error, NT_ALWAYS, NULL); + line_info_reset_start(self, hst); } } static void line_info_page_up(ToxWindow *self, struct history *hst) { - int x2, y2; + int x2; + int y2; getmaxyx(self->window, y2, x2); UNUSED_VAR(x2); - size_t jump_dist = y2 / 2; + const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; + const int max_y = y2 - top_offset; + size_t jump_dist = max_y / 2; for (size_t i = 0; i < jump_dist && hst->line_start->prev; ++i) { hst->line_start = hst->line_start->prev; } + + self->scroll_pause = true; } static void line_info_page_down(ToxWindow *self, struct history *hst) { - int x2, y2; - getmaxyx(self->window, y2, x2); + if (!self->scroll_pause) { + return; + } + + int x2; + int y2; + getmaxyx(self->chatwin->history, y2, x2); UNUSED_VAR(x2); - size_t jump_dist = y2 / 2; + const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0; + const int max_y = y2 - top_offset; + size_t jump_dist = max_y / 2; - for (size_t i = 0; i < jump_dist && hst->line_start->next; ++i) { - hst->line_start = hst->line_start->next; + struct line_info *next = hst->line_start->next; + + for (size_t i = 0; i < jump_dist && next; ++i) { + if (line_info_screen_fit(self, next->next)) { + line_info_reset_start(self, hst); + break; + } + + hst->line_start = next; + next = hst->line_start->next; } } @@ -753,9 +846,9 @@ bool line_info_onKey(ToxWindow *self, wint_t key) } else if (key == user_settings->key_half_page_down) { line_info_page_down(self, hst); } else if (key == user_settings->key_scroll_line_up) { - line_info_scroll_up(hst); + line_info_scroll_up(self, hst); } else if (key == user_settings->key_scroll_line_down) { - line_info_scroll_down(hst); + line_info_scroll_down(self, hst); } else if (key == user_settings->key_page_bottom) { line_info_reset_start(self, hst); } else { diff --git a/src/log.c b/src/log.c index 540a389..ac63d86 100644 --- a/src/log.c +++ b/src/log.c @@ -306,7 +306,7 @@ int load_chat_history(ToxWindow *self, struct chatlog *log) line = strtok_r(NULL, "\n", &tmp); } - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, YELLOW, "---"); free(buf); diff --git a/src/misc_tools.c b/src/misc_tools.c index 5fa5cb5..ef021c1 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -71,16 +71,19 @@ int timed_out(time_t timestamp, time_t timeout) return timestamp + timeout <= get_unix_time(); } -/* Sleeps the caller's thread for `usec` microseconds */ +/* Attempts to sleep the caller's thread for `usec` microseconds */ void sleep_thread(long int usec) { struct timespec req; + struct timespec rem; req.tv_sec = 0; req.tv_nsec = usec * 1000L; - if (nanosleep(&req, NULL) == -1) { - fprintf(stderr, "nanosleep() returned -1\n"); + if (nanosleep(&req, &rem) == -1) { + if (nanosleep(&rem, NULL) == -1) { + fprintf(stderr, "nanosleep() returned -1\n"); + } } } diff --git a/src/misc_tools.h b/src/misc_tools.h index 2d3e49b..de2902e 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -108,7 +108,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); /* Returns 1 if connection has timed out, 0 otherwise */ int timed_out(time_t timestamp, time_t timeout); -/* Sleeps the caller's thread for `usec` microseconds */ +/* Attempts to sleep the caller's thread for `usec` microseconds */ void sleep_thread(long int usec); /* Colours the window tab according to type. Beeps if is_beep is true */ diff --git a/src/prompt.c b/src/prompt.c index 7f2b913..ae3f21d 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -292,7 +292,7 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) } wclear(ctx->linewin); - wmove(self->window, y2 - CURS_Y_OFFSET, 0); + wmove(self->window, y2, 0); reset_buf(ctx); } @@ -301,7 +301,8 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) static void prompt_onDraw(ToxWindow *self, Tox *m) { - int x2, y2; + int x2; + int y2; getmaxyx(self->window, y2, x2); if (y2 <= 0 || x2 <= 0) { @@ -316,64 +317,88 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) wclear(ctx->linewin); - curs_set(1); - if (ctx->len > 0) { - mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); + mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]); } + mvwhline(ctx->linewin, 0, ctx->len, ' ', x2 - ctx->len); + + curs_set(1); + StatusBar *statusbar = self->stb; - mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2); wmove(statusbar->topline, 0, 0); pthread_mutex_lock(&Winthread.lock); Tox_Connection connection = statusbar->connection; + Tox_User_Status status = statusbar->status; pthread_mutex_unlock(&Winthread.lock); if (connection != TOX_CONNECTION_NONE) { int colour = MAGENTA; const char *status_text = "ERROR"; - pthread_mutex_lock(&Winthread.lock); - Tox_User_Status status = statusbar->status; - pthread_mutex_unlock(&Winthread.lock); - switch (status) { case TOX_USER_STATUS_NONE: status_text = "Online"; - colour = GREEN; + colour = GREEN_BLUE; break; case TOX_USER_STATUS_AWAY: status_text = "Away"; - colour = YELLOW; + colour = YELLOW_BLUE; break; case TOX_USER_STATUS_BUSY: status_text = "Busy"; - colour = RED; + colour = RED_BLUE; break; } - wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); - wprintw(statusbar->topline, " [%s]", status_text); - wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, " ["); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + wattron(statusbar->topline, A_BOLD | COLOR_PAIR(colour)); + wprintw(statusbar->topline, "%s", status_text); + wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(colour)); + + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, "]"); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); - wattron(statusbar->topline, A_BOLD); pthread_mutex_lock(&Winthread.lock); wprintw(statusbar->topline, " %s", statusbar->nick); pthread_mutex_unlock(&Winthread.lock); - wattroff(statusbar->topline, A_BOLD); } else { - wprintw(statusbar->topline, " [Offline]"); - wattron(statusbar->topline, A_BOLD); + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, " ["); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + wprintw(statusbar->topline, "Offline"); + wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, "]"); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + pthread_mutex_lock(&Winthread.lock); wprintw(statusbar->topline, " %s", statusbar->nick); pthread_mutex_unlock(&Winthread.lock); - wattroff(statusbar->topline, A_BOLD); } + int s_y; + int s_x; + getyx(statusbar->topline, s_y, s_x); + + mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x); + wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + /* Reset statusbar->statusmsg on window resize */ if (x2 != self->x) { char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; @@ -401,20 +426,27 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) } if (statusbar->statusmsg[0]) { - wprintw(statusbar->topline, " : %s", statusbar->statusmsg); + wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + wprintw(statusbar->topline, " | "); + wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); + + wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); + wprintw(statusbar->topline, "%s", statusbar->statusmsg); + wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); } pthread_mutex_unlock(&Winthread.lock); - mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); - - int y, x; + int y; + int x; getyx(self->window, y, x); UNUSED_VAR(x); int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); - wmove(self->window, y + 1, new_x); + wmove(self->window, y, new_x); + + draw_window_bar(self); wnoutrefresh(self->window); @@ -535,7 +567,7 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run) prompt_update_nick(prompt, nick); /* Init statusbar subwindow */ - statusbar->topline = subwin(self->window, 2, x2, 0, 0); + statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0); } static void print_welcome_msg(ToxWindow *self) @@ -576,7 +608,9 @@ static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name) static void prompt_onInit(ToxWindow *self, Tox *m) { curs_set(1); - int y2, x2; + + int y2; + int x2; getmaxyx(self->window, y2, x2); if (y2 <= 0 || x2 <= 0) { @@ -584,8 +618,10 @@ static void prompt_onInit(ToxWindow *self, Tox *m) } ChatContext *ctx = self->chatwin; - ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); - ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); + + ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); + self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); + ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0); ctx->log = calloc(1, sizeof(struct chatlog)); ctx->hst = calloc(1, sizeof(struct history)); diff --git a/src/toxic.c b/src/toxic.c index 0d88527..fa41c32 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -256,7 +256,7 @@ static void init_term(void) keypad(stdscr, 1); noecho(); nonl(); - timeout(100); + timeout(50); if (has_colors()) { short bg_color = COLOR_BLACK; @@ -268,15 +268,23 @@ static void init_term(void) } } - init_pair(0, COLOR_WHITE, COLOR_BLACK); - init_pair(1, COLOR_GREEN, bg_color); - init_pair(2, COLOR_CYAN, bg_color); - init_pair(3, COLOR_RED, bg_color); - init_pair(4, COLOR_BLUE, bg_color); - init_pair(5, COLOR_YELLOW, bg_color); - init_pair(6, COLOR_MAGENTA, bg_color); - init_pair(7, COLOR_BLACK, COLOR_BLACK); - init_pair(8, COLOR_BLACK, COLOR_WHITE); + init_pair(WHITE, COLOR_WHITE, COLOR_BLACK); + init_pair(GREEN, COLOR_GREEN, bg_color); + init_pair(CYAN, COLOR_CYAN, bg_color); + init_pair(RED, COLOR_RED, bg_color); + init_pair(BLUE, COLOR_BLUE, bg_color); + init_pair(YELLOW, COLOR_YELLOW, bg_color); + init_pair(MAGENTA, COLOR_MAGENTA, bg_color); + init_pair(BLACK, COLOR_BLACK, COLOR_BLACK); + init_pair(BLUE_BLACK, COLOR_BLUE, COLOR_BLACK); + init_pair(BLACK_WHITE, COLOR_BLACK, COLOR_WHITE); + init_pair(WHITE_BLUE, COLOR_WHITE, COLOR_BLUE); + init_pair(CYAN_BLUE, COLOR_CYAN, COLOR_BLUE); + init_pair(GREEN_BLUE, COLOR_GREEN, COLOR_BLUE); + init_pair(PURPLE_BLUE, COLOR_MAGENTA, COLOR_BLUE); + init_pair(BLACK_BLUE, COLOR_BLACK, COLOR_BLUE); + init_pair(YELLOW_BLUE, COLOR_YELLOW, COLOR_BLUE); + init_pair(RED_BLUE, COLOR_RED, COLOR_BLUE); } refresh(); @@ -1421,6 +1429,7 @@ int main(int argc, char **argv) prompt = init_windows(m); prompt_init_statusbar(prompt, m, !datafile_exists); load_conferences(m); + set_active_window_index(0); if (pthread_mutex_init(&Winthread.lock, NULL) != 0) { exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); @@ -1483,7 +1492,6 @@ int main(int argc, char **argv) pthread_mutex_lock(&Winthread.lock); print_init_messages(prompt); - set_active_window_index(0); pthread_mutex_unlock(&Winthread.lock); cleanup_init_messages(); diff --git a/src/toxic.h b/src/toxic.h index 4949397..f30cb42 100644 --- a/src/toxic.h +++ b/src/toxic.h @@ -75,7 +75,7 @@ #define T_KEY_C_DOWN 0x20D /* ctrl-down arrow */ #define T_KEY_TAB 0x09 /* TAB key */ -#define ONLINE_CHAR "*" +#define ONLINE_CHAR "o" #define OFFLINE_CHAR "o" typedef enum _FATAL_ERRS { diff --git a/src/windows.c b/src/windows.c index 72d4685..68aa660 100644 --- a/src/windows.c +++ b/src/windows.c @@ -336,7 +336,7 @@ int add_window(Tox *m, ToxWindow *w) } w->index = i; - w->window = newwin(LINES - 2, COLS, 0, 0); + w->window = newwin(LINES, COLS, 0, 0); if (w->window == NULL) { return -1; @@ -397,8 +397,9 @@ void del_window(ToxWindow *w) set_active_window_index(0); uint8_t idx = w->index; + delwin(w->window_bar); delwin(w->window); - free(windows[idx]); + free(w); windows[idx] = NULL; clear(); @@ -408,7 +409,12 @@ void del_window(ToxWindow *w) ToxWindow *init_windows(Tox *m) { + if (COLS <= CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT) { + exit_toxic_err("add_window() for prompt failed in init_windows", FATALERR_WININIT); + } + prompt = new_prompt(); + int n_prompt = add_window(m, prompt); if (n_prompt < 0) { @@ -430,25 +436,18 @@ void on_window_resize(void) refresh(); clear(); - /* equivalent to LINES and COLS */ - int x2, y2; - getmaxyx(stdscr, y2, x2); - y2 -= 2; - - if (y2 <= 0 || x2 <= 0) { - return; - } - for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { - if (windows[i] == NULL) { + ToxWindow *w = windows[i]; + + if (w == NULL) { continue; } - ToxWindow *w = windows[i]; - - if (windows[i]->type == WINDOW_TYPE_FRIEND_LIST) { + if (w->type == WINDOW_TYPE_FRIEND_LIST) { + delwin(w->window_bar); delwin(w->window); - w->window = newwin(y2, x2, 0, 0); + w->window = newwin(LINES, COLS, 0, 0); + w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, COLS, LINES - 2, 0); continue; } @@ -465,22 +464,35 @@ void on_window_resize(void) delwin(w->chatwin->linewin); delwin(w->chatwin->history); + delwin(w->window_bar); delwin(w->window); - w->window = newwin(y2, x2, 0, 0); - w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0); + w->window = newwin(LINES, COLS, 0, 0); + + int x2; + int y2; + getmaxyx(w->window, y2, x2); + + if (y2 <= 0 || x2 <= 0) { + fprintf(stderr, "Failed to resize window: max_x: %d, max_y: %d\n", x2, y2); + delwin(w->window); + return; + } if (w->show_peerlist) { - w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0); - w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); + w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0); + w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); } else { - w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); + w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0); if (w->type != WINDOW_TYPE_CONFERENCE) { - w->stb->topline = subwin(w->window, 2, x2, 0, 0); + w->stb->topline = subwin(w->window, TOP_BAR_HEIGHT, x2, 0, 0); } } + w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0); + w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0); + #ifdef AUDIO if (w->chatwin->infobox.active) { @@ -491,97 +503,103 @@ void on_window_resize(void) #endif /* AUDIO */ scrollok(w->chatwin->history, 0); + wmove(w->window, y2 - CURS_Y_OFFSET, 0); } } -static void draw_window_tab(ToxWindow *toxwin, bool active_window) +static void draw_window_tab(WINDOW *win, ToxWindow *toxwin, bool active_window) { pthread_mutex_lock(&Winthread.lock); - if (toxwin->alert != WINDOW_ALERT_NONE) { - attron(COLOR_PAIR(toxwin->alert)); - } - + bool has_alert = toxwin->alert != WINDOW_ALERT_NONE; unsigned int pending_messages = toxwin->pending_messages; pthread_mutex_unlock(&Winthread.lock); - clrtoeol(); - WINDOW_TYPE type = toxwin->type; - if (active_window || (type == WINDOW_TYPE_PROMPT || type == WINDOW_TYPE_FRIEND_LIST)) { - printw(" [%s]", toxwin->name); + if (active_window) { + wattron(win, A_BOLD | COLOR_PAIR(CYAN_BLUE)); + wprintw(win, " ["); + wattroff(win, COLOR_PAIR(CYAN_BLUE)); + wattron(win, COLOR_PAIR(WHITE_BLUE)); } else { - if (pending_messages > 0) { - printw(" [%u]", pending_messages); + if (has_alert) { + wattron(win, COLOR_PAIR(CYAN_BLUE)); + wprintw(win, " ["); + wattroff(win, COLOR_PAIR(CYAN_BLUE)); + wattron(win, A_BOLD | COLOR_PAIR(toxwin->alert)); } else { - printw(" [*]"); + wattron(win, COLOR_PAIR(CYAN_BLUE)); + wprintw(win, " ["); + wattroff(win, COLOR_PAIR(CYAN_BLUE)); + wattron(win, COLOR_PAIR(WHITE_BLUE)); } } - pthread_mutex_lock(&Winthread.lock); - - if (toxwin->alert != WINDOW_ALERT_NONE) { - attroff(COLOR_PAIR(toxwin->alert)); + if (active_window || (type == WINDOW_TYPE_PROMPT || type == WINDOW_TYPE_FRIEND_LIST)) { + wprintw(win, "%s", toxwin->name); + } else { + if (pending_messages > 0) { + wprintw(win, "%u", pending_messages); + } else { + wprintw(win, "-"); + } } - pthread_mutex_unlock(&Winthread.lock); + if (active_window) { + wattroff(win, COLOR_PAIR(WHITE_BLUE)); + wattron(win, COLOR_PAIR(CYAN_BLUE)); + wprintw(win, "]"); + wattroff(win, A_BOLD | COLOR_PAIR(CYAN_BLUE)); + } else { + if (has_alert) { + wattroff(win, A_BOLD | COLOR_PAIR(toxwin->alert)); + wattron(win, COLOR_PAIR(CYAN_BLUE)); + wprintw(win, "]"); + wattroff(win, COLOR_PAIR(CYAN_BLUE)); + } else { + wattroff(win, COLOR_PAIR(WHITE_BLUE)); + wattron(win, COLOR_PAIR(CYAN_BLUE)); + wprintw(win, "]"); + wattroff(win, COLOR_PAIR(CYAN_BLUE)); + } + } } -static void draw_bar(void) +void draw_window_bar(ToxWindow *self) { - int y, x; + WINDOW *win = self->window_bar; + wclear(win); - ToxWindow *w = windows[active_window_index]; - - if (w == NULL) { - return; + if (self->scroll_pause) { + wattron(win, A_BLINK | A_BOLD | COLOR_PAIR(YELLOW_BLUE)); + wprintw(win, "^"); + wattroff(win, A_BLINK | A_BOLD | COLOR_PAIR(YELLOW_BLUE)); + } else { + wattron(win, COLOR_PAIR(WHITE_BLUE)); + wprintw(win, " "); + wattroff(win, COLOR_PAIR(WHITE_BLUE)); } - // save current cursor position - getyx(w->window, y, x); - - attron(COLOR_PAIR(BLUE)); - mvhline(LINES - 2, 0, '_', COLS); - attroff(COLOR_PAIR(BLUE)); - - move(LINES - 1, 0); - for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i] == NULL) { continue; } bool active_window = i == active_window_index; - - if (active_window) { - -#ifdef URXVT_FIX - attron(A_BOLD | COLOR_PAIR(GREEN)); - } else { -#endif - - attron(A_BOLD); - } - - draw_window_tab(windows[i], active_window); - - if (active_window) { - -#ifdef URXVT_FIX - attroff(A_BOLD | COLOR_PAIR(GREEN)); - } else { -#endif - - attroff(A_BOLD); - } + draw_window_tab(win, windows[i], active_window); } - // restore cursor position after drawing - move(y, x); + int cur_x; + int cur_y; + UNUSED_VAR(cur_y); - refresh(); + getyx(win, cur_y, cur_x); + + wattron(win, COLOR_PAIR(WHITE_BLUE)); + mvwhline(win, 0, cur_x, ' ', COLS - cur_x); + wattroff(win, COLOR_PAIR(WHITE_BLUE)); } /* @@ -684,8 +702,6 @@ void draw_active_window(Tox *m) a->pending_messages = 0; pthread_mutex_unlock(&Winthread.lock); - draw_bar(); - touchwin(a->window); a->onDraw(a, m); wrefresh(a->window); diff --git a/src/windows.h b/src/windows.h index d3c9bf7..ba55a1e 100644 --- a/src/windows.h +++ b/src/windows.h @@ -39,9 +39,11 @@ #define MAX_WINDOWS_NUM 20 #define MAX_WINDOW_NAME_LENGTH 22 #define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */ -#define CHATBOX_HEIGHT 2 +#define CHATBOX_HEIGHT 1 +#define TOP_BAR_HEIGHT 1 +#define WINDOW_BAR_HEIGHT 1 -/* Curses foreground colours (background is black) */ +/* ncurses colour pairs as FOREGROUND_BACKGROUND. No background defaults to black. */ typedef enum { WHITE, GREEN, @@ -51,14 +53,23 @@ typedef enum { YELLOW, MAGENTA, BLACK, + BLUE_BLACK, + BLACK_WHITE, + WHITE_BLUE, + GREEN_BLUE, + CYAN_BLUE, + PURPLE_BLUE, + BLACK_BLUE, + RED_BLUE, + YELLOW_BLUE, } C_COLOURS; /* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */ typedef enum { WINDOW_ALERT_NONE = 0, - WINDOW_ALERT_0 = GREEN, - WINDOW_ALERT_1 = RED, - WINDOW_ALERT_2 = MAGENTA, + WINDOW_ALERT_0 = GREEN_BLUE, + WINDOW_ALERT_1 = CYAN_BLUE, + WINDOW_ALERT_2 = PURPLE_BLUE, } WINDOW_ALERTS; typedef enum { @@ -167,6 +178,7 @@ struct ToxWindow { char name[TOXIC_MAX_NAME_LENGTH + 1]; uint32_t num; /* corresponds to friendnumber in chat windows */ uint8_t index; /* This window's index in the windows array */ + bool scroll_pause; /* true if this window is not scrolled to the bottom */ unsigned int pending_messages; /* # of new messages in this window since the last time it was focused */ int x; @@ -181,6 +193,7 @@ struct ToxWindow { Help *help; WINDOW *window; + WINDOW *window_bar; }; /* statusbar info holder */ @@ -264,6 +277,7 @@ void on_window_resize(void); void force_refresh(WINDOW *w); ToxWindow *get_window_ptr(size_t i); ToxWindow *get_active_window(void); +void draw_window_bar(ToxWindow *self); /* refresh inactive windows to prevent scrolling bugs. call at least once per second */