- Track unread message indicators and paging state in the window component
- Clear and restore unread markers correctly when navigating between conversations
- Suppress buffer updates and message printing while viewing history to prevent
unwanted scroll jumps and viewport lock
Also includes minor code-style, whitespace, and comment cleanups as well as
updated documentation for metrics and buffer handling.
Prior commit (6ad8a190) did not properly handle overflow by long (9000 lines) message,
to address this issue, multiple changes were made:
- Add lines recalculation on win_redraw
- Move `prof_buff_t` struct to header file so its lines could be externally changed
- Call win_redraw once overflow is detected: it allows to recalculate sizes in lines and apply changes to the buffer
Refactor buffer trimming logic to use dynamic line counts (_lines per entry)
against PAD_SIZE (10k) instead of fixed entry count (MAX_BUFFER_SIZE=200). This
prevents premature cleanups for short messages, reduces scrolling disruptions
during history loads, and scales better with rendered content size.
Delete oldest/newest entry from opposite end only when total lines approach
ncurses pad limit, minimizing unnecessary deletions.
Simplify overflow warning log by removing unused MAX_BUFFER_SIZE reference.
Update buffer.c header for improved DX:
- Add detailed module overview explaining role (in-memory UI buffer vs. DB
persistence), key features (trimming, metadata), and integration (ncurses).
- Shorten copyright/license boilerplate to concise pointer (LICENSE ref).
- Add fork notice; preserve original copyrights per GPLv3.
Partially addresses #36 (stuck scroll mitigation via fewer cleanups)
Related to #36
When running profanity under Valgrind with slashguard enabled, the
following error could occur:
```
[...]
==4021347== Invalid read of size 1
==4021347== at 0x4851F49: memchr (vg_replace_strmem.c:986)
==4021347== by 0x45CEAD: _inp_slashguard_check (inputwin.c:183)
==4021347== by 0x45CEAD: inp_readline (inputwin.c:225)
==4021347== by 0x431184: prof_run (profanity.c:121)
==4021347== by 0x42C609: main (main.c:176)
==4021347== Address 0xe850883 is 0 bytes after a block of size 3 alloc'd
==4021347== at 0x48477C4: malloc (vg_replace_malloc.c:446)
[...]
```
`memchr()` requires the complete memory that shall be searched to be
accessible. Using `strchr()` could work for shorter strings, but we only
want to search in the first 4 chars.
Instead of somehow working around those limitations, simply search manually
in the first 4 bytes.
Fixes: 3c56b289 ("Add slashguard feature")
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
(cherry picked from commit 7f48452d84)
- Modified chatwin_outgoing_msg to call _chatwin_set_last_message for corrected messages, using replace_id per XEP-0308.
- Updated _chatwin_set_last_message to skip redundant and invalid free/strdup for same pointers.
- Added null checks in _chatwin_set_last_message for safety.
- Fixes autocompletion suggesting original message content instead of corrected content.
Fixes#24
Implement /force-encryption command to configure forced encryption
settings with on|off and policy resend-to-confirm|block options.
The changes enhance user experience by clarifying encryption requirements
and providing actionable commands. Users can now press Enter again to
confirm unencrypted messages in resend-to-confirm mode.
The synchronous `get_message_from_editor` blocked the main loop (`prof_run`) while launching an external editor like `vim`, halting network I/O in `session_process_events` and preventing incoming message reception.
Introduced `get_message_from_editor_async` for `cmd_editor` to run the editor asynchronously. It forks and execs the editor in a thread (`editor_thread`), suspending NCurses to free the terminal. The main loop skips `inp_readline` and `ui_update` via a new `background_mode` flag while the editor runs, allowing `session_process_events` to keep the connection alive.
On editor completion, `editor_process` (called per loop iteration) resumes NCurses with `ui_resize`, inserts the result into the readline buffer and clears `background_mode`.
Retained synchronous `get_message_from_editor` for ~20 existing code paths (e.g., `vcard_nickname`) to avoid breaking them.
Tested with `vim` and `nano`: confirms no rendering conflicts, messages received during editing, and seamless resume. Edge cases like editor crashes handled via error logging and seamless resume.
When paging down beyond the visible chat buffer, the scroll position could
jump unexpectedly. This occurred because the visual distance from the bottom
(e.g. 6 lines until the last message) wasn’t preserved after loading new
messages from the DB.
This patch fixes that by capturing the visual offset (`current_offset`) before
the DB fetch and reapplying it after, based on the y-position of the final
buffer entry. This ensures consistent and smooth scroll transitions, even
as message heights vary.
In addition to fixing the visual glitch, this commit introduces a targeted
performance optimization: once the bottom of the chat has been reached
(`WIN_SCROLL_REACHED_BOTTOM`), we skip expensive operations entirely —
including datetime lookups and entry formatting — during further page_down
events. This reduces performance stalls
during rapid paging (e.g. holding the Page Down key at the bottom of the scrolling area).
This update also adapts to the recent change in `chatwin_db_history()`,
which now returns a `db_history_result_t` instead of `gboolean`, allowing
correct differentiation between an empty result and an actual DB error.
Together, these changes:
- Fix a scroll-jump bug in `page_down`
- Improve performance and responsiveness during rapid navigation
- Align with improved DB result handling
- Introduce static helper `_truncate_datetime_suffix()` to safely trim datetime strings, removing unwanted suffixes like timezone offsets
- Replace manual string management with auto_gchar and g_strdup for safer, clearer ownership and to prevent leaks
- Add safety checks and logging warnings for unexpected datetime string lengths or null pointers
- Refactor _mam_rsm_id_handler to use the helper function and updated string handling
- Change log_database_get_previous_chat parameters for consistent ownership semantics, avoiding double frees and mem leaks
- Overall improve stability and prevent memory leaks during log database queries
This commit refines the existing logic in win_page_up() by:
- Improving comments to clearly explain the rationale behind adjusting
the scroll offset relative to the first buffer entry’s visual position,
helping future maintainers understand why this is necessary.
- Fixing offset recalculation to better handle cases where older messages
with variable heights are loaded from the DB, improving scroll smoothness.
- Changing the logging of negative *page_start values from warning to debug,
recognizing that this can be a normal scenario when insufficient history is loaded.
- Simplifying some conditionals and renaming variables for clearer intent.
No changes yet applied to win_page_down(), but similar improvements could
be considered in the future.
Overall, this enhances the robustness and user experience of scrolling up
in chat windows, while preserving existing functional logic.
f27fa98 commit introduced issue of scrolling seemingly randomly being stuck and the following message flood in the logs: "WRN: Ncurses Overflow..."
A pad is an off-screen buffer — larger than the visible terminal screen — that you can scroll, render parts of, or update selectively.
Unlike regular windows, a pad is not tied to screen size. You can create a pad that's 1,000 lines tall, even if your terminal is only 40 lines tall.
This commit addresses the issue by increasing PAD_SIZE to a reasonable number,
allowing messages from buffer to be displayed without overflowing NCurses' window size (maxy).
Minor change: add .cache folder to gitignore
> The returned data is valid until changes to the hash release those keys.
As of the doc of `g_hash_table_get_keys()` [0].
> The returned data is valid until hash_table is modified.
As of the doc of `g_hash_table_get_values()` [1].
Use this property and create the lists when modifying the hashtable.
The `keys` list is most of the time used sorted, so sort it immediately. In
the cases where it was not used sorted, it does not matter.
This started off by looking into whether this can be improved.
`g_hash_table_foreach()` is discouraged to be used and proposes to use
a `GHashTableIter`. Since our lists are not modified very often it does not
really make sense, so I decided to keep using lists, but allocating
those lists statically instead of per invocation.
Instead of using a list, we could maybe use the `GArray` version, but
I am not sure whether it wouldn't be even better to replace the
`GHashTable` all along to store the `windows` instead.
[0] https://docs.gtk.org/glib/type_func.HashTable.get_keys.html
[1] https://docs.gtk.org/glib/type_func.HashTable.get_values.html
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
In order to keep the C plugins API stable, we have to revert this part of
the changes done in 16ed7cc187.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* Add an internal `_notifier_uninit()` function.
* Instead of `#ifdef` inside `notify()`, provide different target-specific
static implementations.
This also introduces `log_error_once()`.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
While trying to get the unit tests working again I stumbled over all those
things that I thought could be better^TM.
Now we also know "TODO: why does this make the test fail?" - because
the unit tests are brittle AF ... and we have to init the subsystems
we use in the test, otherwise the cleanup will fail...
BTW. you can now also only run a single test ... or a pattern or so ...
you'd have to read how `cmocka_set_test_filter()` works exactly.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* destroy/free/shutdown/close in reverse order of allocation
* more static/auto_Xfree
* less variables/strlen/GString
* properly `\0`-terminate string of testcase
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
Instead of adding stuff to `_shutdown()`, we can now register a shutdown
routine from the respective `init()` function of our modules.
This also has the advantage, that we're sure they're called in reverse
order from how the initialization happened.
I didn't simply use `atexit()` because POSIX says that the number of
possible callbacks is limited (min 32) and I was not sure whether
we will maybe extend this number at one point.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
* Fix some linter suggestions.
* Change some defines to `const` vars.
* Free buffer entries in the reverse order they were allocated.
Signed-off-by: Steffen Jaeckel <s@jaeckel.eu>
In the documentation of the `muc_nick` function we can read:
> The nickname is owned by the chat room and should not be modified or freed
We should reflect that in the return type and `strdup` the value for the
plugin API.
Fixes: https://github.com/profanity-im/profanity/issues/2013
When using `/statusbar tabmode actlist` and `/statusbar self user` then we get `[00:23] [user]` and now `[00:23] [1:user]` where `1` is the active tab. The active tab is only displayed with fulljid.
This commit fixes 7d290b04d.
Fix https://github.com/profanity-im/profanity/issues/1980
This lets users use the actlist and decide if they want to see name or
numbers.
The old behaviour can be achieved with:
```
/statusbar hide read
/statusbar hide name
/statusbar show number
/statusbar tabmode actlist
```
Fix https://github.com/profanity-im/profanity/issues/1974
Revert "Merge pull request #1943 from H3rnand3zzz/gmainloop
This reverts commit 609fde0998, reversing
changes made to 2ec94064ed.
Revert "Merge pull request #1948 from H3rnand3zzz/fix/rl-less-refreshes"
This reverts commit 11762fd2b0, reversing
changes made to 609fde0998.
We have got several issues, that we don't quite see how to solve, with
the merge of the gmainloop PR.
* Slashguard is broken (#1955) (though #1956 could fix that)
* One person reported problems with copy paste selection via mouse
* Some input buffer seems not to be cleared correctly
It happened that I was debugging profanity used `/connect` and typed
the password. I then debugged so long that a time out occurred, so
profanity disconnected. Then it printed "unknown command: $password".
There was something else that I forgot now.
Bottomline is: so far we didn't get it right so we will undo these
changes until someone proposes a working solution.
We got a slight performance increase (apparently noticable when
alt+mouse scrolling) but got too many issues with this change.
When user was scrolling down using alt + mouse wheel/arrows, he would be
stuck in most of the cases after the buffer finishes
due to the part of code that moves the "cursor" up to show full last page.
New algorithm considers close to border cursor as passing for
DB loading condition,
at the same time it calculates offset, allowing flexible scroll size.
The problem was introduced by @H3rnand3zzz in the following commit:
23692fedff
The redisplay function in keyboard handling event was introduced by @rodarima
in the following commit:
7eac636fc8
At the point first commit introduction it made sense, but later
the following commit added proper handling of input redisplay.
ccede06a65
This change was overlooked by me and introduced in #1943.
Potential solution for #1947.
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
might be a part of the improved solution for the "underscrolling" problem,
if we are going to limit profanity's buffer size by amount of lines as
opposed to the limitation based on the amount of message which is currently
used.
Before adding a limitation by amount of lines, careful consideration is
required, as some users don't use history and their temporary message
history can be cut to minimal limit because of 1 long received/sent message.
Underscrolling problem was fixed in a previous commit
d7e46d64fe
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 https://github.com/profanity-im/profanity/issues/1934