Browse Source

punk::console::get_size mechanism selection based on timings

master
Julian Noble 3 months ago
parent
commit
f44f67852a
  1. 178
      src/bootsupport/modules/punk/console-0.1.1.tm
  2. 178
      src/modules/punk/console-999999.0a1.0.tm

178
src/bootsupport/modules/punk/console-0.1.1.tm

@ -1522,17 +1522,114 @@ namespace eval punk::console {
}
#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]} {
return $sizedict
}
lappend tried_mechlist $mech
}
#if {![catch {get_size_using_cursorrestore $inoutchannels} sizedict]} {
# return $sizedict
#}
#lappend tried_mechlist "cursorrestore"
#if {![catch {get_size_using_cursormove $inoutchannels} sizedict]} {
# return $sizedict
#}
#lappend tried_mechlist "cursormove"
#if {![catch {get_size_using_tput $inoutchannels} sizedict]} {
# return $sizedict
#}
#lappend tried_mechlist "tput"
error "can't get console size. Tried mechanisms: $tried_mechlist"
}
proc get_size_using_chanconfigure {{inoutchannels {stdin stdout}}} {
set out [lindex $inoutchannels 1]
set outconf [chan configure $out]
if {[dict exists $outconf -winsize]} {
#this mechanism is much faster than ansi cursor movements
#REVIEW check if any x-platform anomalies with this method?
#can -winsize key exist but contain erroneous info? We will check that we get 2 ints at least
lassign [dict get $outconf -winsize] cols lines
if {[string is integer -strict $cols] && [string is integer -strict $lines]} {
return [dict create columns $cols rows $lines]
}
}
error "chan configure method of getting console size not supported or failed to get valid size info"
#we don't need to care about the input channel if chan configure on the output can give us the info.
#short circuit ansi cursor movement method if chan configure supports the -winsize value
set outconf [chan configure $out]
@ -1542,43 +1639,45 @@ namespace eval punk::console {
#can -winsize key exist but contain erroneous info? We will check that we get 2 ints at least
lassign [dict get $outconf -winsize] cols lines
if {[string is integer -strict $cols] && [string is integer -strict $lines]} {
return [list columns $cols rows $lines]
return [dict create columns $cols rows $lines]
}
#continue on to ansi mechanism if we didn't get 2 ints
}
if {[catch {chan eof $in} is_eof]} {
error "punk::console::get_size input channel $in seems to be closed ([info level 1])"
}
proc get_size_using_tput {{inoutchannels {stdin stdout}}} {
set tputcmd [auto_execok tput]
if {$tputcmd eq ""} {
error "tput command not found - cannot use tput method to get console size"
}
lassign [exec {*}$tputcmd lines cols] lines cols
return [dict create columns $cols rows $lines]
}
proc get_size_using_cursormove {{inoutchannels {stdin stdout}}} {
set out [lindex $inoutchannels 1]
#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_using_cursormove output channel $out seems to be closed ([info level 1])"
} else {
if {$is_eof} {
error "punk::console::get_size eof on input channel $in ([info level 1])"
error "punk::console::get_size_using_cursormove eof on output channel $out ([info level 1])"
}
}
#keep out of catch - no point in even trying a restore move if we can't get start position - just fail here.
#no vt52 equiv? may as well strip all vt52 from here?
lassign [get_cursor_pos_list $inoutchannels] start_row start_col
variable is_vt52
if {!$is_vt52} {
set movefunc "punk::ansi::move"
set func_coff "punk::ansi::cursor_off"
set func_con "punk::ansi::cursor_on"
} else {
set movefunc "punk::ansi::vt52move"
set func_coff "punk::ansi::vt52cursor_off"
set func_con "punk::ansi::vt52cursor_on"
}
if {[catch {
#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 [$func_coff][$movefunc 2000 2000]
puts -nonewline $out [punk::ansi::cursor_off][punk::ansi::move 2000 2000]
lassign [get_cursor_pos_list $inoutchannels] lines cols
puts -nonewline $out [$movefunc $start_row $start_col][$func_con];flush stdout
set result [list columns $cols rows $lines]
puts -nonewline $out [punk::ansi::move $start_row $start_col][punk::ansi::cursor_on];flush stdout
set result [dict create columns $cols rows $lines]
} errM]} {
puts -nonewline $out [$movefunc $start_row $start_col]
puts -nonewline $out [$func_con]
puts -nonewline $out [punk::ansi::move $start_row $start_col]
puts -nonewline $out [punk::ansi::cursor_on]
error "$errM"
} else {
return $result
@ -1586,14 +1685,16 @@ namespace eval punk::console {
}
#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
proc get_size_cursorrestore {{inoutchannels {stdin stdout}}} {
proc get_size_using_cursorrestore {{inoutchannels {stdin stdout}}} {
lassign $inoutchannels in out
#we use the same shortcircuit mechanism as get_size to avoid ansi at all if the output channel will give us the info directly
set outconf [chan configure $out]
if {[dict exists $outconf -winsize]} {
lassign [dict get $outconf -winsize] cols lines
if {[string is integer -strict $cols] && [string is integer -strict $lines]} {
return [list columns $cols rows $lines]
#don't use shortcut mechanisms - this function is intended to specificall use the cursor_save/restore method
if {[catch {chan eof $out} is_eof]} {
error "punk::console::get_size_using_cursorrestore output channel $out seems to be closed ([info level 1])"
} else {
if {$is_eof} {
error "punk::console::get_size_using_cursorrestore eof on output channel $out ([info level 1])"
}
}
@ -1603,7 +1704,7 @@ namespace eval punk::console {
puts -nonewline $out [punk::ansi::cursor_off][punk::ansi::cursor_save_dec][punk::ansi::move 2000 2000]
lassign [get_cursor_pos_list $inoutchannels] lines cols
puts -nonewline $out [punk::ansi::cursor_restore][punk::console::cursor_on];flush $out
set result [list columns $cols rows $lines]
set result [dict create columns $cols rows $lines]
} errM]} {
puts -nonewline $out [punk::ansi::cursor_restore_dec]
puts -nonewline $out [punk::ansi::cursor_on]
@ -1612,10 +1713,15 @@ namespace eval punk::console {
return $result
}
}
proc get_dimensions {{inoutchannels {stdin stdout}}} {
lassign [get_size $inoutchannels] _c cols _l lines
return "${cols}x${lines}"
}
#the (xterm?) CSI 18t query is supported by *some* terminals
proc get_xterm_size {{inoutchannels {stdin stdout}}} {
set capturingregex {(.*)(\x1b\[8;([0-9]+;[0-9]+)t)$} ;#must capture prefix,entire-response,response-payload

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

@ -1522,17 +1522,114 @@ namespace eval punk::console {
}
#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]} {
return $sizedict
}
lappend tried_mechlist $mech
}
#if {![catch {get_size_using_cursorrestore $inoutchannels} sizedict]} {
# return $sizedict
#}
#lappend tried_mechlist "cursorrestore"
#if {![catch {get_size_using_cursormove $inoutchannels} sizedict]} {
# return $sizedict
#}
#lappend tried_mechlist "cursormove"
#if {![catch {get_size_using_tput $inoutchannels} sizedict]} {
# return $sizedict
#}
#lappend tried_mechlist "tput"
error "can't get console size. Tried mechanisms: $tried_mechlist"
}
proc get_size_using_chanconfigure {{inoutchannels {stdin stdout}}} {
set out [lindex $inoutchannels 1]
set outconf [chan configure $out]
if {[dict exists $outconf -winsize]} {
#this mechanism is much faster than ansi cursor movements
#REVIEW check if any x-platform anomalies with this method?
#can -winsize key exist but contain erroneous info? We will check that we get 2 ints at least
lassign [dict get $outconf -winsize] cols lines
if {[string is integer -strict $cols] && [string is integer -strict $lines]} {
return [dict create columns $cols rows $lines]
}
}
error "chan configure method of getting console size not supported or failed to get valid size info"
#we don't need to care about the input channel if chan configure on the output can give us the info.
#short circuit ansi cursor movement method if chan configure supports the -winsize value
set outconf [chan configure $out]
@ -1542,43 +1639,45 @@ namespace eval punk::console {
#can -winsize key exist but contain erroneous info? We will check that we get 2 ints at least
lassign [dict get $outconf -winsize] cols lines
if {[string is integer -strict $cols] && [string is integer -strict $lines]} {
return [list columns $cols rows $lines]
return [dict create columns $cols rows $lines]
}
#continue on to ansi mechanism if we didn't get 2 ints
}
if {[catch {chan eof $in} is_eof]} {
error "punk::console::get_size input channel $in seems to be closed ([info level 1])"
}
proc get_size_using_tput {{inoutchannels {stdin stdout}}} {
set tputcmd [auto_execok tput]
if {$tputcmd eq ""} {
error "tput command not found - cannot use tput method to get console size"
}
lassign [exec {*}$tputcmd lines cols] lines cols
return [dict create columns $cols rows $lines]
}
proc get_size_using_cursormove {{inoutchannels {stdin stdout}}} {
set out [lindex $inoutchannels 1]
#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_using_cursormove output channel $out seems to be closed ([info level 1])"
} else {
if {$is_eof} {
error "punk::console::get_size eof on input channel $in ([info level 1])"
error "punk::console::get_size_using_cursormove eof on output channel $out ([info level 1])"
}
}
#keep out of catch - no point in even trying a restore move if we can't get start position - just fail here.
#no vt52 equiv? may as well strip all vt52 from here?
lassign [get_cursor_pos_list $inoutchannels] start_row start_col
variable is_vt52
if {!$is_vt52} {
set movefunc "punk::ansi::move"
set func_coff "punk::ansi::cursor_off"
set func_con "punk::ansi::cursor_on"
} else {
set movefunc "punk::ansi::vt52move"
set func_coff "punk::ansi::vt52cursor_off"
set func_con "punk::ansi::vt52cursor_on"
}
if {[catch {
#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 [$func_coff][$movefunc 2000 2000]
puts -nonewline $out [punk::ansi::cursor_off][punk::ansi::move 2000 2000]
lassign [get_cursor_pos_list $inoutchannels] lines cols
puts -nonewline $out [$movefunc $start_row $start_col][$func_con];flush stdout
set result [list columns $cols rows $lines]
puts -nonewline $out [punk::ansi::move $start_row $start_col][punk::ansi::cursor_on];flush stdout
set result [dict create columns $cols rows $lines]
} errM]} {
puts -nonewline $out [$movefunc $start_row $start_col]
puts -nonewline $out [$func_con]
puts -nonewline $out [punk::ansi::move $start_row $start_col]
puts -nonewline $out [punk::ansi::cursor_on]
error "$errM"
} else {
return $result
@ -1586,14 +1685,16 @@ namespace eval punk::console {
}
#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
proc get_size_cursorrestore {{inoutchannels {stdin stdout}}} {
proc get_size_using_cursorrestore {{inoutchannels {stdin stdout}}} {
lassign $inoutchannels in out
#we use the same shortcircuit mechanism as get_size to avoid ansi at all if the output channel will give us the info directly
set outconf [chan configure $out]
if {[dict exists $outconf -winsize]} {
lassign [dict get $outconf -winsize] cols lines
if {[string is integer -strict $cols] && [string is integer -strict $lines]} {
return [list columns $cols rows $lines]
#don't use shortcut mechanisms - this function is intended to specificall use the cursor_save/restore method
if {[catch {chan eof $out} is_eof]} {
error "punk::console::get_size_using_cursorrestore output channel $out seems to be closed ([info level 1])"
} else {
if {$is_eof} {
error "punk::console::get_size_using_cursorrestore eof on output channel $out ([info level 1])"
}
}
@ -1603,7 +1704,7 @@ namespace eval punk::console {
puts -nonewline $out [punk::ansi::cursor_off][punk::ansi::cursor_save_dec][punk::ansi::move 2000 2000]
lassign [get_cursor_pos_list $inoutchannels] lines cols
puts -nonewline $out [punk::ansi::cursor_restore][punk::console::cursor_on];flush $out
set result [list columns $cols rows $lines]
set result [dict create columns $cols rows $lines]
} errM]} {
puts -nonewline $out [punk::ansi::cursor_restore_dec]
puts -nonewline $out [punk::ansi::cursor_on]
@ -1612,10 +1713,15 @@ namespace eval punk::console {
return $result
}
}
proc get_dimensions {{inoutchannels {stdin stdout}}} {
lassign [get_size $inoutchannels] _c cols _l lines
return "${cols}x${lines}"
}
#the (xterm?) CSI 18t query is supported by *some* terminals
proc get_xterm_size {{inoutchannels {stdin stdout}}} {
set capturingregex {(.*)(\x1b\[8;([0-9]+;[0-9]+)t)$} ;#must capture prefix,entire-response,response-payload

Loading…
Cancel
Save