Skip to content

Commit

Permalink
Fix overscrolling issue
Browse files Browse the repository at this point in the history
Before there was a problem of overscrolling:
when messages longer than y axis of the terminal are fetched from the DB,
profanity scroll "jumps" to the top, skipping some messages.

It's resolved by keeping messages' starting and ending line in the
internal profanity buffer, which allows to track proper message positions
and to adjust window position accordingly.

Message size is now tracked as part of the buffer's record in `_line`
variable, which allows calculation of the total buffer size, which
is a part of the improved solution for the "underscrolling" problem.

Underscrolling problem was fixed in a previous commit
d7e46d6
Short recap of the problem:
Despite user scrolling to top/bottom of history,
factual position is offset from the intended location

Another feature of this commit is a minor change which adds fetching
message stanza IDs from the DB. It allows correcting messages
fetched from history.

Fixes #1934
  • Loading branch information
H3rnand3zzz committed Dec 12, 2023
1 parent 9ef05f4 commit e4a33aa
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 77 deletions.
4 changes: 3 additions & 1 deletion src/database.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ log_database_get_previous_chat(const gchar* const contact_barejid, const char* s
auto_gchar gchar* end_date_fmt = end_time ? end_time : g_date_time_format_iso8601(now);
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 "
"A.`timestamp`, A.`from_jid`, A.`to_jid`, A.`type`, A.`encryption`, A.`stanza_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)) "
Expand Down Expand Up @@ -371,8 +371,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);
char* id = (char*)sqlite3_column_text(stmt, 6);

ProfMessage* msg = message_init();
msg->id = id ? strdup(id) : NULL;
msg->from_jid = jid_create(from);
msg->to_jid = jid_create(to_jid);
msg->plain = strdup(message ?: "");
Expand Down
101 changes: 57 additions & 44 deletions src/ui/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,31 @@
#include <curses.h>
#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;
}

Expand All @@ -84,58 +92,39 @@ 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);
}

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;

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;
_free_entry(entry_to_delete);
buffer->entries = g_slist_delete_link(buffer->entries, buffer_entry_to_delete);
}

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);
// if amount of lines in the message exceeds available ncurses' limit, we get the same line at start and end
// (e.g. 9999, 9999), despite message length; if `from_jid` is empty, then it's probably just inline text
if (from_jid && y_end_pos == y_start_pos) {
log_warning("Ncurses Overflow! from: %s, pos: %d, ID: %s, message: %s", from_jid, y_end_pos, id, message);
}

buffer->entries = g_slist_prepend(buffer->entries, e);
buffer->entries = append ? g_slist_append(buffer->entries, e) : g_slist_prepend(buffer->entries, e);
}

void
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -201,6 +193,27 @@ 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)
{
Expand Down
8 changes: 6 additions & 2 deletions src/ui/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Loading

0 comments on commit e4a33aa

Please sign in to comment.