Interface improvements and bump to v0.10.0

- Give window tab and statusbar a make over
- Place window tab above input field
- Reduce input field to one square in height
- Refactor window tab so that it's now a subwin of its parent ToxWindow
- Fix bug causing notification counter to sometimes increment by 2
- No longer scroll on output when output is not at bottom of screen
- Show a small indicator on far left of window tab when output is
  not at bottom of screen
- Reduce ncurses/UI thread sleep time by half
- Handle nanosleep errors better
This commit is contained in:
jfreegman 2020-11-29 23:26:51 -05:00
parent 61740bda85
commit 1e985c1456
No known key found for this signature in database
GPG Key ID: 3627F3144076AE63
17 changed files with 519 additions and 272 deletions

View File

@ -30,7 +30,7 @@ endif
# Check if LLVM Address Sanitizer is enabled # Check if LLVM Address Sanitizer is enabled
ASAN_ENABLED := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi) ASAN_ENABLED := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
ifneq ($(ASAN_ENABLED), disabled) 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 endif
# Check on wich system we are running # Check on wich system we are running

View File

@ -5,7 +5,7 @@
Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client. 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 ## Installation
[See the install instructions](/INSTALL.md) [See the install instructions](/INSTALL.md)

View File

@ -55,9 +55,9 @@ author = 'Jakob Kreuze'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.9.1' version = '0.10.0'
# The full version, including alpha/beta/rc tags. # 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 # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -1,5 +1,5 @@
# Version # Version
TOXIC_VERSION = 0.9.1 TOXIC_VERSION = 0.10.0
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
ifneq (, $(findstring error, $(REV))) ifneq (, $(findstring error, $(REV)))
VERSION = $(TOXIC_VERSION) VERSION = $(TOXIC_VERSION)

View File

@ -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.length = length;
Friends.list[friendnumber].conference_invite.type = type; 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]; char name[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, name, friendnumber); get_nick_truncate(m, name, friendnumber);
const char *description = type == TOX_CONFERENCE_TYPE_AV ? "an audio conference" : "a conference"; const char *description = type == TOX_CONFERENCE_TYPE_AV ? "an audio conference" : "a conference";
if (self->active_box != -1) { 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 { } 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); 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); wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2, 0);
reset_buf(ctx); 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) static void chat_onDraw(ToxWindow *self, Tox *m)
{ {
int x2, y2; int x2;
int y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) { if (y2 <= 0 || x2 <= 0) {
@ -1209,15 +1210,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
wclear(ctx->linewin); wclear(ctx->linewin);
curs_set(1);
if (ctx->len > 0) { 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 */ /* Draw status bar */
StatusBar *statusbar = self->stb; StatusBar *statusbar = self->stb;
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
wmove(statusbar->topline, 0, 0); wmove(statusbar->topline, 0, 0);
/* Draw name, status and note in statusbar */ /* Draw name, status and note in statusbar */
@ -1227,38 +1227,63 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
switch (status) { switch (status) {
case TOX_USER_STATUS_NONE: case TOX_USER_STATUS_NONE:
colour = GREEN; colour = GREEN_BLUE;
break; break;
case TOX_USER_STATUS_AWAY: case TOX_USER_STATUS_AWAY:
colour = YELLOW; colour = YELLOW_BLUE;
break; break;
case TOX_USER_STATUS_BUSY: case TOX_USER_STATUS_BUSY:
colour = RED; colour = RED_BLUE;
break; 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); 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); wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
if (Friends.list[self->num].is_typing) { wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
wattron(statusbar->topline, COLOR_PAIR(YELLOW)); 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);
wprintw(statusbar->topline, " %s ", statusbar->nick);
wattroff(statusbar->topline, A_BOLD);
if (Friends.list[self->num].is_typing) { if (is_typing) {
wattroff(statusbar->topline, COLOR_PAIR(YELLOW)); wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(YELLOW_BLUE));
} else {
wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(WHITE_BLUE));
} }
} else { } else {
wprintw(statusbar->topline, " %s", OFFLINE_CHAR); wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
wattron(statusbar->topline, A_BOLD); wprintw(statusbar->topline, " [");
wprintw(statusbar->topline, " %s ", statusbar->nick); wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
wattroff(statusbar->topline, A_BOLD);
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 */ /* Reset statusbar->statusmsg on window resize */
@ -1287,30 +1312,51 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
} }
if (statusbar->statusmsg[0]) { 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); wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3);
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
wprintw(statusbar->topline, "{"); 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, "%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); getyx(self->window, y, x);
UNUSED_VAR(x); UNUSED_VAR(x);
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); 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); 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) static void chat_onInit(ToxWindow *self, Tox *m)
{ {
curs_set(1); curs_set(1);
int x2, y2;
int x2;
int y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) { if (y2 <= 0 || x2 <= 0) {
@ -1390,9 +1438,10 @@ static void chat_onInit(ToxWindow *self, Tox *m)
/* Init subwindows */ /* Init subwindows */
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
statusbar->topline = subwin(self->window, 2, x2, 0, 0); statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0);
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);
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);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0);
ctx->hst = calloc(1, sizeof(struct history)); ctx->hst = calloc(1, sizeof(struct history));
ctx->log = calloc(1, sizeof(struct chatlog)); ctx->log = calloc(1, sizeof(struct chatlog));

View File

@ -307,9 +307,9 @@ void redraw_conference_win(ToxWindow *self)
refresh(); refresh();
clear(); clear();
int x2, y2; int x2;
getmaxyx(stdscr, y2, x2); int y2;
y2 -= 2; getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) { if (y2 <= 0 || x2 <= 0) {
return; return;
@ -322,20 +322,22 @@ void redraw_conference_win(ToxWindow *self)
delwin(ctx->linewin); delwin(ctx->linewin);
delwin(ctx->history); delwin(ctx->history);
delwin(self->window_bar);
delwin(self->window); delwin(self->window);
self->window = newwin(y2, x2, 0, 0); self->window = newwin(y2, x2, 0, 0);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 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) { if (self->show_peerlist) {
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);
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);
} else { } 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); 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, 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 */ /* Only play sound if mentioned by someone else */
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) { 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) { 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 { } 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; nick_clr = RED;
@ -963,7 +965,7 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} }
wclear(ctx->linewin); wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2, 0);
reset_buf(ctx); reset_buf(ctx);
} }
@ -1049,15 +1051,16 @@ static void conference_onDraw(ToxWindow *self, Tox *m)
curs_set(1); curs_set(1);
if (ctx->len > 0) { 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); wclear(ctx->sidebar);
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
if (self->show_peerlist) { if (self->show_peerlist) {
wattron(ctx->sidebar, COLOR_PAIR(BLUE));
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT); mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE); mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
wattroff(ctx->sidebar, COLOR_PAIR(BLUE));
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
const uint32_t num_peers = chat->num_peers; 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); wattroff(ctx->sidebar, A_BOLD);
++line; ++line;
wattron(ctx->sidebar, COLOR_PAIR(BLUE));
mvwaddch(ctx->sidebar, line, 0, ACS_LTEE); mvwaddch(ctx->sidebar, line, 0, ACS_LTEE);
mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1); mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
wattroff(ctx->sidebar, COLOR_PAIR(BLUE));
++line; ++line;
for (uint32_t i = 0; for (uint32_t i = 0;
@ -1135,7 +1141,9 @@ static void conference_onDraw(ToxWindow *self, Tox *m)
UNUSED_VAR(x); UNUSED_VAR(x);
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); 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); wnoutrefresh(self->window);
@ -1155,9 +1163,10 @@ static void conference_onInit(ToxWindow *self, Tox *m)
ChatContext *ctx = self->chatwin; 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->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->hst = calloc(1, sizeof(struct history));
ctx->log = calloc(1, sizeof(struct chatlog)); ctx->log = calloc(1, sizeof(struct chatlog));

View File

@ -1022,7 +1022,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
wmove(self->window, y2 - 1, 1); wmove(self->window, y2 - 1, 1);
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
wprintw(self->window, "Key: "); wprintw(self->window, "Public key: ");
wattroff(self->window, A_BOLD); wattroff(self->window, A_BOLD);
int i; int i;
@ -1057,6 +1057,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "key for help\n\n"); wprintw(self->window, "key for help\n\n");
wattroff(self->window, COLOR_PAIR(CYAN)); wattroff(self->window, COLOR_PAIR(CYAN));
draw_window_bar(self);
if (blocklist_view == 1) { if (blocklist_view == 1) {
blocklist_onDraw(self, m, y2, x2); blocklist_onDraw(self, m, y2, x2);
return; return;
@ -1247,7 +1249,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wmove(self->window, y2 - 1, 1); wmove(self->window, y2 - 1, 1);
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
wprintw(self->window, "Key: "); wprintw(self->window, "Public key: ");
wattroff(self->window, A_BOLD); wattroff(self->window, A_BOLD);
int i; 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) void disable_chatwin(uint32_t f_num)
{ {
Friends.list[f_num].chatwin = -1; Friends.list[f_num].chatwin = -1;
@ -1343,6 +1360,7 @@ ToxWindow *new_friendlist(void)
ret->type = WINDOW_TYPE_FRIEND_LIST; ret->type = WINDOW_TYPE_FRIEND_LIST;
ret->onInit = &friendlist_onInit;
ret->onKey = &friendlist_onKey; ret->onKey = &friendlist_onKey;
ret->onDraw = &friendlist_onDraw; ret->onDraw = &friendlist_onDraw;
ret->onFriendAdded = &friendlist_onFriendAdded; ret->onFriendAdded = &friendlist_onFriendAdded;

View File

@ -82,6 +82,7 @@ typedef struct {
} FriendsList; } FriendsList;
ToxWindow *new_friendlist(void); ToxWindow *new_friendlist(void);
void friendlist_onInit(ToxWindow *self, Tox *m);
void disable_chatwin(uint32_t f_num); void disable_chatwin(uint32_t f_num);
int get_friendnum(uint8_t *name); int get_friendnum(uint8_t *name);
int load_blocklist(char *data); int load_blocklist(char *data);

View File

@ -63,10 +63,10 @@ void line_info_reset_start(ToxWindow *self, struct history *hst)
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
UNUSED_VAR(x2); UNUSED_VAR(x2);
int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? 2 : 0; int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
int max_y = y2 - CHATBOX_HEIGHT - top_offst; int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst;
int curlines = 0; uint16_t curlines = 0;
do { do {
curlines += line->format_lines; 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); } while (line->prev && curlines + line->format_lines <= max_y);
hst->line_start = line; hst->line_start = line;
self->scroll_pause = false;
} }
void line_info_cleanup(struct history *hst) 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`. /* 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; bool newline = false;
char ch; char ch;
for (size_t i = 0; i < n && (ch = s[i]); ++i) { for (size_t i = 0; i < n && (ch = s[i]); ++i) {
if (win) {
wprintw(win, "%c", ch);
}
if (ch == '\n') { if (ch == '\n') {
newline = true; 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 * If `win` is null nothing will be printed to the window. This is useful to set the
* `format_lines` field on initialization. * `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; const char *msg = line->msg;
uint16_t length = line->msg_len; uint16_t length = line->msg_len;
uint16_t lines = 0; uint16_t lines = 0;
const int x_start = line->len - line->msg_len - 1; // manually keep track of x position because ncurses sucks 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; 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); fprintf(stderr, "Warning: x_limit <= 0 in print_wrap(): %d\n", x_limit);
return; return -1;
} }
while (msg) { 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 (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); lines += newline_count(msg);
} else if (p_ret == -1) {
return -1;
} }
++lines; ++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); int newline_idx = newline_index(msg, x_limit - 1);
if (newline_idx >= 0) { if (newline_idx >= 0) {
print_n_chars(win, msg, newline_idx + 1); if (print_n_chars(win, msg, newline_idx + 1, max_y) == -1) {
msg += newline_idx + 1; return -1;
}
msg += (newline_idx + 1);
length -= (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 x_limit = max_x; // if we find a newline we stop adding column padding for rest of message
++lines; ++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); int space_idx = rspace_index(msg, x_limit - 1);
if (space_idx >= 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; msg += space_idx + 1;
length -= (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'); waddch(win, '\n');
} }
} else { } else {
print_n_chars(win, msg, x_limit); if (print_n_chars(win, msg, x_limit, max_y) == -1) {
return -1;
}
msg += x_limit; msg += x_limit;
length -= 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) { if (win && line->noread_flag) {
int x;
int y;
UNUSED_VAR(y);
getyx(win, y, x); getyx(win, y, x);
if (x >= max_x - 1 || x == x_start) { 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; 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 y2;
int max_x; int x2;
UNUSED_VAR(max_y); 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. /* 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; break;
case PROMPT: case PROMPT:
++len; len += 2;
break; break;
default: 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->noread_flag = false;
new_line->timestamp = get_unix_time(); 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; 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 = line;
hst->line_end->id = line->id; hst->line_end->id = line->id;
int y; if (!self->scroll_pause) {
int y2; line_info_reset_start(self, hst);
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;
}
} }
} }
@ -482,16 +503,31 @@ void line_info_print(ToxWindow *self)
if (self->type == WINDOW_TYPE_CONFERENCE) { if (self->type == WINDOW_TYPE_CONFERENCE) {
wmove(win, 0, 0); wmove(win, 0, 0);
} else { } else {
wmove(win, 2, 0); wmove(win, TOP_BAR_HEIGHT, 0);
} }
struct line_info *line = hst->line_start->next; 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; 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; uint8_t type = line->type;
switch (type) { switch (type) {
@ -529,7 +565,7 @@ void line_info_print(ToxWindow *self)
wattron(win, COLOR_PAIR(RED)); 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] == '>') { if (line->msg[0] == '>') {
wattroff(win, COLOR_PAIR(GREEN)); wattroff(win, COLOR_PAIR(GREEN));
@ -559,7 +595,7 @@ void line_info_print(ToxWindow *self)
wattron(win, COLOR_PAIR(YELLOW)); wattron(win, COLOR_PAIR(YELLOW));
wprintw(win, "%s %s ", user_settings->line_normal, line->name1); 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)); wattroff(win, COLOR_PAIR(YELLOW));
if (type == OUT_ACTION && !line->read_flag) { if (type == OUT_ACTION && !line->read_flag) {
@ -586,7 +622,7 @@ void line_info_print(ToxWindow *self)
wattron(win, COLOR_PAIR(line->colour)); 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'); waddch(win, '\n');
if (line->bold) { if (line->bold) {
@ -605,7 +641,7 @@ void line_info_print(ToxWindow *self)
wattroff(win, COLOR_PAIR(GREEN)); wattroff(win, COLOR_PAIR(GREEN));
if (line->msg[0]) { if (line->msg[0]) {
print_wrap(win, line, max_x); print_ret = print_wrap(win, line, max_x, max_y);
} }
waddch(win, '\n'); waddch(win, '\n');
@ -623,7 +659,7 @@ void line_info_print(ToxWindow *self)
wprintw(win, "%s ", line->name1); wprintw(win, "%s ", line->name1);
wattroff(win, A_BOLD); wattroff(win, A_BOLD);
print_wrap(win, line, max_x); print_ret = print_wrap(win, line, max_x, max_y);
waddch(win, '\n'); waddch(win, '\n');
wattroff(win, COLOR_PAIR(line->colour)); wattroff(win, COLOR_PAIR(line->colour));
@ -642,7 +678,7 @@ void line_info_print(ToxWindow *self)
wprintw(win, "%s ", line->name1); wprintw(win, "%s ", line->name1);
wattroff(win, A_BOLD); wattroff(win, A_BOLD);
print_wrap(win, line, max_x); print_ret = print_wrap(win, line, max_x, max_y);
waddch(win, '\n'); waddch(win, '\n');
wattroff(win, COLOR_PAIR(line->colour)); wattroff(win, COLOR_PAIR(line->colour));
@ -660,7 +696,7 @@ void line_info_print(ToxWindow *self)
wprintw(win, "%s", line->name1); wprintw(win, "%s", line->name1);
wattroff(win, A_BOLD); wattroff(win, A_BOLD);
print_wrap(win, line, max_x); print_ret = print_wrap(win, line, max_x, max_y);
wattron(win, A_BOLD); wattron(win, A_BOLD);
wprintw(win, "%s\n", line->name2); 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 */ /* puts msg in specified line_info msg buffer */
void line_info_set(ToxWindow *self, uint32_t id, char *msg) 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) { if (hst->line_start->prev) {
hst->line_start = hst->line_start->prev; hst->line_start = hst->line_start->prev;
} else { self->scroll_pause = true;
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
} }
} }
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) { struct line_info *next = hst->line_start->next;
hst->line_start = 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 { } 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) static void line_info_page_up(ToxWindow *self, struct history *hst)
{ {
int x2, y2; int x2;
int y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
UNUSED_VAR(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) { for (size_t i = 0; i < jump_dist && hst->line_start->prev; ++i) {
hst->line_start = hst->line_start->prev; hst->line_start = hst->line_start->prev;
} }
self->scroll_pause = true;
} }
static void line_info_page_down(ToxWindow *self, struct history *hst) static void line_info_page_down(ToxWindow *self, struct history *hst)
{ {
int x2, y2; if (!self->scroll_pause) {
getmaxyx(self->window, y2, x2); return;
}
int x2;
int y2;
getmaxyx(self->chatwin->history, y2, x2);
UNUSED_VAR(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) { struct line_info *next = hst->line_start->next;
hst->line_start = 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) { } else if (key == user_settings->key_half_page_down) {
line_info_page_down(self, hst); line_info_page_down(self, hst);
} else if (key == user_settings->key_scroll_line_up) { } 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) { } 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) { } else if (key == user_settings->key_page_bottom) {
line_info_reset_start(self, hst); line_info_reset_start(self, hst);
} else { } else {

View File

@ -306,7 +306,7 @@ int load_chat_history(ToxWindow *self, struct chatlog *log)
line = strtok_r(NULL, "\n", &tmp); 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); free(buf);

View File

@ -71,16 +71,19 @@ int timed_out(time_t timestamp, time_t timeout)
return timestamp + timeout <= get_unix_time(); 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) void sleep_thread(long int usec)
{ {
struct timespec req; struct timespec req;
struct timespec rem;
req.tv_sec = 0; req.tv_sec = 0;
req.tv_nsec = usec * 1000L; req.tv_nsec = usec * 1000L;
if (nanosleep(&req, NULL) == -1) { if (nanosleep(&req, &rem) == -1) {
fprintf(stderr, "nanosleep() returned -1\n"); if (nanosleep(&rem, NULL) == -1) {
fprintf(stderr, "nanosleep() returned -1\n");
}
} }
} }

View File

@ -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 */ /* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(time_t timestamp, time_t timeout); 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); void sleep_thread(long int usec);
/* Colours the window tab according to type. Beeps if is_beep is true */ /* Colours the window tab according to type. Beeps if is_beep is true */

View File

@ -292,7 +292,7 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} }
wclear(ctx->linewin); wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2, 0);
reset_buf(ctx); 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) static void prompt_onDraw(ToxWindow *self, Tox *m)
{ {
int x2, y2; int x2;
int y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) { if (y2 <= 0 || x2 <= 0) {
@ -316,64 +317,88 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
wclear(ctx->linewin); wclear(ctx->linewin);
curs_set(1);
if (ctx->len > 0) { 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; StatusBar *statusbar = self->stb;
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
wmove(statusbar->topline, 0, 0); wmove(statusbar->topline, 0, 0);
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
Tox_Connection connection = statusbar->connection; Tox_Connection connection = statusbar->connection;
Tox_User_Status status = statusbar->status;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
if (connection != TOX_CONNECTION_NONE) { if (connection != TOX_CONNECTION_NONE) {
int colour = MAGENTA; int colour = MAGENTA;
const char *status_text = "ERROR"; const char *status_text = "ERROR";
pthread_mutex_lock(&Winthread.lock);
Tox_User_Status status = statusbar->status;
pthread_mutex_unlock(&Winthread.lock);
switch (status) { switch (status) {
case TOX_USER_STATUS_NONE: case TOX_USER_STATUS_NONE:
status_text = "Online"; status_text = "Online";
colour = GREEN; colour = GREEN_BLUE;
break; break;
case TOX_USER_STATUS_AWAY: case TOX_USER_STATUS_AWAY:
status_text = "Away"; status_text = "Away";
colour = YELLOW; colour = YELLOW_BLUE;
break; break;
case TOX_USER_STATUS_BUSY: case TOX_USER_STATUS_BUSY:
status_text = "Busy"; status_text = "Busy";
colour = RED; colour = RED_BLUE;
break; break;
} }
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
wprintw(statusbar->topline, " [%s]", status_text); wprintw(statusbar->topline, " [");
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); 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); pthread_mutex_lock(&Winthread.lock);
wprintw(statusbar->topline, " %s", statusbar->nick); wprintw(statusbar->topline, " %s", statusbar->nick);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
wattroff(statusbar->topline, A_BOLD);
} else { } else {
wprintw(statusbar->topline, " [Offline]"); wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
wattron(statusbar->topline, A_BOLD); 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); pthread_mutex_lock(&Winthread.lock);
wprintw(statusbar->topline, " %s", statusbar->nick); wprintw(statusbar->topline, " %s", statusbar->nick);
pthread_mutex_unlock(&Winthread.lock); 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 */ /* Reset statusbar->statusmsg on window resize */
if (x2 != self->x) { if (x2 != self->x) {
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH]; char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
@ -401,20 +426,27 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
} }
if (statusbar->statusmsg[0]) { 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); pthread_mutex_unlock(&Winthread.lock);
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); int y;
int x;
int y, x;
getyx(self->window, y, x); getyx(self->window, y, x);
UNUSED_VAR(x); UNUSED_VAR(x);
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); 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); wnoutrefresh(self->window);
@ -535,7 +567,7 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run)
prompt_update_nick(prompt, nick); prompt_update_nick(prompt, nick);
/* Init statusbar subwindow */ /* 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) 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) static void prompt_onInit(ToxWindow *self, Tox *m)
{ {
curs_set(1); curs_set(1);
int y2, x2;
int y2;
int x2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) { if (y2 <= 0 || x2 <= 0) {
@ -584,8 +618,10 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
} }
ChatContext *ctx = self->chatwin; 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->log = calloc(1, sizeof(struct chatlog));
ctx->hst = calloc(1, sizeof(struct history)); ctx->hst = calloc(1, sizeof(struct history));

View File

@ -256,7 +256,7 @@ static void init_term(void)
keypad(stdscr, 1); keypad(stdscr, 1);
noecho(); noecho();
nonl(); nonl();
timeout(100); timeout(50);
if (has_colors()) { if (has_colors()) {
short bg_color = COLOR_BLACK; short bg_color = COLOR_BLACK;
@ -268,15 +268,23 @@ static void init_term(void)
} }
} }
init_pair(0, COLOR_WHITE, COLOR_BLACK); init_pair(WHITE, COLOR_WHITE, COLOR_BLACK);
init_pair(1, COLOR_GREEN, bg_color); init_pair(GREEN, COLOR_GREEN, bg_color);
init_pair(2, COLOR_CYAN, bg_color); init_pair(CYAN, COLOR_CYAN, bg_color);
init_pair(3, COLOR_RED, bg_color); init_pair(RED, COLOR_RED, bg_color);
init_pair(4, COLOR_BLUE, bg_color); init_pair(BLUE, COLOR_BLUE, bg_color);
init_pair(5, COLOR_YELLOW, bg_color); init_pair(YELLOW, COLOR_YELLOW, bg_color);
init_pair(6, COLOR_MAGENTA, bg_color); init_pair(MAGENTA, COLOR_MAGENTA, bg_color);
init_pair(7, COLOR_BLACK, COLOR_BLACK); init_pair(BLACK, COLOR_BLACK, COLOR_BLACK);
init_pair(8, COLOR_BLACK, COLOR_WHITE); 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(); refresh();
@ -1421,6 +1429,7 @@ int main(int argc, char **argv)
prompt = init_windows(m); prompt = init_windows(m);
prompt_init_statusbar(prompt, m, !datafile_exists); prompt_init_statusbar(prompt, m, !datafile_exists);
load_conferences(m); load_conferences(m);
set_active_window_index(0);
if (pthread_mutex_init(&Winthread.lock, NULL) != 0) { if (pthread_mutex_init(&Winthread.lock, NULL) != 0) {
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
@ -1483,7 +1492,6 @@ int main(int argc, char **argv)
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
print_init_messages(prompt); print_init_messages(prompt);
set_active_window_index(0);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
cleanup_init_messages(); cleanup_init_messages();

View File

@ -75,7 +75,7 @@
#define T_KEY_C_DOWN 0x20D /* ctrl-down arrow */ #define T_KEY_C_DOWN 0x20D /* ctrl-down arrow */
#define T_KEY_TAB 0x09 /* TAB key */ #define T_KEY_TAB 0x09 /* TAB key */
#define ONLINE_CHAR "*" #define ONLINE_CHAR "o"
#define OFFLINE_CHAR "o" #define OFFLINE_CHAR "o"
typedef enum _FATAL_ERRS { typedef enum _FATAL_ERRS {

View File

@ -336,7 +336,7 @@ int add_window(Tox *m, ToxWindow *w)
} }
w->index = i; w->index = i;
w->window = newwin(LINES - 2, COLS, 0, 0); w->window = newwin(LINES, COLS, 0, 0);
if (w->window == NULL) { if (w->window == NULL) {
return -1; return -1;
@ -397,8 +397,9 @@ void del_window(ToxWindow *w)
set_active_window_index(0); set_active_window_index(0);
uint8_t idx = w->index; uint8_t idx = w->index;
delwin(w->window_bar);
delwin(w->window); delwin(w->window);
free(windows[idx]); free(w);
windows[idx] = NULL; windows[idx] = NULL;
clear(); clear();
@ -408,7 +409,12 @@ void del_window(ToxWindow *w)
ToxWindow *init_windows(Tox *m) 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(); prompt = new_prompt();
int n_prompt = add_window(m, prompt); int n_prompt = add_window(m, prompt);
if (n_prompt < 0) { if (n_prompt < 0) {
@ -430,25 +436,18 @@ void on_window_resize(void)
refresh(); refresh();
clear(); 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) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] == NULL) { ToxWindow *w = windows[i];
if (w == NULL) {
continue; continue;
} }
ToxWindow *w = windows[i]; if (w->type == WINDOW_TYPE_FRIEND_LIST) {
delwin(w->window_bar);
if (windows[i]->type == WINDOW_TYPE_FRIEND_LIST) {
delwin(w->window); 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; continue;
} }
@ -465,22 +464,35 @@ void on_window_resize(void)
delwin(w->chatwin->linewin); delwin(w->chatwin->linewin);
delwin(w->chatwin->history); delwin(w->chatwin->history);
delwin(w->window_bar);
delwin(w->window); delwin(w->window);
w->window = newwin(y2, x2, 0, 0); w->window = newwin(LINES, COLS, 0, 0);
w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 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) { if (w->show_peerlist) {
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0); 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 + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH); w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
} else { } 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) { 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 #ifdef AUDIO
if (w->chatwin->infobox.active) { if (w->chatwin->infobox.active) {
@ -491,97 +503,103 @@ void on_window_resize(void)
#endif /* AUDIO */ #endif /* AUDIO */
scrollok(w->chatwin->history, 0); 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); pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) { bool has_alert = toxwin->alert != WINDOW_ALERT_NONE;
attron(COLOR_PAIR(toxwin->alert));
}
unsigned int pending_messages = toxwin->pending_messages; unsigned int pending_messages = toxwin->pending_messages;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
clrtoeol();
WINDOW_TYPE type = toxwin->type; WINDOW_TYPE type = toxwin->type;
if (active_window || (type == WINDOW_TYPE_PROMPT || type == WINDOW_TYPE_FRIEND_LIST)) { if (active_window) {
printw(" [%s]", toxwin->name); wattron(win, A_BOLD | COLOR_PAIR(CYAN_BLUE));
wprintw(win, " [");
wattroff(win, COLOR_PAIR(CYAN_BLUE));
wattron(win, COLOR_PAIR(WHITE_BLUE));
} else { } else {
if (pending_messages > 0) { if (has_alert) {
printw(" [%u]", pending_messages); wattron(win, COLOR_PAIR(CYAN_BLUE));
wprintw(win, " [");
wattroff(win, COLOR_PAIR(CYAN_BLUE));
wattron(win, A_BOLD | COLOR_PAIR(toxwin->alert));
} else { } 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 (active_window || (type == WINDOW_TYPE_PROMPT || type == WINDOW_TYPE_FRIEND_LIST)) {
wprintw(win, "%s", toxwin->name);
if (toxwin->alert != WINDOW_ALERT_NONE) { } else {
attroff(COLOR_PAIR(toxwin->alert)); 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 (self->scroll_pause) {
wattron(win, A_BLINK | A_BOLD | COLOR_PAIR(YELLOW_BLUE));
if (w == NULL) { wprintw(win, "^");
return; 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) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] == NULL) { if (windows[i] == NULL) {
continue; continue;
} }
bool active_window = i == active_window_index; bool active_window = i == active_window_index;
draw_window_tab(win, windows[i], active_window);
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);
}
} }
// restore cursor position after drawing int cur_x;
move(y, 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; a->pending_messages = 0;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
draw_bar();
touchwin(a->window); touchwin(a->window);
a->onDraw(a, m); a->onDraw(a, m);
wrefresh(a->window); wrefresh(a->window);

View File

@ -39,9 +39,11 @@
#define MAX_WINDOWS_NUM 20 #define MAX_WINDOWS_NUM 20
#define MAX_WINDOW_NAME_LENGTH 22 #define MAX_WINDOW_NAME_LENGTH 22
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */ #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 { typedef enum {
WHITE, WHITE,
GREEN, GREEN,
@ -51,14 +53,23 @@ typedef enum {
YELLOW, YELLOW,
MAGENTA, MAGENTA,
BLACK, BLACK,
BLUE_BLACK,
BLACK_WHITE,
WHITE_BLUE,
GREEN_BLUE,
CYAN_BLUE,
PURPLE_BLUE,
BLACK_BLUE,
RED_BLUE,
YELLOW_BLUE,
} C_COLOURS; } C_COLOURS;
/* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */ /* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */
typedef enum { typedef enum {
WINDOW_ALERT_NONE = 0, WINDOW_ALERT_NONE = 0,
WINDOW_ALERT_0 = GREEN, WINDOW_ALERT_0 = GREEN_BLUE,
WINDOW_ALERT_1 = RED, WINDOW_ALERT_1 = CYAN_BLUE,
WINDOW_ALERT_2 = MAGENTA, WINDOW_ALERT_2 = PURPLE_BLUE,
} WINDOW_ALERTS; } WINDOW_ALERTS;
typedef enum { typedef enum {
@ -167,6 +178,7 @@ struct ToxWindow {
char name[TOXIC_MAX_NAME_LENGTH + 1]; char name[TOXIC_MAX_NAME_LENGTH + 1];
uint32_t num; /* corresponds to friendnumber in chat windows */ uint32_t num; /* corresponds to friendnumber in chat windows */
uint8_t index; /* This window's index in the windows array */ 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 */ unsigned int pending_messages; /* # of new messages in this window since the last time it was focused */
int x; int x;
@ -181,6 +193,7 @@ struct ToxWindow {
Help *help; Help *help;
WINDOW *window; WINDOW *window;
WINDOW *window_bar;
}; };
/* statusbar info holder */ /* statusbar info holder */
@ -264,6 +277,7 @@ void on_window_resize(void);
void force_refresh(WINDOW *w); void force_refresh(WINDOW *w);
ToxWindow *get_window_ptr(size_t i); ToxWindow *get_window_ptr(size_t i);
ToxWindow *get_active_window(void); ToxWindow *get_active_window(void);
void draw_window_bar(ToxWindow *self);
/* refresh inactive windows to prevent scrolling bugs. /* refresh inactive windows to prevent scrolling bugs.
call at least once per second */ call at least once per second */