From 466be8a544eb28bc88b9f5501c1d6e6f5cfe21c0 Mon Sep 17 00:00:00 2001 From: OlegTheSnowman <123775785+OlegTheSnowman@users.noreply.github.com> Date: Sat, 11 Jul 2026 00:39:44 +0300 Subject: [PATCH] Fix jump-to-end firing on every line: debounce sustained unfocus, not recency Debug logging disproved the previous "recent append perturbs GetFocus" theory - one spurious jump fired 7.67s after the last append. The real pattern: GetFocus() reports "not focused" for a single poll tick now and then even while the user never left the window, and that lone blip was enough for the very next tick to be read as "just switched to the window," triggering a jump on practically every incoming line. Now requires 3 consecutive unfocused poll ticks (~0.6s) before the next focused tick counts as a genuine switch, filtering out single-tick noise from MUSHclient's own UI thread. Removed the now-unused last_append_clock tracking from the previous (wrong) fix attempt. --- .../worlds/plugins/output_functions.xml | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/cosmic rage/worlds/plugins/output_functions.xml b/cosmic rage/worlds/plugins/output_functions.xml index 84c23cf..7eb8895 100644 --- a/cosmic rage/worlds/plugins/output_functions.xml +++ b/cosmic rage/worlds/plugins/output_functions.xml @@ -407,11 +407,7 @@ end -- Note: text should use "\r\n" line endings, not bare "\n" - the plain -- Win32 Edit control that backs the notepad doesn't recognise bare "\n" -- as a line break for scrolling/line-count purposes. -last_append_clock = 0 - function SmartAppendToNotepad(title, text) - last_append_clock = os.clock() - local edit = find_notepad_edit(title) local was_at_end = true local sel_start, sel_end, first_visible @@ -459,36 +455,49 @@ end -- while a separate notepad window has focus (confirmed by Nick Gammon on -- the MUSHclient forum), so this can't be event-driven - polling every -- 0.2s is the safe, non-hooking way to catch it. +-- +-- GetFocus() genuinely reports "not focused" for a single poll tick now +-- and then, even while the user never left the window and nothing was +-- just appended (confirmed via logging - one case was 7+ seconds after +-- the last append) - just noise from MUSHclient's own UI thread being +-- momentarily busy. A single such blip was enough to make this treat +-- the very next tick as "just switched to the window" and jump, on +-- practically every incoming line. Requiring several *consecutive* +-- unfocused ticks before the next focused tick counts as a genuine +-- switch filters that out. was_output_focused = false +unfocused_streak = 0 +FOCUS_DEBOUNCE_TICKS = 3 -- ~0.6s of sustained unfocus at the 0.2s poll rate function poll_output_focus() if GetVariable("jump_to_end_on_focus") == "0" then was_output_focused = false + unfocused_streak = 0 return end local edit = find_notepad_edit("output") if not edit then was_output_focused = false + unfocused_streak = 0 return end local focused = GetFocus() -- Compare by string representation, not raw "==": Alien's pointer -- returns are full userdata objects, so two separate calls returning -- the same real HWND don't reliably compare equal with "==" (which - -- fell back to identity comparison), causing spurious "just gained - -- focus" edges and repeated jump-to-end even while focus never moved. + -- falls back to identity comparison). local is_focused = (tostring(focused) == tostring(edit)) - -- Skip the jump if a SmartAppendToNotepad call happened very recently: - -- AppendToNotepad's own SetSel/ReplaceSel appears to momentarily - -- perturb GetFocus() around each append, which was making this poll - -- see a spurious "just gained focus" edge - and jump to the end - on - -- practically every incoming line, even though the user never left - -- the window. - local recent_append = (os.clock() - last_append_clock) < 0.5 - if is_focused and not was_output_focused and not recent_append then - jump_notepad_to_end(edit) + + if is_focused then + if not was_output_focused and unfocused_streak >= FOCUS_DEBOUNCE_TICKS then + jump_notepad_to_end(edit) + end + unfocused_streak = 0 + was_output_focused = true + else + unfocused_streak = unfocused_streak + 1 + was_output_focused = false end - was_output_focused = is_focused end ------------------------------------------------------------------