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.
This commit is contained in:
OlegTheSnowman
2026-07-11 00:39:44 +03:00
parent b95f743bb2
commit 466be8a544

View File

@@ -407,11 +407,7 @@ end
-- Note: text should use "\r\n" line endings, not bare "\n" - the plain -- 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" -- Win32 Edit control that backs the notepad doesn't recognise bare "\n"
-- as a line break for scrolling/line-count purposes. -- as a line break for scrolling/line-count purposes.
last_append_clock = 0
function SmartAppendToNotepad(title, text) function SmartAppendToNotepad(title, text)
last_append_clock = os.clock()
local edit = find_notepad_edit(title) local edit = find_notepad_edit(title)
local was_at_end = true local was_at_end = true
local sel_start, sel_end, first_visible local sel_start, sel_end, first_visible
@@ -459,36 +455,49 @@ end
-- while a separate notepad window has focus (confirmed by Nick Gammon on -- while a separate notepad window has focus (confirmed by Nick Gammon on
-- the MUSHclient forum), so this can't be event-driven - polling every -- the MUSHclient forum), so this can't be event-driven - polling every
-- 0.2s is the safe, non-hooking way to catch it. -- 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 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() function poll_output_focus()
if GetVariable("jump_to_end_on_focus") == "0" then if GetVariable("jump_to_end_on_focus") == "0" then
was_output_focused = false was_output_focused = false
unfocused_streak = 0
return return
end end
local edit = find_notepad_edit("output") local edit = find_notepad_edit("output")
if not edit then if not edit then
was_output_focused = false was_output_focused = false
unfocused_streak = 0
return return
end end
local focused = GetFocus() local focused = GetFocus()
-- Compare by string representation, not raw "==": Alien's pointer -- Compare by string representation, not raw "==": Alien's pointer
-- returns are full userdata objects, so two separate calls returning -- returns are full userdata objects, so two separate calls returning
-- the same real HWND don't reliably compare equal with "==" (which -- the same real HWND don't reliably compare equal with "==" (which
-- fell back to identity comparison), causing spurious "just gained -- falls back to identity comparison).
-- focus" edges and repeated jump-to-end even while focus never moved.
local is_focused = (tostring(focused) == tostring(edit)) 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 if is_focused then
-- perturb GetFocus() around each append, which was making this poll if not was_output_focused and unfocused_streak >= FOCUS_DEBOUNCE_TICKS then
-- see a spurious "just gained focus" edge - and jump to the end - on jump_notepad_to_end(edit)
-- practically every incoming line, even though the user never left end
-- the window. unfocused_streak = 0
local recent_append = (os.clock() - last_append_clock) < 0.5 was_output_focused = true
if is_focused and not was_output_focused and not recent_append then else
jump_notepad_to_end(edit) unfocused_streak = unfocused_streak + 1
was_output_focused = false
end end
was_output_focused = is_focused
end end
------------------------------------------------------------------ ------------------------------------------------------------------