From 8e84abff9b1e5fb840692b00108c799bbb964855 Mon Sep 17 00:00:00 2001 From: John Hernandez <129467592+H3rnand3zzz@users.noreply.github.com> Date: Tue, 28 Nov 2023 09:34:55 +0100 Subject: [PATCH] Fix scrolling problems (wip) --- src/database.c | 11 +++++- src/ui/buffer.c | 101 +++++++++++++++++++++++++++--------------------- src/ui/buffer.h | 8 +++- src/ui/window.c | 87 +++++++++++++++++++++++++++-------------- 4 files changed, 129 insertions(+), 78 deletions(-) diff --git a/src/database.c b/src/database.c index ce1d1d3d2..187567a70 100644 --- a/src/database.c +++ b/src/database.c @@ -337,9 +337,12 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s gchar* sort2 = !flip ? "ASC" : "DESC"; GDateTime* now = g_date_time_new_now_local(); auto_gchar gchar* end_date_fmt = end_time ? end_time : g_date_time_format_iso8601(now); + // DEBUG, remove comment and change. + // to make debug easier, "- {id}" is appended to message so we always know ID of the message + // (we might as well pass it later to make interaction with DB easier) auto_sqlite gchar* query = sqlite3_mprintf("SELECT * FROM (" - "SELECT COALESCE(B.`message`, A.`message`) AS message, " - "A.`timestamp`, A.`from_jid`, A.`to_jid`, A.`type`, A.`encryption` FROM `ChatLogs` AS A " + "SELECT CONCAT_WS(' - ', COALESCE(B.`message`, A.`message`), A.`id`) AS message, " + "A.`timestamp`, A.`from_jid`, A.`to_jid`, A.`type`, A.`encryption`, A.`id` FROM `ChatLogs` AS A " "LEFT JOIN `ChatLogs` AS B ON (A.`replaced_by_db_id` = B.`id` AND A.`from_jid` = B.`from_jid`) " "WHERE (A.`replaces_db_id` IS NULL) " "AND ((A.`from_jid` = %Q AND A.`to_jid` = %Q) OR (A.`from_jid` = %Q AND A.`to_jid` = %Q)) " @@ -371,6 +374,10 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s char* to_jid = (char*)sqlite3_column_text(stmt, 3); char* type = (char*)sqlite3_column_text(stmt, 4); char* encryption = (char*)sqlite3_column_text(stmt, 5); + // DEBUG, remove comment and change. + // output message ID to track displayed messages better + char* mid = (char*)sqlite3_column_text(stmt, 6); + log_debug(mid); ProfMessage* msg = message_init(); msg->from_jid = jid_create(from); diff --git a/src/ui/buffer.c b/src/ui/buffer.c index e14434c2b..8c0e5fc12 100644 --- a/src/ui/buffer.c +++ b/src/ui/buffer.c @@ -50,23 +50,32 @@ #include #endif +// DEBUG, remove comment and log.h import. +#include "log.h" #include "ui/window.h" #include "ui/buffer.h" -#define BUFF_SIZE 200 +#define BUFF_SIZE 200 +#define BUFF_SIZE_LINES 800 +#define STRDUP_OR_NULL(str) ((str) ? strdup(str) : NULL) struct prof_buff_t { GSList* entries; + int lines; }; static void _free_entry(ProfBuffEntry* entry); +static ProfBuffEntry* _create_entry(const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos); +static void _buffer_add(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos, gboolean append); + ProfBuff buffer_create(void) { ProfBuff new_buff = malloc(sizeof(struct prof_buff_t)); new_buff->entries = NULL; + new_buff->lines = 0; return new_buff; } @@ -84,58 +93,38 @@ buffer_free(ProfBuff buffer) } void -buffer_append(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id) +buffer_append(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos) { - ProfBuffEntry* e = malloc(sizeof(struct prof_buff_entry_t)); - e->show_char = strdup(show_char); - e->pad_indent = pad_indent; - e->flags = flags; - e->theme_item = theme_item; - e->time = g_date_time_ref(time); - e->display_from = display_from ? strdup(display_from) : NULL; - e->from_jid = from_jid ? strdup(from_jid) : NULL; - e->message = strdup(message); - e->receipt = receipt; - if (id) { - e->id = strdup(id); - } else { - e->id = NULL; - } - - if (g_slist_length(buffer->entries) == BUFF_SIZE) { - _free_entry(buffer->entries->data); - buffer->entries = g_slist_delete_link(buffer->entries, buffer->entries); - } - - buffer->entries = g_slist_append(buffer->entries, e); + _buffer_add(buffer, show_char, pad_indent, time, flags, theme_item, display_from, from_jid, message, receipt, id, y_start_pos, y_end_pos, TRUE); } void -buffer_prepend(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id) +buffer_prepend(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos) { - ProfBuffEntry* e = malloc(sizeof(struct prof_buff_entry_t)); - e->show_char = strdup(show_char); - e->pad_indent = pad_indent; - e->flags = flags; - e->theme_item = theme_item; - e->time = g_date_time_ref(time); - e->display_from = display_from ? strdup(display_from) : NULL; - e->from_jid = from_jid ? strdup(from_jid) : NULL; - e->message = strdup(message); - e->receipt = receipt; - if (id) { - e->id = strdup(id); - } else { - e->id = NULL; - } + _buffer_add(buffer, show_char, pad_indent, time, flags, theme_item, display_from, from_jid, message, receipt, id, y_start_pos, y_end_pos, FALSE); +} - if (g_slist_length(buffer->entries) == BUFF_SIZE) { - GSList* last = g_slist_last(buffer->entries); - _free_entry(last->data); - buffer->entries = g_slist_delete_link(buffer->entries, last); +static void +_buffer_add(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos, gboolean append) +{ + ProfBuffEntry* e = _create_entry(show_char, pad_indent, time, flags, theme_item, display_from, from_jid, message, receipt, id, y_start_pos, y_end_pos); + + buffer->lines += e->_lines; + int bl = buffer->lines; // DBG + + while (g_slist_length(buffer->entries) > 11 && (g_slist_length(buffer->entries) >= BUFF_SIZE || buffer->lines > BUFF_SIZE_LINES)) { + GSList* buffer_entry_to_delete = append ? buffer->entries : g_slist_last(buffer->entries); + ProfBuffEntry* entry_to_delete = (ProfBuffEntry*)buffer_entry_to_delete->data; + buffer->lines -= entry_to_delete->_lines; + // DEBUG, remove comment and `log_debug`. + log_debug("deleting entry %d; new amount of lines: %d; lines cleaned up: %d", entry_to_delete->id, buffer->lines, entry_to_delete->_lines); + _free_entry(entry_to_delete); + buffer->entries = g_slist_delete_link(buffer->entries, buffer_entry_to_delete); } + // DEBUG, remove comment and `log_debug`. + log_debug("%s; new end pos: %d, old_buff_lines: %d, buff_lines: %d, buff entries: %d", append ? "append" : "prepend", y_end_pos, bl, buffer->lines, g_slist_length(buffer->entries)); - buffer->entries = g_slist_prepend(buffer->entries, e); + buffer->entries = append ? g_slist_append(buffer->entries, e) : g_slist_prepend(buffer->entries, e); } void @@ -145,6 +134,7 @@ buffer_remove_entry_by_id(ProfBuff buffer, const char* const id) while (entries) { ProfBuffEntry* entry = entries->data; if (entry->id && (g_strcmp0(entry->id, id) == 0)) { + buffer->lines -= entry->_lines; _free_entry(entry); buffer->entries = g_slist_delete_link(buffer->entries, entries); break; @@ -157,6 +147,8 @@ void buffer_remove_entry(ProfBuff buffer, int entry) { GSList* node = g_slist_nth(buffer->entries, entry); + ProfBuffEntry* e = node->data; + buffer->lines -= e->_lines; _free_entry(node->data); buffer->entries = g_slist_delete_link(buffer->entries, node); } @@ -201,6 +193,25 @@ buffer_get_entry_by_id(ProfBuff buffer, const char* const id) return NULL; } +static ProfBuffEntry* _create_entry(const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos) { + ProfBuffEntry* e = malloc(sizeof(struct prof_buff_entry_t)); + e->show_char = STRDUP_OR_NULL(show_char); + e->pad_indent = pad_indent; + e->flags = flags; + e->theme_item = theme_item; + e->time = g_date_time_ref(time); + e->display_from = STRDUP_OR_NULL(display_from); + e->from_jid = STRDUP_OR_NULL(from_jid); + e->message = STRDUP_OR_NULL(message); + e->receipt = receipt; + e->id = STRDUP_OR_NULL(id); + e->y_start_pos = y_start_pos; + e->y_end_pos = y_end_pos; + e->_lines = e->y_end_pos - e->y_start_pos; + + return e; +} + static void _free_entry(ProfBuffEntry* entry) { diff --git a/src/ui/buffer.h b/src/ui/buffer.h index 591c48812..cf9b056ca 100644 --- a/src/ui/buffer.h +++ b/src/ui/buffer.h @@ -52,6 +52,10 @@ typedef struct prof_buff_entry_t // pointer because it could be a unicode symbol as well gchar* show_char; int pad_indent; + // curses positions before and after message is displayed (used for scrolling offset) + int y_start_pos; + int y_end_pos; + int _lines; GDateTime* time; int flags; theme_item_t theme_item; @@ -69,8 +73,8 @@ typedef struct prof_buff_t* ProfBuff; ProfBuff buffer_create(); void buffer_free(ProfBuff buffer); -void buffer_append(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const barejid, const char* const message, DeliveryReceipt* receipt, const char* const id); -void buffer_prepend(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const barejid, const char* const message, DeliveryReceipt* receipt, const char* const id); +void buffer_append(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const barejid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos); +void buffer_prepend(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const barejid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos); void buffer_remove_entry_by_id(ProfBuff buffer, const char* const id); void buffer_remove_entry(ProfBuff buffer, int entry); int buffer_size(ProfBuff buffer); diff --git a/src/ui/window.c b/src/ui/window.c index 348bfeaa6..d8e7ddd6f 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -626,14 +626,15 @@ void win_page_up(ProfWin* window) { _reached_bottom_of_database = FALSE; - int rows = getmaxy(stdscr); - int y = getcury(window->layout->win); - int page_space = rows - 4; - int* page_start = &(window->layout->y_pos); + int page_space = getmaxy(stdscr) - 4; + int curses_buffer_size = getcury(window->layout->win); + int* curses_buffer_pos = &(window->layout->y_pos); + // DEBUG, remove comment and change. + log_debug("page_up-1; page_space=%d, curses_buffer_size=%d, curses_buffer_pos=%d, buffer_size=%d", page_space, getcury(window->layout->win), *curses_buffer_pos, buffer_size(window->layout->buffer)); - *page_start -= page_space; + *curses_buffer_pos -= page_space; - if (*page_start == -page_space && window->type == WIN_CHAT) { + if (*curses_buffer_pos == -page_space && window->type == WIN_CHAT) { ProfChatWin* chatwin = (ProfChatWin*)window; ProfBuffEntry* first_entry = buffer_size(window->layout->buffer) != 0 ? buffer_get_entry(window->layout->buffer, 0) : NULL; @@ -647,18 +648,26 @@ win_page_up(ProfWin* window) win_print_loading_history(window); iq_mam_request_older(chatwin); } + + int buff_size = buffer_size(window->layout->buffer); + int offset_entry_id = buff_size > 10 ? 10 : buff_size - 1; + int offset = buffer_get_entry(window->layout->buffer, offset_entry_id)->y_end_pos; + *curses_buffer_pos = offset - page_space; } } - // went past beginning, show first page - if (*page_start < 0) - *page_start = 0; + // Went past beginning, set to 0 + if (*curses_buffer_pos < 0) { + *curses_buffer_pos = 0; + } window->layout->paged = 1; win_update_virtual(window); + // DEBUG, remove comment and change. + log_debug("page_up-2; page_space=%d, curses_buffer_size=%d, new_curses_buffer_size=%d, curses_buffer_pos=%d, buffer_size=%d", page_space, curses_buffer_size, getcury(window->layout->win), *curses_buffer_pos, buffer_size(window->layout->buffer)); // switch off page if last line and space line visible - if ((y) - *page_start == page_space) { + if ((curses_buffer_size) - *curses_buffer_pos == page_space) { window->layout->paged = 0; } } @@ -668,41 +677,53 @@ win_page_down(ProfWin* window) { _reached_top_of_database = FALSE; int rows = getmaxy(stdscr); - int y = getcury(window->layout->win); + int lines_ncurses_buffered = getcury(window->layout->win); int page_space = rows - 4; int* page_start = &(window->layout->y_pos); + // DEBUG, remove comment and change. + log_debug("page_down-1; page_space=%d, lines_ncurses_buffered=%d, pos_in_buffer=%d, buffer_size=%d", page_space, lines_ncurses_buffered, *page_start, buffer_size(window->layout->buffer)); *page_start += page_space; // Scrolled down after reaching the bottom of the page - if ((*page_start == y || (*page_start == page_space && *page_start >= y)) && window->type == WIN_CHAT) { + if ((*page_start == lines_ncurses_buffered || (*page_start == page_space && *page_start >= lines_ncurses_buffered)) && window->type == WIN_CHAT) { int bf_size = buffer_size(window->layout->buffer); if (bf_size > 0) { - auto_gchar gchar* start = g_date_time_format_iso8601(buffer_get_entry(window->layout->buffer, bf_size - 1)->time); + ProfBuffEntry* last_entry = buffer_get_entry(window->layout->buffer, bf_size - 1); + auto_gchar gchar* start = g_date_time_format_iso8601(last_entry->time); GDateTime* now = g_date_time_new_now_local(); - gchar* end = g_date_time_format_iso8601(now); + gchar* end_date = g_date_time_format_iso8601(now); // end is free'd inside - if (!_reached_bottom_of_database && !chatwin_db_history((ProfChatWin*)window, start, end, FALSE)) { + if (!_reached_bottom_of_database && !chatwin_db_history((ProfChatWin*)window, start, end_date, FALSE)) { _reached_bottom_of_database = TRUE; } g_date_time_unref(now); + + // `last_entry->y_end_pos` is recalculated after `win_redraw` called in `chatwin_db_history` + int offset = last_entry->y_end_pos - 1; + *page_start = offset; } } + lines_ncurses_buffered = getcury(window->layout->win); + // only got half a screen, show full screen - if ((y - (*page_start)) < page_space) - *page_start = y - page_space; + if ((lines_ncurses_buffered - (*page_start)) < page_space) + *page_start = lines_ncurses_buffered - page_space; // went past end, show full screen - else if (*page_start >= y) - *page_start = y - page_space - 1; + else if (*page_start >= lines_ncurses_buffered) + *page_start = lines_ncurses_buffered - page_space - 1; window->layout->paged = 1; win_update_virtual(window); + // DEBUG, remove comment and change. + log_debug("page_down-2; page_space=%d, lines_ncurses_buffered=%d, pos_in_buffer=%d, buffer_size=%d", page_space, lines_ncurses_buffered, *page_start, buffer_size(window->layout->buffer)); + // switch off page if last line and space line visible - if ((y) - *page_start == page_space) { + if ((lines_ncurses_buffered) - *page_start == page_space) { window->layout->paged = 0; } } @@ -1468,10 +1489,11 @@ win_print_history(ProfWin* window, const ProfMessage* const message) auto_gchar gchar* display_name = get_display_name(message, &flags); auto_char char* ch = get_show_char(message->enc); - buffer_append(window->layout->buffer, ch, 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, NULL, message->plain, NULL, NULL); wins_add_urls_ac(window, message, FALSE); wins_add_quotes_ac(window, message->plain, FALSE); + int y_start_pos = getcury(window->layout->win); _win_print_internal(window, ch, 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, message->plain, NULL); + buffer_append(window->layout->buffer, ch, 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, NULL, message->plain, NULL, NULL, y_start_pos, getcury(window->layout->win)); inp_nonblocking(TRUE); g_date_time_unref(message->timestamp); @@ -1487,10 +1509,11 @@ win_print_old_history(ProfWin* window, const ProfMessage* const message) auto_char char* ch = get_show_char(message->enc); - buffer_prepend(window->layout->buffer, ch, 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, NULL, message->plain, NULL, NULL); + int y_start_pos = getcury(window->layout->win); wins_add_urls_ac(window, message, TRUE); wins_add_quotes_ac(window, message->plain, TRUE); _win_print_internal(window, ch, 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, message->plain, NULL); + buffer_prepend(window->layout->buffer, ch, 0, message->timestamp, flags, THEME_TEXT_HISTORY, display_name, NULL, message->plain, NULL, NULL, y_start_pos, getcury(window->layout->win)); inp_nonblocking(TRUE); g_date_time_unref(message->timestamp); @@ -1503,8 +1526,9 @@ win_println_va_internal(ProfWin* window, theme_item_t theme_item, int pad, int f auto_gchar gchar* msg = g_strdup_vprintf(message, arg); - buffer_append(window->layout->buffer, show_char, pad, timestamp, flags, theme_item, "", NULL, msg, NULL, NULL); + int y_start_pos = getcury(window->layout->win); _win_print_internal(window, show_char, pad, timestamp, flags, theme_item, "", msg, NULL); + buffer_append(window->layout->buffer, show_char, pad, timestamp, flags, theme_item, "", NULL, msg, NULL, NULL, y_start_pos, getcury(window->layout->win)); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1597,8 +1621,9 @@ win_print_outgoing_with_receipt(ProfWin* window, const char* show_char, const ch if (_win_correct(window, message, id, replace_id, myjid)) { free(receipt); // TODO: probably we should use this in _win_correct() } else { - buffer_append(window->layout->buffer, show_char, 0, time, 0, THEME_TEXT_ME, from, myjid, message, receipt, id); + int y_start_pos = getcury(window->layout->win); _win_print_internal(window, show_char, 0, time, 0, THEME_TEXT_ME, from, message, receipt); + buffer_append(window->layout->buffer, show_char, 0, time, 0, THEME_TEXT_ME, from, myjid, message, receipt, id, y_start_pos, getcury(window->layout->win)); } // TODO: cross-reference.. this should be replaced by a real event-based system @@ -1657,8 +1682,9 @@ _win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* t auto_gchar gchar* msg = g_strdup_vprintf(message, arg); - buffer_append(window->layout->buffer, show_char, pad_indent, timestamp, flags, theme_item, display_from, from_jid, msg, NULL, message_id); + int y_start_pos = getcury(window->layout->win); _win_print_internal(window, show_char, pad_indent, timestamp, flags, theme_item, display_from, msg, NULL); + buffer_append(window->layout->buffer, show_char, pad_indent, timestamp, flags, theme_item, display_from, from_jid, msg, NULL, message_id, y_start_pos, getcury(window->layout->win)); inp_nonblocking(TRUE); g_date_time_unref(timestamp); @@ -1937,13 +1963,13 @@ win_print_trackbar(ProfWin* window) void win_redraw(ProfWin* window) { - int size; + int size = buffer_size(window->layout->buffer); werase(window->layout->win); - size = buffer_size(window->layout->buffer); for (int i = 0; i < size; i++) { ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i); + e->y_start_pos = getcury(window->layout->win); if (e->display_from == NULL && e->message && e->message[0] == '-') { // just an indicator to print the trackbar/separator not the actual message win_print_trackbar(window); @@ -1951,6 +1977,7 @@ win_redraw(ProfWin* window) // regular thing to print _win_print_internal(window, e->show_char, e->pad_indent, e->time, e->flags, e->theme_item, e->display_from, e->message, e->receipt); } + e->y_end_pos = getcury(window->layout->win); } } @@ -1966,7 +1993,8 @@ win_print_loading_history(ProfWin* window) timestamp = g_date_time_new_now_local(); } - buffer_prepend(window->layout->buffer, "-", 0, timestamp, NO_DATE, THEME_ROOMINFO, NULL, NULL, LOADING_MESSAGE, NULL, NULL); + int cur_y = getcury(window->layout->win); + buffer_prepend(window->layout->buffer, "-", 0, timestamp, NO_DATE, THEME_ROOMINFO, NULL, NULL, LOADING_MESSAGE, NULL, NULL, cur_y, cur_y + 1); if (is_buffer_empty) g_date_time_unref(timestamp); @@ -2184,7 +2212,8 @@ win_insert_last_read_position_marker(ProfWin* window, char* id) // the trackbar/separator will actually be print in win_redraw(). // this only puts it in the buffer and win_redraw() will interpret it. // so that we have the correct length even when resizing. - buffer_append(window->layout->buffer, " ", 0, time, 0, THEME_TEXT, NULL, NULL, "-", NULL, id); + int y_start_pos = getcury(window->layout->win); + buffer_append(window->layout->buffer, " ", 0, time, 0, THEME_TEXT, NULL, NULL, "-", NULL, id, y_start_pos, getcury(window->layout->win)); win_redraw(window); g_date_time_unref(time);