#todo - determine cursor on/off state before the call to restore properly.
variable get_size_mechanism
set get_size_mechanism [dict create]
proc get_size {{inoutchannels {stdin stdout}}} {
lassign $inoutchannels in out
#we can't reliably use [chan names] for stdin,stdout. There could be stacked channels and they may have a names such as file22fb27fe810
#chan eof is faster whether chan exists or not than
if {[catch {chan eof $out} is_eof]} {
error "punk::console::get_size output channel $out seems to be closed ([info level 1])"
} else {
if {$is_eof} {
error "punk::console::get_size eof on output channel $out ([info level 1])"
set tried_mechlist [list]
#fastest mechanism if available - use Tcl's inbuilt -winsize key from chan configure if available - this is much faster than any ANSI mechanism
#unknown which platforms support this.
if {![catch {get_size_using_chanconfigure $inoutchannels} sizedict]} {
return $sizedict
}
lappend tried_mechlist "chanconfigure"
variable is_vt52
if {$is_vt52} {
#vt52 doesn't support cursor save/restore or cursor position reports.
if {![catch {get_size_using_tput $inoutchannels} sizedict]} {
return $sizedict
}
lappend tried_mechlist "tput"
error "can't get console size. Tried mechanisms: $mechlist"
}
variable get_size_mechanism ;#dict keyed on terminal ident. (currently just list of inoutchannels but may be something else in future such as terminal object or ident string)
if {![dict exists $get_size_mechanism $inoutchannels]} {
set try_order [list]
#call each mechanism once to see if it works - and to ensure we don't include initial run in our timings.
#we will also use the results for our initial return of the size.
set successful_mechs [list]
set sizedict [dict create]
if {![catch {get_size_using_cursorrestore $inoutchannels} result]} {
lappend successful_mechs "cursorrestore"
if {![dict size $sizedict]} {
set sizedict $result
}
}
if {![catch {get_size_using_cursormove $inoutchannels} result]} {
lappend successful_mechs "cursormove"
if {![dict size $sizedict]} {
set sizedict $result
}
}
if {![catch {get_size_using_tput $inoutchannels} result] } {
lappend successful_mechs "tput"
if {![dict size $sizedict]} {
set sizedict $result
}
}
set timings [list]
set t_ms 250 ;#default timerate is 1000ms - we are trying at least 3 mechanisms so 1000ms is a bit long for this test as it can slow down the first call to get_size significantly.
foreach sm $successful_mechs {
catch {
set timing_result [timerate {get_size_using_$sm $inoutchannels} $t_ms]
set micros [expr {int([lindex $timing_result 0])}]
lappend timings [list $micros $sm]
}
}
set sorted [lsort -integer -index 0 $timings]
if {[llength $sorted] > 0} {
set try_order [lmap t $sorted {lindex $t 1}]
} else {
set try_order [list]
}
dict set get_size_mechanism $inoutchannels $try_order
return $sizedict
}
foreach mech [dict get $get_size_mechanism $inoutchannels] {
if {![catch {get_size_using_$mech $inoutchannels} sizedict]} {
#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.
#faster than get_size when it is using ansi mechanism - but uses cursor_save - which we may want to avoid if calling during another operation which uses cursor save/restore
#todo - determine cursor on/off state before the call to restore properly.
variable get_size_mechanism
set get_size_mechanism [dict create]
proc get_size {{inoutchannels {stdin stdout}}} {
lassign $inoutchannels in out
#we can't reliably use [chan names] for stdin,stdout. There could be stacked channels and they may have a names such as file22fb27fe810
#chan eof is faster whether chan exists or not than
if {[catch {chan eof $out} is_eof]} {
error "punk::console::get_size output channel $out seems to be closed ([info level 1])"
} else {
if {$is_eof} {
error "punk::console::get_size eof on output channel $out ([info level 1])"
set tried_mechlist [list]
#fastest mechanism if available - use Tcl's inbuilt -winsize key from chan configure if available - this is much faster than any ANSI mechanism
#unknown which platforms support this.
if {![catch {get_size_using_chanconfigure $inoutchannels} sizedict]} {
return $sizedict
}
lappend tried_mechlist "chanconfigure"
variable is_vt52
if {$is_vt52} {
#vt52 doesn't support cursor save/restore or cursor position reports.
if {![catch {get_size_using_tput $inoutchannels} sizedict]} {
return $sizedict
}
lappend tried_mechlist "tput"
error "can't get console size. Tried mechanisms: $mechlist"
}
variable get_size_mechanism ;#dict keyed on terminal ident. (currently just list of inoutchannels but may be something else in future such as terminal object or ident string)
if {![dict exists $get_size_mechanism $inoutchannels]} {
set try_order [list]
#call each mechanism once to see if it works - and to ensure we don't include initial run in our timings.
#we will also use the results for our initial return of the size.
set successful_mechs [list]
set sizedict [dict create]
if {![catch {get_size_using_cursorrestore $inoutchannels} result]} {
lappend successful_mechs "cursorrestore"
if {![dict size $sizedict]} {
set sizedict $result
}
}
if {![catch {get_size_using_cursormove $inoutchannels} result]} {
lappend successful_mechs "cursormove"
if {![dict size $sizedict]} {
set sizedict $result
}
}
if {![catch {get_size_using_tput $inoutchannels} result] } {
lappend successful_mechs "tput"
if {![dict size $sizedict]} {
set sizedict $result
}
}
set timings [list]
set t_ms 250 ;#default timerate is 1000ms - we are trying at least 3 mechanisms so 1000ms is a bit long for this test as it can slow down the first call to get_size significantly.
foreach sm $successful_mechs {
catch {
set timing_result [timerate {get_size_using_$sm $inoutchannels} $t_ms]
set micros [expr {int([lindex $timing_result 0])}]
lappend timings [list $micros $sm]
}
}
set sorted [lsort -integer -index 0 $timings]
if {[llength $sorted] > 0} {
set try_order [lmap t $sorted {lindex $t 1}]
} else {
set try_order [list]
}
dict set get_size_mechanism $inoutchannels $try_order
return $sizedict
}
foreach mech [dict get $get_size_mechanism $inoutchannels] {
if {![catch {get_size_using_$mech $inoutchannels} sizedict]} {
#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.
#faster than get_size when it is using ansi mechanism - but uses cursor_save - which we may want to avoid if calling during another operation which uses cursor save/restore