Browse Source

punk::console 0.7.1: compound emit-then-query operations flush before querying

get_size on tcl 8.6 returned 'columns 1' (real size e.g. 260x49): with the
0.7.1 console-detection fixes in place, 8.6 takes the ANSI size mechanism
(no -winsize shortcut) for the first time under G-007 routing - and that
mechanism is compound: the far-corner cursor move was written unflushed
into the calling thread's stdout channel instance while the routed position
query executed and flushed in the console-owning thread's separate instance
of the same OS handle. The terminal answered before the move reached it.
Pre-routing this was masked because emit and query shared one channel
instance, so the query's own flush pushed the emissions too.

All compound emit-then-query operations now flush their emissions before
querying: get_size_using_cursormove and get_size_using_cursorrestore (the
far-corner move), test_char_width (both the positioning emission and the
measured test emission - the latter would silently corrupt character-width
results), and test_string_cursor (alt-screen/move/erase). Error paths flush
their cursor-restore emissions likewise.

Interactively verified on punksys (tcl 8.6.13, src launch): get_size
reports the true terminal dimensions and test_char_width \t returns 8.
Full suite passes at baseline (exec-14.3 only) under Tcl 9.0.3.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
c19c1167c8
  1. 20
      src/modules/punk/console-999999.0a1.0.tm
  2. 1
      src/modules/punk/console-buildversion.txt

20
src/modules/punk/console-999999.0a1.0.tm

@ -2998,12 +2998,19 @@ namespace eval punk::console {
#some terminals (conemu on windows) scroll the viewport when we make a big move down like this - a move to 1 1 immediately after cursor_save doesn't seem to fix that.
#This issue also occurs when switching back from the alternate screen buffer - so perhaps that needs to be addressed elsewhere.
puts -nonewline $out [punk::ansi::cursor_off][punk::ansi::move 2000 2000]
#flush before querying: the position query may execute in the console-owning
#thread (G-007 routing), whose flush acts on its own channel instance for the same
#OS handle - an unflushed move here would leave the terminal reporting the
#unmoved cursor position (seen as 'columns 1' on tcl 8.6 where the -winsize
#shortcut is unavailable and this mechanism actually runs)
flush $out
lassign [get_cursor_pos_list $inoutchannels] lines cols
puts -nonewline $out [punk::ansi::move $start_row $start_col][punk::ansi::cursor_on];flush $out
set result [dict create columns $cols rows $lines]
} errM]} {
puts -nonewline $out [punk::ansi::move $start_row $start_col]
puts -nonewline $out [punk::ansi::cursor_on]
catch {flush $out}
error "$errM"
} else {
return $result
@ -3037,12 +3044,16 @@ namespace eval punk::console {
#some terminals (conemu on windows) scroll the viewport when we make a big move down like this - a move to 1 1 immediately after cursor_save doesn't seem to fix that.
#This issue also occurs when switching back from the alternate screen buffer - so perhaps that needs to be addressed elsewhere.
puts -nonewline $out [punk::ansi::cursor_off][punk::ansi::cursor_save_dec][punk::ansi::move 2000 2000]
#flush before querying - see get_size_using_cursormove (G-007 routing: the query
#may flush a different channel instance in the console-owning thread)
flush $out
lassign [get_cursor_pos_list $inoutchannels] lines cols
puts -nonewline $out [punk::ansi::cursor_restore][punk::ansi::cursor_on];flush $out
set result [dict create columns $cols rows $lines]
} errM]} {
puts -nonewline $out [punk::ansi::cursor_restore_dec]
puts -nonewline $out [punk::ansi::cursor_on]
catch {flush $out}
error "$errM"
} else {
return $result
@ -4292,6 +4303,10 @@ namespace eval punk::console {
if {!$emit} {
puts -nonewline $out \033\[2K\033\[1G ;#2K erase line, 1G cursor at col1
}
#flush before each cursor query: the query may execute in the console-owning thread
#(G-007 routing) whose flush acts on its own channel instance - unflushed emissions
#here would be measured as if they never happened (see get_size_using_cursormove)
flush $out
set response ""
if {[catch {
set response [punk::console::get_cursor_pos $inoutchannels]
@ -4313,6 +4328,7 @@ namespace eval punk::console {
} errM]} {
puts stderr "test_char_width couldn't emit this string - \nerror: $errM"
}
flush $out ;#the test emission must reach the terminal before the (possibly routed) query measures it
set response [punk::console::get_cursor_pos $inoutchannels]
lassign [split $response ";"] _row2 col2
@ -4367,6 +4383,10 @@ namespace eval punk::console {
if {!$emit} {
puts -nonewline $out \033\[2K\033\[1G ;#2K erase line, 1G cursor at col1
}
#flush before the cursor query - the alt-screen/move/erase emissions above must reach
#the terminal first (the query may flush a different channel instance in the
#console-owning thread under G-007 routing - see get_size_using_cursormove)
flush $out
set response ""
if {[catch {
set response [punk::console::get_cursor_pos $inoutchannels]

1
src/modules/punk/console-buildversion.txt

@ -1,6 +1,7 @@
0.7.1
#First line must be a semantic version number
#all other lines are ignored.
#0.7.1 - compound emit-then-query operations flush their emissions before querying: get_size_using_cursormove/get_size_using_cursorrestore (the far-corner move), test_char_width (positioning and the measured test emission) and test_string_cursor (alt-screen/move/erase). Under G-007 routing the position query may execute in the console-owning thread, whose flush acts on its own channel instance for the same OS handle - the caller's unflushed emissions then reach the terminal after the query measures, e.g. get_size returning 'columns 1' on tcl 8.6 where the -winsize shortcut is unavailable and the ANSI mechanism actually runs (pre-routing this was masked because emit and query shared one channel instance, so the query's flush pushed the emissions too). Error paths flush their cursor-restore emissions likewise.
#0.7.1 - ensure_object_integration hardening: returns 0 as a graceful no-op (without latching object_integration_done) when opunk::console is not loaded, instead of erroring into the nonexistent namespace; and on wiring the lifecycle callback it retro-registers ownership for anchors created in this interp before the wiring (previously 'package require opunk::console; opunk::console::create ...' with no intervening punk::console object operation left the console without a registered owner - fact-store share qualifiers then fell back to the calling thread and other threads could not address that console's facts). Retro-registration fills empty registry entries only; existing live registrations are preserved.
#0.7.1 - fix Tcl 8.6 windows console misdetection (one root cause, three sites): 8.6 console channels expose no -inputmode configure key, so guards that test for -inputmode/-mode classified a real 8.6 console as a pipe. A twapi console handle for stdin is now treated as definitive at all three sites (stdin only - pipe probes still never flip the process console; mintty-as-pipes has no console handle and falls through to the env heuristics). Site 1: get_ansi_response_payload's raw-cycling gate (0.1.5) skipped the raw cycle for 8.6 line-mode queries. Site 2: is_input_console_or_tty false negative made settle_can_respond's layer-2 heuristic settle a real 8.6 console as unable to respond. Site 3 (the line-mode query killer, introduced 0.1.2): input_at_eof took the pipe branch and performed its probe read on the drained console channel - a read on a drained 8.6 console makes the channel driver park a blocking cooked-mode ReadConsole that a later raw flip cannot cancel (verified empirically against clean tclkits 8.6.13/8.6.17: driver reads sample the console mode when issued, not when data arrives), and since get_ansi_response_payload calls input_at_eof immediately before its raw cycle, every 8.6 line-mode query's response was swallowed by that parked read until Enter (~500ms timeout + response leaking to the line reader as phantom input; also the cause of increased ANSI artifacts in outputs like 'help env' once 0.7.0 brokering made code-interp queries actually reach the terminal).
#0.7.0 - G-007 slice 2 (choke-point brokering): a terminal query (internal::get_ansi_response_payload and every query proc layered above it) on the process-default console {stdin stdout} issued by a thread that is not the registered owner is forwarded whole to the owning thread via synchronous thread::send, so queueing, raw-mode cycling, cooperative reader handling (input_chunks_waiting) and can_respond gating all execute in the owner's context; owner-side errors propagate to the caller with routing context

Loading…
Cancel
Save