diff --git a/src/bootsupport/modules/overtype-1.7.4.tm b/src/bootsupport/modules/overtype-1.7.4.tm index d3a642da..d1d0dd44 100644 --- a/src/bootsupport/modules/overtype-1.7.4.tm +++ b/src/bootsupport/modules/overtype-1.7.4.tm @@ -417,6 +417,7 @@ tcl::namespace::eval overtype { set overblock [tcl::string::map {\r\n \n} $overblock] if {$opt_startrow > 1} { set down [expr {$opt_startrow -1}] + #when vt52? set overblock [punk::ansi::move_down $down]$overblock } @@ -4549,7 +4550,7 @@ tcl::namespace::eval overtype { #P3 horizontal grid size - ignored on VT300 - commonly set to zero - # ECMA-48 SSU (ESC Ps I) + # ECMA-48 SSU (ESC [ Ps I) # 0 - CHARACTER # 1 - MILLIMETRE # 2 - COMPUTER DECIPOINT 0.03528mm 1/720 of 25.4mm) diff --git a/src/bootsupport/modules/punk/ansi-0.1.1.tm b/src/bootsupport/modules/punk/ansi-0.1.1.tm index 821af950..7bf4bf7c 100644 --- a/src/bootsupport/modules/punk/ansi-0.1.1.tm +++ b/src/bootsupport/modules/punk/ansi-0.1.1.tm @@ -4670,6 +4670,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun clear_below]] return \033\[0J } + proc vt52clear_below {} { + return \033J + } proc clear_all {} { # - doesn't work?? @@ -4688,10 +4691,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun cursor_off]] return "\033\[?25l" } - proc cursor_on_vt52 {} { + proc vt52cursor_on {} { return \x1be } - proc cursor_off_vt52 {} { + proc vt52cursor_off {} { return \x1bf } @@ -4781,7 +4784,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu Sequence is of the form: ESCY This sequence will generally not be understood by terminals - that are not in vt52 mode (e.g DECANM unset). + that are not in vt52 mode (i.e DECANM unset). } @values -min 2 -max 2 row -type integer -help\ @@ -4959,10 +4962,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[para] DECRC return \x1b8 } - proc cursor_save_vt52 {} { + proc vt52cursor_save {} { return \x1bj } - proc cursor_restore_vt52 {} { + proc vt52cursor_restore {} { return \x1bk } @@ -5021,29 +5024,19 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # \x1b\[?7\;2\$y #where 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset) + + #names for other alt_screen mechanismk: 1047,1048 vs 1049? + #https://wiki.tau.garden/dec-modes/ #(DEC,xterm,contour,mintty,kitty etc) #https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking - #names for other alt_screen mechanismk: 1047,1048 vs 1049? - #variable decmode_names [dict create\ - # DECANM 2\ - # origin 6\ - # DECCOLM 3\ - # line_wrap 7\ - # alt_screen 1049\ - # grapheme_clusters 2027\ - # bracketed_paste 2004\ - # mouse_sgr 1006\ - # mouse_urxvt 1015\ - # mouse_sgr_pixel 1016\ - #] - # # some more ansi mode/sequence info: #https://pkg.go.dev/github.com/charmbracelet/x/ansi #see also: https://ucs-detect.readthedocs.io/results.html#dec-private-modes-support + #REVIEW - these modes are sometimes used for different things on different terminals #For now we are assigning based on common usage in things like xterm & windows terminal #Proper handling would require tables for various terminals - review. @@ -5052,140 +5045,279 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #unset with DECRST ESC [ ? l variable decmode_data { 1 { - {origin DEC description "DECCKM - Cursor Keys Mode" names {DECCKM cursor_keys}} + {origin DEC reset-state "cursor sequences" set-state "application sequences" description "DECCKM - Arrow Keys Cursor Keys Mode" names {DECCKM cursor_keys}} } 2 { - {origin DEC description "DECANM - ANSI/VT52 Mode" names {DECANM} note { + {origin DEC reset-state "VT52" set-state "ANSI" description "DECANM - ANSI/VT52 Mode" names {DECANM} note { Disable to turn on VT52 emulation. In VT52 mode - use \x1b< to exit. } } } 3 { - {origin DEC description "DECCOLM - Column" names {DECCOLM}} + {origin DEC reset-state "80-column font" set-state "132-column font" + description "DECCOLM - Select 80 or 132 Columns per page +(obs - use DECSCPP)" + names {DECCOLM} + see "https://vt100.net/docs/vt510-rm/DECCOLM.html"} } 4 { - {origin DEC description "DECSCLM - Scrolling" names {DECSCLM}} + {origin DEC reset-state "jump scroll" set-state "smooth scroll" description "DECSCLM - Scrolling Mode" names {DECSCLM}} } 5 { - {origin DEC description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} + {origin DEC reset-state "dark mode" set-state "light mode" description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} } 6 { - {origin DEC description "DECOM - Origin Mode (whether cursor is restricted to within page margins)" names {DECOM}} + {origin DEC reset-state "upper-left corner" set-state "within margins" + description "DECOM - Origin Mode +(whether cursor is restricted to within page margins)" + names {DECOM}} } 7 { - {origin DEC description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} + {origin DEC reset-state "no autowrap" set-state "autowrap" description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} } 8 { - {origin DEC description "DECARM - Auto Repeat Mode" names {DECARM autorepeat}} + {origin DEC reset-state "disabled" set-state "0.5s keypress autorepeat" + description "DECARM - Auto Repeat Mode" + see "https://vt100.net/docs/vt510-rm/DECARM.html" + names {DECARM autorepeat}} } 9 { - {origin "xterm" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { + {origin "xterm" reset-state "disable" set-state "enable" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { Escape sequence on button press only. CSI M CbCxCy (6 chars) Coords limited to 223 (=255 - 32) } } - {origin DEC description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + {origin DEC reset-state "" set-state "" description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + } + 10 { + {origin DEC reset-state "" set-state "" description "DECEDM - Editing Mode" names {DECEDM}} + {origin rxvt reset-state "" set-state "" description "show toolbar" names {RXVTSHOWTOOLBAR}} + } + 11 { + {origin DEC reset-state "" set-state "" description "DECLTM - Line Transmit Mode" names {DECLTM}} } 12 { - {origin xterm description "Cursor blink" names {XTCBLINK cursorblink}} - {origin DEC description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} + {origin xterm reset-state "steady" set-state "blink" description "Cursor blink" names {XTCBLINK cursorblink}} + {origin DEC reset-state "" set-state "" description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} } 25 { - {origin DEC description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} + {origin DEC reset-state "invisible" set-state "visible" description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} } 40 { - {origin DEC description "New Line Mode" names {DECCRNLM newline_mode}} - {origin xterm description "Allow 80->132 mode" names {xt80-132}} + {origin DEC reset-state "" set-state "" description "New Line Mode" names {DECCRNLM newline_mode}} + {origin xterm reset-state "" set-state "" description "Allow 80->132 mode" names {xt80-132}} } 45 { - {origin DEC description "Graphics Print Color Syntax" names {DECGPCS}} - {origin xterm description "Reverse Wraparound Mode" names {reverse_wraparound}} + {origin DEC reset-state "" set-state "" description "Graphics Print Color Syntax" names {DECGPCS}} + {origin xterm reset-state "" set-state "" description "Reverse Wraparound Mode" names {reverse_wraparound}} } 47 { - {origin xterm description "xterm alternate buffer" names {xterm_altbuf}} - {origin DEC description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} + {origin xterm reset-state "" set-state "" description "xterm alternate buffer" names {xterm_altbuf}} + {origin DEC reset-state "" set-state "" description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} } 64 { - {origin DEC description "DECPCCM - Page Cursor Coupling Mode" names {DECPCCM}} + {origin DEC reset-state "uncoupled" set-state "coupled" description "DECPCCM - Page Cursor Coupling Mode" + see "https://vt100.net/docs/vt510-rm/DECPCCM.html" + names {DECPCCM}} } 66 { - {origin DEC description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} + {origin DEC reset-state "keypad chars" set-state "app sequences" description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} } 67 { - {origin DEC description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} + {origin DEC reset-state "delete key" set-state "backspace key" description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} } 69 { - {origin DEC description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} + {origin DEC reset-state "cannot set margins" set-state "can set margins" description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} } 80 { - {origin ??? description "Sixel Display Mode" see "" names {sixel_display}} + {origin DEC reset-state "scrolling enabled" set-state "scrolling disabled" description "Sixel Display Mode +(VT382 disable on set) +(VT330 manual error?) +some emulators may reverse" + see "https://github.com/dankamongmen/notcurses/issues/1782" + names {sixel_display DECSDM}} + } + 95 { + {origin DEC reset-state "clear screen on column change" set-state "no clear on column change" + description "No Clearing Screen on Column Change Mode" see "" + names {DECNCSM}} + } + 98 { + {origin DEC reset-state "disabled" set-state "enabled" description "Auto Resize Mode" see "" names {DECARSM}} + } + 115 { + {origin DEC reset-state "disabled" set-state "enabled" description "Alternate Text Color Blink Mode" see "" names {DECATCBM}} } 117 { - {origin ??? description "Erase Color Mode" see "" names {erase_color}} + {origin DEC reset-state "" set-state "" description "Erase Color Mode" see "" names {DECECM erase_color}} } 1000 { - {origin "xterm" description "VT200 compatibility mouse" names {SET_VT200_MOUSE} note { + {origin "xterm" reset-state "" set-state "" description "VT200 compatibility mouse" + names {MOUSE_REPORT_CLICK SET_VT200_MOUSE} + note { Escape sequence on both button press and release. CSI M CbCxCy } } } 1001 { - {origin "???" description "Use Hilite Mouse Tracking" names {mouse_tracking_hilite}} + {origin "xterm" reset-state "" set-state "" description "Use Hilite Mouse Tracking" + names {MOUSE_HILITE_TRACKING mouse_tracking_hilite}} } 1002 { - {origin "???" description "Use Cell Motion Mouse Tracking" names {mouse_tracking_cellmotion}} + {origin "xterm" reset-state "" set-state "" description "Use Cell Motion Mouse Tracking" + names {MOUSE_REPORT_DRAG mouse_tracking_cellmotion}} } 1003 { - {origin "???" description "Use All Motion Mouse Tracking" names {mouse_tracking_allmotion}} + {origin "xterm" reset-state "" set-state "" description "Use All Motion Mouse Tracking" + names {MOUSE_ALLMOTION mouse_tracking_allmotion}} } 1004 { - {origin "xterm" description "Send FocusIn/FocusOut events" names {mouse_focus_event}} + {origin "xterm" reset-state "" set-state "" description "Send FocusIn/FocusOut events" + names {FOCUS_IN_OUT_EVENTS mouse_focus_event}} } 1005 { - {origin "xterm" description "Enable UTF-8 Mouse Mode" names {mouse_utf8 mouse_utf8_extended}} + {origin "xterm" reset-state "" set-state "" + description "Enable UTF-8 Mouse Mode" + names {MOUSE_EXTENDED_UTF8 mouse_utf8 mouse_utf8_extended}} } 1006 { - {origin "xterm" description "Enable SGR Mouse Mode" names {mouse_sgr mouse_sgr_extended} note{ + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Mouse Mode" names + {MOUSE_EXTENDED_SGR mouse_sgr mouse_sgr_extended} + note{ SET_SGR_EXT_MODE_MOUSE - extended compared to x10 mouse protocol which limits x y coords to 223 (=255 - 32) } } } 1007 { - {origin "???" description "Enable Alternate Scroll Mode" names {alternate_scroll}} + {origin "xterm" reset-state "" set-state "" + description "Enable Alternate Scroll Mode" + names {ALT_SCROLL_XTERM alternate_scroll}} } 1015 { - {origin "urxvt" description "Enable urxvt Mouse Mode" names {mouse_urxvt}} + {origin "urxvt" reset-state "" set-state "" + description "Enable urxvt Mouse Mode" + names {MOUSE_URXVT}} } 1016 { - {origin "xterm" description "Enable SGR Pixel Mouse Mode" names {mouse_sgr_pixel}} + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Pixel Mouse Mode" + names {MOUSE_SGR_PIXELS}} + } + 1042 { + {origin "xterm" reset-state "" set-state "" + description "Enable Urgency window manager hint +when Control-G is received" + names {URGENCY_ON_CTRL_G xt1042}} + } + 1043 { + {origin "xterm" reset-state "" set-state "" + description "Enable raising of the window +when Control-G is received" + names {RAISE_ON_CTRL_G xt1043}} } 1047 { - {origin "xterm" description "Alternate Buffer" names {alt_buffer_only}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer" + names {ALT_SCREEN_BUFFER_XTERM alt_buffer_only}} } 1049 { - {origin "xterm" description "Alternate Buffer with save cursor" names {alt_buffer alt_screen}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer with save cursor" + names {ALT_SCREEN_AND_SAVE_CLEAR alt_buffer alt_screen}} + } + 1070 { + {origin "xterm" reset-state "" set-state "" + description "Use private color registers for each graphic" + names {SIXEL_PRIVATE_PALETTE}} } 2004 { - {origin "xterm" description "Set bracketed paste mode" names {bracketed_paste}} + {origin "xterm" reset-state "" set-state "" + description "Set bracketed paste mode" + names {BRACKETED_PASTE}} + } + 2026 { + {origin iTerm2 reset-state "screen updates" set-state "no screen updates" + description "Synchronized Output" + see "https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036" + names {SYNCHRONIZED_OUTPUT synch}} } 2027 { - {origin Contour description "Grapheme Cluster Processing" names {grapheme_clusters}} + {origin Contour reset-state "" set-state "" + description "Grapheme Cluster Processing" + names {GRAPHEME_CLUSTERING grapheme_clusters}} + } + 2028 { + {origin Contour reset-state "" set-state "" + description "Text reflow" + names {TEXT_REFLOW}} } 2048 { - {origin "rockorager" description "Enable in-band window resize notifications (modern VTEEWR analog)" see "https://rockorager.dev/misc/in-band-resize-notifications/" names {window_resize_reports}} + {origin "@rockorager" reset-state "" set-state "" + description "Enable in-band window resize notifications +(modern VTEEWR analog)" + names {IN_BAND_WINDOW_RESIZE window_resize_reports} + see "https://rockorager.dev/misc/in-band-resize-notifications/" + } } 5522 { - {origin "kitty rockorager" description "Automatic Paste notifications" see "https://rockorager.dev/misc/bracketed-paste-mime/" names {automatic_paste_reports}} + {origin "kitty @rockorager" + reset-state "" set-state "" + description "Automatic Paste notifications" + names {automatic_paste_reports} + see "https://rockorager.dev/misc/bracketed-paste-mime/"} + } + 7700 { + {origin mintty reset-state "disable reporting" set-state "enable reporting" + description "Ambiguous width reporting" + notes "When enabled, mintty sends ^[[1W for an 'ambiguous narrow' font +and ^[[2W for an 'ambiguous wide' font allowing the application +to adjust its layout accordingly." + names {AMBIGUOUS_WIDTH_REPORTING}} + } + 7766 { + {origin mintty reset-state "hide" set-state "show" + description "Show/hide scrollbar" + names {SHOW_HIDE_SCROLLBAR mintty_scrollbar}} + } + 8200 { + {origin TeraTerm reset-state "Don't move cursor" set-state "Cursor to home" + description "Move cursor when erase complete display (ED 2)" + names {TTCTH}} } 8452 { - {origin "rlogin xterm" description "Post sixel cursor position" names {RLSIXPOS}} + {origin "rlogin xterm" + reset-state "" set-state "" + description "Post sixel cursor position" + names {SIXEL_SCROLLING_LEAVES_CURSOR}} } 9001 { - {origin "windows" description "win32 input mode" names {win32-input}} + {origin "conpty" + reset-state "" set-state "" + description "win32 input mode" + names {WIN32_INPUT_MODE}} + } + 19997 { + {origin "kitty" + reset-state "" set-state "" + description "Handle Ctrl-C/Ctrl-Z mode" + names {KITTY_HANDLE_CTRL_C_Z kitty_ctrl_c_ctrl_z}} + } + 77096 { + {origin "mintty" + reset-state "" set-state "" + description "BiDi mode" + names {mintty_bidi}} + } + 737769 { + {origin "foot" + reset-state "" set-state "" + description "Input Method Editor (IME) mode" + names {foot_ime}} } } set decmode_names [dict create] @@ -5205,6 +5337,14 @@ to 223 (=255 - 32) #primary source for ansimode_data: # https://ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf + + #todo - different table for different terminal emulations + #modern terminal emulators generally don't support any modes above 22 + #but some such as SCOANSI, Wyse and S97801 go higher + #e.g + #Wyse wy370 uses CSI 33 h/l for steady vs blinking cursor + #SCOANSI uses 1048 for cursor on + variable ansimode_data { 1 { {origin "ECMA-48" reset-state "replacing" set-state "cumulative" description "Guarded Area Transfer Mode" names {GATM}} @@ -5228,10 +5368,10 @@ to 223 (=255 - 32) {origin "ECMA-48" reset-state "following" set-state "preceding" description "Line Editing Mode (Vertical Editing Mode)" names {VEM}} } 8 { - {origin "???" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} + {origin "ECMA-48" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} } 9 { - {origin "???" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} + {origin "ECMA-48" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} } 10 { {origin "ECMA-48" reset-state "following" set-state "preceding" description "Character Editing Mode (Horizontal Editing Mode)" names {HEM}} @@ -5271,10 +5411,10 @@ note - No control functions are affected. {origin "ECMA-48" reset-state "multiple" set-state "single" description "Tabulation Stop Mode" names {TSM}} } 19 { - {origin "???" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} + {origin "ECMA-48 deprecated" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} } 20 { - {origin "???" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { + {origin "ECMA-48 deprecated" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { For terminals that support LNM, the default is off meaning a lone CR respresents the character emitted when enter is pushed. Turning LNM on would mean that diff --git a/src/bootsupport/modules/punk/console-0.1.1.tm b/src/bootsupport/modules/punk/console-0.1.1.tm index 21101c73..ff5c2904 100644 --- a/src/bootsupport/modules/punk/console-0.1.1.tm +++ b/src/bootsupport/modules/punk/console-0.1.1.tm @@ -1140,7 +1140,12 @@ namespace eval punk::console { return } #tailcall punk::ansi::a {*}$args - ::punk::ansi::a {*}$args + variable is_vt52 + if {$is_vt52} { + return + } else { + ::punk::ansi::a {*}$args + } } lappend PUNKARGS_aliases {::punk::console::code_a? ::punk::ansi::a?} proc code_a? {args} { @@ -1537,11 +1542,11 @@ namespace eval punk::console { set cell_size "" set cell_size_fallback 10x20 - #todo - change -inoutchannels to -terminalobject with prebuilt default + #todo - change -console to -terminalobject with prebuilt default punk::args::define { @id -id ::punk::console::cell_size - -inoutchannels -default {stdin stdout} -type list + -console -default {stdin stdout} -type list @values -min 0 -max 1 newsize -default "" -help\ "character cell pixel dimensions WxH @@ -1549,7 +1554,7 @@ namespace eval punk::console { } proc cell_size {args} { set argd [punk::args::parse $args -cache 1 withid ::punk::console::cell_size] - set inoutchannels [dict get $argd opts -inoutchannels] + set terminal [dict get $argd opts -console] set newsize [dict get $argd values newsize] variable cell_size @@ -1557,7 +1562,7 @@ namespace eval punk::console { #query existing setting if {$cell_size eq ""} { #not set - try to query terminal's overall dimensions - set pixeldict [punk::console::get_xterm_pixels $inoutchannels] + set pixeldict [punk::console::get_xterm_pixels $terminal] lassign $pixeldict _w sw _h sh if {[string is integer -strict $sw] && [string is integer -strict $sh]} { lassign [punk::console::get_size] _cols columns _rows rows @@ -1651,8 +1656,8 @@ namespace eval punk::console { set func_con "punk::ansi::cursor_on" } else { set movefunc "punk::ansi::vt52move" - set func_coff "punk::ansi::cursor_off_vt52" - set func_con "punk::ansi::cursor_on_vt52" + 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. @@ -1763,10 +1768,16 @@ namespace eval punk::console { 4 - mode is permanently unset The response is automatically retrieved from the terminal - and so should not be displayed unless there is such a + and so should not be displayed unless there is such a significant delay that the request times out. The value of is returned by the dec_get_mode function. + + The delay in retrieving a terminal response can range from + a few ms to 10s of ms depending on the terminal or other + runtime conditions. + In some cases it may be beneficial to call dec_has_mode first, + (which is cached) to reduce the number of queries to the terminal. } @opts -console -type list -minsize 2 -default {stdin stdout} @@ -1910,10 +1921,24 @@ namespace eval punk::console { the sequence: ESC [ ? $ p Where is an integer identifier. + + Will return zero if the mode is unsupported, 1|2|3|4 if supported. + + The result is cached. + Cached values of 1 or 2 may not represent current state. + ${$B}dec_get_mode${$N} or ${$B}dec_has_mode -refresh${$N} should be + used if up-to-date current state of a supported mode is required. } @opts -console -type list -minsize 2 -default {stdin stdout} - -refresh -type none + -refresh -type none -help\ + "Force a re-test of the mode." + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } @values -min 1 -max 1 mode -type {int|string} -help\ "integer for DEC mode, or name as in the dict: @@ -1925,11 +1950,8 @@ namespace eval punk::console { lassign [dict values $argd] leaders opts values received set console [dict get $opts -console] set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 - } else { - set do_refresh 0 - } + set do_refresh [dict exists $received -refresh] + set return [dict get $opts -return] if {[string is integer -strict $num_or_name]} { set m $num_or_name @@ -1952,10 +1974,23 @@ namespace eval punk::console { set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[\?%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[?$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set dec_has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + #we can use the payload result as the response as non-zero responses evaluate to true + set has_mode $payload + if {$has_mode ne ""} { + dict set dec_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { set has_mode [dict get $dec_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } @@ -1971,64 +2006,148 @@ namespace eval punk::console { -console -type list -minsize 2 -default {stdin stdout} -test -type none -help\ "Test current value/support for each mode" - @values -min 0 -max 0 + -supported -type none -help\ + "Limit results to supported DEC modes" + @values -min 0 -max -1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] proc dec_modes {args} { set argd [punk::args::parse $args withid ::punk::console::dec_modes] lassign [dict values $argd] leaders opts values received set terminal [dict get $opts -console] - if {[dict exists $received -test]} { - set do_test 1 + set do_test [dict exists $received -test] + set only_supported [dict exists $received -supported] + if {[dict exists $values match]} { + set matches [dict get $values match] } else { - set do_test 0 + set matches {} } upvar ::punk::ansi::decmode_data decmode_data + upvar ::punk::ansi::decmode_names decmode_names set t [textblock::class::table new "Dec Modes"] $t configure -show_header 1 -show_hseps 1 $t add_column -headers Code $t add_column -headers Names $t add_column -headers Origin $t add_column -headers Description + $t add_column -headers set-state + $t add_column -headers reset-state if {$do_test} { $t add_column -headers Status } - dict for {code items} $decmode_data { + set names [dict keys $decmode_names] + if {[llength $matches] == 0} { + #show all entries + set codes [dict keys $decmode_data] + } else { + set codes [list] + foreach m $matches { + if {[punk::lib::is_indexset $m]} { + set defaultmax 10000 ;#some codes are very high e.g foot IME 737769 - but we don't want to default test/scan for hours + #todo set max to actual largest value from indexset + lappend codes {*}[punk::lib::indexset_resolve -base 1 $defaultmax $m] + } else { + foreach nm $names { + if {[string match -nocase $m $nm]} { + set code [dict get $decmode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $decmode_names $nm] + } + } + } + } + } + } + #dict for {code items} $decmode_data {} + foreach code $codes { + if {[dict exists $decmode_data $code]} { + set items [dict get $decmode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } set colour "" + set set_state_colour "" + set reset_state_colour "" set RST "" if {$do_test} { - set testresult [dec_get_mode $code] + #dec_has_mode can be cached - in which case only 0|3|4 can be relied upon without re-querying + set hasmode_dict [dec_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [dec_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + switch -- $testresult { 0 { set colour [punk::ansi::a+ red bold] } 1 { set colour [punk::ansi::a+ green] + set set_state_colour $colour } 2 { set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour } 3 { set colour [punk::ansi::a+ green] + set set_state_colour $colour } 4 { set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour } default { - #unexpected + #failedquery + set colour [punk::ansi::a+ red bold] } } if {$colour ne ""} { set RST "\x1b\[m" } set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #dec_has_mode still queries terminal - but is cached if a response was received + if {[dec_has_mode -console $terminal $code] == 0} { + continue + } + } } foreach itm $items { set code $colour$code$RST set names $colour[dict get $itm names]$RST set origin [dict get $itm origin] set desc [dict get $itm description] - set row [list $code [join $names \n] $origin $desc] + set set_state $set_state_colour[dict get $itm set-state]$RST + set reset_state $reset_state_colour[dict get $itm reset-state]$RST + set row [list $code [join $names \n] $origin $desc $set_state $reset_state] if {$do_test} { lappend row $testdisplay } @@ -2059,6 +2178,12 @@ namespace eval punk::console { #-console -type list -typesynopsis {{${$I}inputchan${$NI} ${$I}outputchan${$NI}}} -minsize 2 -default {stdin stdout} -console -type list -minsize 2 -default {stdin stdout} -refresh -type none + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } @values -min 1 -max 1 mode -type {int|string} -help\ "integer for ANSI mode, or name as in the dict: @@ -2070,11 +2195,8 @@ namespace eval punk::console { lassign [dict values $argd] leaders opts values received set console [dict get $opts -console] set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 - } else { - set do_refresh 0 - } + set return [dict get $opts -return] + set do_refresh [dict exists $received -refresh] if {[string is integer -strict $num_or_name]} { set m $num_or_name @@ -2097,10 +2219,22 @@ namespace eval punk::console { set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set ansi_has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + set has_mode $payload + if {$has_mode ne ""} { + dict set ansi_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { set has_mode [dict get $ansi_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } @@ -2272,23 +2406,23 @@ namespace eval punk::console { -console -type list -minsize 2 -default {stdin stdout} -test -type none -help\ "Test current value/support for each mode" + -supported -type none -help\ + "Limit results to supported ANSI modes" @values -min 0 -max -1 - match -type indexset|string -multiple 1 -optional 1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] proc ansi_modes {args} { set argd [punk::args::parse $args withid ::punk::console::ansi_modes] lassign [dict values $argd] leaders opts values received set terminal [dict get $opts -console] - if {[dict exists $received -test]} { - set do_test 1 - } else { - set do_test 0 - } + set do_test [dict exists $received -test] if {[dict exists $values match]} { set matches [dict get $values match] } else { set matches {} } + set only_supported [dict exists $received -supported] upvar ::punk::ansi::ansimode_data ansimode_data upvar ::punk::ansi::ansimode_names ansimode_names @@ -2312,11 +2446,42 @@ namespace eval punk::console { set codes [list] foreach m $matches { if {[punk::lib::is_indexset $m]} { - lappend codes {*}[punk::lib::indexset_resolve -base 1 [dict size $ansimode_data] $m] + set iparts [split $m ,] + #determine the largest index requested - which may be greater than the defined range if explicitly specified + #(allow scanning for unusual ansi modes above 22 - *some* known to exist - private modes should use DECSET DECRESET instead) + #above 22 values seem to be mainly from SCOANSI and Wyse terminals + + set maxint [dict size $ansimode_data] + foreach i $iparts { + if {[string is integer -strict $i]} { + if {$i > $maxint} { + set maxint $i + } + } else { + if {![string match *end* $i]} { + lassign [split [string map [list .. \uFFEF] $i] \uFFEF] a b + if {$a > $b} { + if {$a > $maxint} { + set maxint $a + } + } else { + if {$b > $maxint} { + set maxint $b + } + } + } + } + } + puts "---> maxint: $maxint" + lappend codes {*}[punk::lib::indexset_resolve -base 1 $maxint $m] } else { foreach nm $names { if {[string match -nocase $m $nm]} { - lappend codes [dict get $ansimode_names $nm] + set code [dict get $ansimode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $ansimode_names $nm] + } } } } @@ -2325,13 +2490,46 @@ namespace eval punk::console { #dict for {code items} $ansimode_data {} foreach code $codes { - set items [dict get $ansimode_data $code] + if {[dict exists $ansimode_data $code]} { + set items [dict get $ansimode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } set colour "" set set_state_colour "" set reset_state_colour "" set RST "" if {$do_test} { - set testresult [ansi_get_mode $code] + set hasmode_dict [ansi_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [ansi_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + + #set testresult [ansi_get_mode $code] switch -- $testresult { 0 { set colour [punk::ansi::a+ red bold] @@ -2360,6 +2558,13 @@ namespace eval punk::console { set RST "\x1b\[m" } set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #ansi_has_mode still queries terminal - but is cached if a response was received + if {[ansi_has_mode -console $terminal $code] == 0} { + continue + } + } } foreach itm $items { set code $colour$code$RST @@ -2381,35 +2586,47 @@ namespace eval punk::console { return $out } + #see https://vt100.net/dec/ek-vt520-rm.pdf set DECRQSS_DATA { + "Assign Color" DECAC ",|" + "Alternate Text Color" DECATC ",\}" + "CRT Saver Timing" DECCRTST "-q" + "Down Line Load Allocation" DECDLDA ",z" + "Energy Save Timing" DECSEST "-r" + "Select Auto Repeat Rate" DECARR "-p" "Select Active Status Display" DECSASD "$\}" "Select Attribute Change Extent" DECSACE "*x" "Set Character Attribute" DECSCA "\"q" - "Set Conformance Level" DECSCL "\"p" + "Select Communication Port" DECSCP "*u" "Set Columns Per Page" DECSCPP "\$|" + "Set Conformance Level" DECSCL "\"p" + "Select Communication Speed" DECSCS "*r" + "Set Cursor Style" DECSCUSR " q" + "Select Disconnect Delay Time" DECSDDT "\$q" + "Select Digital Printed Data Type" DECSDPT "(p" + "Select Flow Control Type" DECSFC "*s" + "Set Key Click Volume" DECSKCV " r" + "Set Lock Key Style" DECSLCK " v" + "Set Left and Right Margins" DECSLRM "s" "Set Lines Per Page" DECSLPP "t" + "Set Margin Bell Volume" DECSMBV " u" "Set Number of Lines per Screen" DECSNLS "*|" - "Set Status Line Type" DECSSDT "$~" - "Set Left and Right Margins" DECSLRM "s" - "Set Top and Bottom Margins" DECSTBM "r" - "Set Graphic Rendition" SGR "m" - "Select Set-Up Language" DECSSL "p" + "Session Page Memory Allocation" DECSPMA ",x" + "Set Port Parameter" DECSPP "\+w" + "Select ProPrinter Character Set" DECSPPCS "*p" "Select Printer Type" DECSPRTT "\$s" "Select Refresh Rate" DECSRFR "\"t" - "Select Digital Printed Data Type" DECSDPT "(p" - "Select ProPrinter Character Set" DECSPPCS "*p" - "Select Communication Speed" DECSCS "*r" - "Select Communication Port" DECSCP "*u" "Set Scroll Speed" DECSSCLS " p" - "Set Cursor Style" DECSCUSR " q" - "Set Key Click Volume" DECSKCV " r" - "Set Warning Bell Volume" DECSWBV " t" - "Set Margin Bell Volume" DECSMBV " u" - "Set Lock Key Style" DECSLCK " v" - "Select Flow Control Type" DECSFC "*s" - "Select Disconnect Delay Time" DECSDDT "\$q" + "Set Status Line Type" DECSSDT "$~" + "Select Set-Up Language" DECSSL "p" + "Set Top and Bottom Margins" DECSTBM "r" + "Select Color Lookup Table" DECSTGLT ")\{" "Set Transmit Rate Limit" DECSTRL "u" - "Set Port Parameter" DECSPP "\+w" + "Set Warning Bell Volume" DECSWBV " t" + "Select Zero Symbol" DECSZS ",\{" + "Terminal Mode Emulation" DECTME " ~" + "Set Graphic Rendition" SGR "m" + "invalid" invalid ".." } set DECRQSS_DICT [dict create] foreach {desc name str} $DECRQSS_DATA { @@ -2461,12 +2678,12 @@ namespace eval punk::console { set c1 [string index $payload 0] switch -- $c1 { 0 { - error "dec_request_setting - terminal doesn't recognise request '[punk::ansi::ansistring VIEW $request]' as valid" + error "dec_request_setting - terminal doesn't support querying settings for $name '[punk::ansi::ansistring VIEW $request]'" } 1 {} default { - #shouldn't get here - error "dec_request_setting - unrecognised response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" + #probable timeout - terminal doesn't recognise/respond + error "dec_request_setting - unrecognised or missing response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" } } #strip leading 1$r @@ -2772,7 +2989,12 @@ namespace eval punk::console { puts -nonewline stdout [punk::ansi::clear_above] } proc clear_below {} { - puts -nonewline stdout [punk::ansi::clear_below] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::clear_below] + } else { + puts -nonewline stdout [punk::ansi::vt52clear_below] + } } proc clear_all {} { puts -nonewline stdout [punk::ansi::clear_all] @@ -2785,7 +3007,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_on] } else { - puts -nonewline stdout [punk::ansi::cursor_on_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_on] } } proc cursor_off {} { @@ -2793,7 +3015,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_off] } else { - puts -nonewline stdout [punk::ansi::cursor_off_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_off] } } @@ -2858,10 +3080,20 @@ namespace eval punk::console { } } proc move_up {n} { - puts -nonewline stdout [punk::ansi::move_up $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_up $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_up $n] + } } proc move_down {n} { - puts -nonewline stdout [punk::ansi::move_down $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_down $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_down $n] + } } proc move_column {col} { upvar ::punk::console::is_vt52 is_vt52 @@ -2925,12 +3157,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] append commands [punk::ansi::vt52move_emit $row $col $data] foreach {row col data} $args { append commands [punk::ansi::vt52move_emit $row $col $data] } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands; flush stdout } @@ -2945,12 +3177,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] foreach ln [split $textblock \n] { append commands [punk::ansi::vt52move_emit $row $col $ln] incr row } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands;flush stdout return diff --git a/src/bootsupport/modules/punk/repl-0.1.2.tm b/src/bootsupport/modules/punk/repl-0.1.2.tm index 5a351c2b..0272500d 100644 --- a/src/bootsupport/modules/punk/repl-0.1.2.tm +++ b/src/bootsupport/modules/punk/repl-0.1.2.tm @@ -1529,7 +1529,7 @@ proc repl::repl_handler {inputchan prompt_config} { #puts -nonewline stdout [punk::ansi::move $rows 4]$msg #use cursorsave_ version which avoids get_cursor_pos_list call set msglen [ansistring length $msg] - punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg + punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg ;#supports also vt52 } else { #no mechanism to get console dimensions #we are reduced to continuously spewing lines. diff --git a/src/modules/overtype-999999.0a1.0.tm b/src/modules/overtype-999999.0a1.0.tm index dad03b77..ba698bc0 100644 --- a/src/modules/overtype-999999.0a1.0.tm +++ b/src/modules/overtype-999999.0a1.0.tm @@ -417,6 +417,7 @@ tcl::namespace::eval overtype { set overblock [tcl::string::map {\r\n \n} $overblock] if {$opt_startrow > 1} { set down [expr {$opt_startrow -1}] + #when vt52? set overblock [punk::ansi::move_down $down]$overblock } @@ -4549,7 +4550,7 @@ tcl::namespace::eval overtype { #P3 horizontal grid size - ignored on VT300 - commonly set to zero - # ECMA-48 SSU (ESC Ps I) + # ECMA-48 SSU (ESC [ Ps I) # 0 - CHARACTER # 1 - MILLIMETRE # 2 - COMPUTER DECIPOINT 0.03528mm 1/720 of 25.4mm) diff --git a/src/modules/punk/ansi-999999.0a1.0.tm b/src/modules/punk/ansi-999999.0a1.0.tm index f0bec472..8c28bf55 100644 --- a/src/modules/punk/ansi-999999.0a1.0.tm +++ b/src/modules/punk/ansi-999999.0a1.0.tm @@ -4670,6 +4670,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun clear_below]] return \033\[0J } + proc vt52clear_below {} { + return \033J + } proc clear_all {} { # - doesn't work?? @@ -4688,10 +4691,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun cursor_off]] return "\033\[?25l" } - proc cursor_on_vt52 {} { + proc vt52cursor_on {} { return \x1be } - proc cursor_off_vt52 {} { + proc vt52cursor_off {} { return \x1bf } @@ -4781,7 +4784,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu Sequence is of the form: ESCY This sequence will generally not be understood by terminals - that are not in vt52 mode (e.g DECANM unset). + that are not in vt52 mode (i.e DECANM unset). } @values -min 2 -max 2 row -type integer -help\ @@ -4959,10 +4962,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[para] DECRC return \x1b8 } - proc cursor_save_vt52 {} { + proc vt52cursor_save {} { return \x1bj } - proc cursor_restore_vt52 {} { + proc vt52cursor_restore {} { return \x1bk } @@ -5021,29 +5024,19 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # \x1b\[?7\;2\$y #where 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset) + + #names for other alt_screen mechanismk: 1047,1048 vs 1049? + #https://wiki.tau.garden/dec-modes/ #(DEC,xterm,contour,mintty,kitty etc) #https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking - #names for other alt_screen mechanismk: 1047,1048 vs 1049? - #variable decmode_names [dict create\ - # DECANM 2\ - # origin 6\ - # DECCOLM 3\ - # line_wrap 7\ - # alt_screen 1049\ - # grapheme_clusters 2027\ - # bracketed_paste 2004\ - # mouse_sgr 1006\ - # mouse_urxvt 1015\ - # mouse_sgr_pixel 1016\ - #] - # # some more ansi mode/sequence info: #https://pkg.go.dev/github.com/charmbracelet/x/ansi #see also: https://ucs-detect.readthedocs.io/results.html#dec-private-modes-support + #REVIEW - these modes are sometimes used for different things on different terminals #For now we are assigning based on common usage in things like xterm & windows terminal #Proper handling would require tables for various terminals - review. @@ -5052,140 +5045,279 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #unset with DECRST ESC [ ? l variable decmode_data { 1 { - {origin DEC description "DECCKM - Cursor Keys Mode" names {DECCKM cursor_keys}} + {origin DEC reset-state "cursor sequences" set-state "application sequences" description "DECCKM - Arrow Keys Cursor Keys Mode" names {DECCKM cursor_keys}} } 2 { - {origin DEC description "DECANM - ANSI/VT52 Mode" names {DECANM} note { + {origin DEC reset-state "VT52" set-state "ANSI" description "DECANM - ANSI/VT52 Mode" names {DECANM} note { Disable to turn on VT52 emulation. In VT52 mode - use \x1b< to exit. } } } 3 { - {origin DEC description "DECCOLM - Column" names {DECCOLM}} + {origin DEC reset-state "80-column font" set-state "132-column font" + description "DECCOLM - Select 80 or 132 Columns per page +(obs - use DECSCPP)" + names {DECCOLM} + see "https://vt100.net/docs/vt510-rm/DECCOLM.html"} } 4 { - {origin DEC description "DECSCLM - Scrolling" names {DECSCLM}} + {origin DEC reset-state "jump scroll" set-state "smooth scroll" description "DECSCLM - Scrolling Mode" names {DECSCLM}} } 5 { - {origin DEC description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} + {origin DEC reset-state "dark mode" set-state "light mode" description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} } 6 { - {origin DEC description "DECOM - Origin Mode (whether cursor is restricted to within page margins)" names {DECOM}} + {origin DEC reset-state "upper-left corner" set-state "within margins" + description "DECOM - Origin Mode +(whether cursor is restricted to within page margins)" + names {DECOM}} } 7 { - {origin DEC description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} + {origin DEC reset-state "no autowrap" set-state "autowrap" description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} } 8 { - {origin DEC description "DECARM - Auto Repeat Mode" names {DECARM autorepeat}} + {origin DEC reset-state "disabled" set-state "0.5s keypress autorepeat" + description "DECARM - Auto Repeat Mode" + see "https://vt100.net/docs/vt510-rm/DECARM.html" + names {DECARM autorepeat}} } 9 { - {origin "xterm" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { + {origin "xterm" reset-state "disable" set-state "enable" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { Escape sequence on button press only. CSI M CbCxCy (6 chars) Coords limited to 223 (=255 - 32) } } - {origin DEC description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + {origin DEC reset-state "" set-state "" description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + } + 10 { + {origin DEC reset-state "" set-state "" description "DECEDM - Editing Mode" names {DECEDM}} + {origin rxvt reset-state "" set-state "" description "show toolbar" names {RXVTSHOWTOOLBAR}} + } + 11 { + {origin DEC reset-state "" set-state "" description "DECLTM - Line Transmit Mode" names {DECLTM}} } 12 { - {origin xterm description "Cursor blink" names {XTCBLINK cursorblink}} - {origin DEC description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} + {origin xterm reset-state "steady" set-state "blink" description "Cursor blink" names {XTCBLINK cursorblink}} + {origin DEC reset-state "" set-state "" description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} } 25 { - {origin DEC description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} + {origin DEC reset-state "invisible" set-state "visible" description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} } 40 { - {origin DEC description "New Line Mode" names {DECCRNLM newline_mode}} - {origin xterm description "Allow 80->132 mode" names {xt80-132}} + {origin DEC reset-state "" set-state "" description "New Line Mode" names {DECCRNLM newline_mode}} + {origin xterm reset-state "" set-state "" description "Allow 80->132 mode" names {xt80-132}} } 45 { - {origin DEC description "Graphics Print Color Syntax" names {DECGPCS}} - {origin xterm description "Reverse Wraparound Mode" names {reverse_wraparound}} + {origin DEC reset-state "" set-state "" description "Graphics Print Color Syntax" names {DECGPCS}} + {origin xterm reset-state "" set-state "" description "Reverse Wraparound Mode" names {reverse_wraparound}} } 47 { - {origin xterm description "xterm alternate buffer" names {xterm_altbuf}} - {origin DEC description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} + {origin xterm reset-state "" set-state "" description "xterm alternate buffer" names {xterm_altbuf}} + {origin DEC reset-state "" set-state "" description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} } 64 { - {origin DEC description "DECPCCM - Page Cursor Coupling Mode" names {DECPCCM}} + {origin DEC reset-state "uncoupled" set-state "coupled" description "DECPCCM - Page Cursor Coupling Mode" + see "https://vt100.net/docs/vt510-rm/DECPCCM.html" + names {DECPCCM}} } 66 { - {origin DEC description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} + {origin DEC reset-state "keypad chars" set-state "app sequences" description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} } 67 { - {origin DEC description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} + {origin DEC reset-state "delete key" set-state "backspace key" description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} } 69 { - {origin DEC description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} + {origin DEC reset-state "cannot set margins" set-state "can set margins" description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} } 80 { - {origin ??? description "Sixel Display Mode" see "" names {sixel_display}} + {origin DEC reset-state "scrolling enabled" set-state "scrolling disabled" description "Sixel Display Mode +(VT382 disable on set) +(VT330 manual error?) +some emulators may reverse" + see "https://github.com/dankamongmen/notcurses/issues/1782" + names {sixel_display DECSDM}} + } + 95 { + {origin DEC reset-state "clear screen on column change" set-state "no clear on column change" + description "No Clearing Screen on Column Change Mode" see "" + names {DECNCSM}} + } + 98 { + {origin DEC reset-state "disabled" set-state "enabled" description "Auto Resize Mode" see "" names {DECARSM}} + } + 115 { + {origin DEC reset-state "disabled" set-state "enabled" description "Alternate Text Color Blink Mode" see "" names {DECATCBM}} } 117 { - {origin ??? description "Erase Color Mode" see "" names {erase_color}} + {origin DEC reset-state "" set-state "" description "Erase Color Mode" see "" names {DECECM erase_color}} } 1000 { - {origin "xterm" description "VT200 compatibility mouse" names {SET_VT200_MOUSE} note { + {origin "xterm" reset-state "" set-state "" description "VT200 compatibility mouse" + names {MOUSE_REPORT_CLICK SET_VT200_MOUSE} + note { Escape sequence on both button press and release. CSI M CbCxCy } } } 1001 { - {origin "???" description "Use Hilite Mouse Tracking" names {mouse_tracking_hilite}} + {origin "xterm" reset-state "" set-state "" description "Use Hilite Mouse Tracking" + names {MOUSE_HILITE_TRACKING mouse_tracking_hilite}} } 1002 { - {origin "???" description "Use Cell Motion Mouse Tracking" names {mouse_tracking_cellmotion}} + {origin "xterm" reset-state "" set-state "" description "Use Cell Motion Mouse Tracking" + names {MOUSE_REPORT_DRAG mouse_tracking_cellmotion}} } 1003 { - {origin "???" description "Use All Motion Mouse Tracking" names {mouse_tracking_allmotion}} + {origin "xterm" reset-state "" set-state "" description "Use All Motion Mouse Tracking" + names {MOUSE_ALLMOTION mouse_tracking_allmotion}} } 1004 { - {origin "xterm" description "Send FocusIn/FocusOut events" names {mouse_focus_event}} + {origin "xterm" reset-state "" set-state "" description "Send FocusIn/FocusOut events" + names {FOCUS_IN_OUT_EVENTS mouse_focus_event}} } 1005 { - {origin "xterm" description "Enable UTF-8 Mouse Mode" names {mouse_utf8 mouse_utf8_extended}} + {origin "xterm" reset-state "" set-state "" + description "Enable UTF-8 Mouse Mode" + names {MOUSE_EXTENDED_UTF8 mouse_utf8 mouse_utf8_extended}} } 1006 { - {origin "xterm" description "Enable SGR Mouse Mode" names {mouse_sgr mouse_sgr_extended} note{ + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Mouse Mode" names + {MOUSE_EXTENDED_SGR mouse_sgr mouse_sgr_extended} + note{ SET_SGR_EXT_MODE_MOUSE - extended compared to x10 mouse protocol which limits x y coords to 223 (=255 - 32) } } } 1007 { - {origin "???" description "Enable Alternate Scroll Mode" names {alternate_scroll}} + {origin "xterm" reset-state "" set-state "" + description "Enable Alternate Scroll Mode" + names {ALT_SCROLL_XTERM alternate_scroll}} } 1015 { - {origin "urxvt" description "Enable urxvt Mouse Mode" names {mouse_urxvt}} + {origin "urxvt" reset-state "" set-state "" + description "Enable urxvt Mouse Mode" + names {MOUSE_URXVT}} } 1016 { - {origin "xterm" description "Enable SGR Pixel Mouse Mode" names {mouse_sgr_pixel}} + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Pixel Mouse Mode" + names {MOUSE_SGR_PIXELS}} + } + 1042 { + {origin "xterm" reset-state "" set-state "" + description "Enable Urgency window manager hint +when Control-G is received" + names {URGENCY_ON_CTRL_G xt1042}} + } + 1043 { + {origin "xterm" reset-state "" set-state "" + description "Enable raising of the window +when Control-G is received" + names {RAISE_ON_CTRL_G xt1043}} } 1047 { - {origin "xterm" description "Alternate Buffer" names {alt_buffer_only}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer" + names {ALT_SCREEN_BUFFER_XTERM alt_buffer_only}} } 1049 { - {origin "xterm" description "Alternate Buffer with save cursor" names {alt_buffer alt_screen}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer with save cursor" + names {ALT_SCREEN_AND_SAVE_CLEAR alt_buffer alt_screen}} + } + 1070 { + {origin "xterm" reset-state "" set-state "" + description "Use private color registers for each graphic" + names {SIXEL_PRIVATE_PALETTE}} } 2004 { - {origin "xterm" description "Set bracketed paste mode" names {bracketed_paste}} + {origin "xterm" reset-state "" set-state "" + description "Set bracketed paste mode" + names {BRACKETED_PASTE}} + } + 2026 { + {origin iTerm2 reset-state "screen updates" set-state "no screen updates" + description "Synchronized Output" + see "https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036" + names {SYNCHRONIZED_OUTPUT synch}} } 2027 { - {origin Contour description "Grapheme Cluster Processing" names {grapheme_clusters}} + {origin Contour reset-state "" set-state "" + description "Grapheme Cluster Processing" + names {GRAPHEME_CLUSTERING grapheme_clusters}} + } + 2028 { + {origin Contour reset-state "" set-state "" + description "Text reflow" + names {TEXT_REFLOW}} } 2048 { - {origin "rockorager" description "Enable in-band window resize notifications (modern VTEEWR analog)" see "https://rockorager.dev/misc/in-band-resize-notifications/" names {window_resize_reports}} + {origin "@rockorager" reset-state "" set-state "" + description "Enable in-band window resize notifications +(modern VTEEWR analog)" + names {IN_BAND_WINDOW_RESIZE window_resize_reports} + see "https://rockorager.dev/misc/in-band-resize-notifications/" + } } 5522 { - {origin "kitty rockorager" description "Automatic Paste notifications" see "https://rockorager.dev/misc/bracketed-paste-mime/" names {automatic_paste_reports}} + {origin "kitty @rockorager" + reset-state "" set-state "" + description "Automatic Paste notifications" + names {automatic_paste_reports} + see "https://rockorager.dev/misc/bracketed-paste-mime/"} + } + 7700 { + {origin mintty reset-state "disable reporting" set-state "enable reporting" + description "Ambiguous width reporting" + notes "When enabled, mintty sends ^[[1W for an 'ambiguous narrow' font +and ^[[2W for an 'ambiguous wide' font allowing the application +to adjust its layout accordingly." + names {AMBIGUOUS_WIDTH_REPORTING}} + } + 7766 { + {origin mintty reset-state "hide" set-state "show" + description "Show/hide scrollbar" + names {SHOW_HIDE_SCROLLBAR mintty_scrollbar}} + } + 8200 { + {origin TeraTerm reset-state "Don't move cursor" set-state "Cursor to home" + description "Move cursor when erase complete display (ED 2)" + names {TTCTH}} } 8452 { - {origin "rlogin xterm" description "Post sixel cursor position" names {RLSIXPOS}} + {origin "rlogin xterm" + reset-state "" set-state "" + description "Post sixel cursor position" + names {SIXEL_SCROLLING_LEAVES_CURSOR}} } 9001 { - {origin "windows" description "win32 input mode" names {win32-input}} + {origin "conpty" + reset-state "" set-state "" + description "win32 input mode" + names {WIN32_INPUT_MODE}} + } + 19997 { + {origin "kitty" + reset-state "" set-state "" + description "Handle Ctrl-C/Ctrl-Z mode" + names {KITTY_HANDLE_CTRL_C_Z kitty_ctrl_c_ctrl_z}} + } + 77096 { + {origin "mintty" + reset-state "" set-state "" + description "BiDi mode" + names {mintty_bidi}} + } + 737769 { + {origin "foot" + reset-state "" set-state "" + description "Input Method Editor (IME) mode" + names {foot_ime}} } } set decmode_names [dict create] @@ -5205,6 +5337,14 @@ to 223 (=255 - 32) #primary source for ansimode_data: # https://ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf + + #todo - different table for different terminal emulations + #modern terminal emulators generally don't support any modes above 22 + #but some such as SCOANSI, Wyse and S97801 go higher + #e.g + #Wyse wy370 uses CSI 33 h/l for steady vs blinking cursor + #SCOANSI uses 1048 for cursor on + variable ansimode_data { 1 { {origin "ECMA-48" reset-state "replacing" set-state "cumulative" description "Guarded Area Transfer Mode" names {GATM}} @@ -5228,10 +5368,10 @@ to 223 (=255 - 32) {origin "ECMA-48" reset-state "following" set-state "preceding" description "Line Editing Mode (Vertical Editing Mode)" names {VEM}} } 8 { - {origin "???" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} + {origin "ECMA-48" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} } 9 { - {origin "???" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} + {origin "ECMA-48" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} } 10 { {origin "ECMA-48" reset-state "following" set-state "preceding" description "Character Editing Mode (Horizontal Editing Mode)" names {HEM}} @@ -5271,10 +5411,10 @@ note - No control functions are affected. {origin "ECMA-48" reset-state "multiple" set-state "single" description "Tabulation Stop Mode" names {TSM}} } 19 { - {origin "???" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} + {origin "ECMA-48 deprecated" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} } 20 { - {origin "???" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { + {origin "ECMA-48 deprecated" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { For terminals that support LNM, the default is off meaning a lone CR respresents the character emitted when enter is pushed. Turning LNM on would mean that diff --git a/src/modules/punk/console-999999.0a1.0.tm b/src/modules/punk/console-999999.0a1.0.tm index fe937f68..c6e3d485 100644 --- a/src/modules/punk/console-999999.0a1.0.tm +++ b/src/modules/punk/console-999999.0a1.0.tm @@ -1140,7 +1140,12 @@ namespace eval punk::console { return } #tailcall punk::ansi::a {*}$args - ::punk::ansi::a {*}$args + variable is_vt52 + if {$is_vt52} { + return + } else { + ::punk::ansi::a {*}$args + } } lappend PUNKARGS_aliases {::punk::console::code_a? ::punk::ansi::a?} proc code_a? {args} { @@ -1537,11 +1542,11 @@ namespace eval punk::console { set cell_size "" set cell_size_fallback 10x20 - #todo - change -inoutchannels to -terminalobject with prebuilt default + #todo - change -console to -terminalobject with prebuilt default punk::args::define { @id -id ::punk::console::cell_size - -inoutchannels -default {stdin stdout} -type list + -console -default {stdin stdout} -type list @values -min 0 -max 1 newsize -default "" -help\ "character cell pixel dimensions WxH @@ -1549,7 +1554,7 @@ namespace eval punk::console { } proc cell_size {args} { set argd [punk::args::parse $args -cache 1 withid ::punk::console::cell_size] - set inoutchannels [dict get $argd opts -inoutchannels] + set terminal [dict get $argd opts -console] set newsize [dict get $argd values newsize] variable cell_size @@ -1557,7 +1562,7 @@ namespace eval punk::console { #query existing setting if {$cell_size eq ""} { #not set - try to query terminal's overall dimensions - set pixeldict [punk::console::get_xterm_pixels $inoutchannels] + set pixeldict [punk::console::get_xterm_pixels $terminal] lassign $pixeldict _w sw _h sh if {[string is integer -strict $sw] && [string is integer -strict $sh]} { lassign [punk::console::get_size] _cols columns _rows rows @@ -1651,8 +1656,8 @@ namespace eval punk::console { set func_con "punk::ansi::cursor_on" } else { set movefunc "punk::ansi::vt52move" - set func_coff "punk::ansi::cursor_off_vt52" - set func_con "punk::ansi::cursor_on_vt52" + 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. @@ -1763,10 +1768,16 @@ namespace eval punk::console { 4 - mode is permanently unset The response is automatically retrieved from the terminal - and so should not be displayed unless there is such a + and so should not be displayed unless there is such a significant delay that the request times out. The value of is returned by the dec_get_mode function. + + The delay in retrieving a terminal response can range from + a few ms to 10s of ms depending on the terminal or other + runtime conditions. + In some cases it may be beneficial to call dec_has_mode first, + (which is cached) to reduce the number of queries to the terminal. } @opts -console -type list -minsize 2 -default {stdin stdout} @@ -1910,10 +1921,24 @@ namespace eval punk::console { the sequence: ESC [ ? $ p Where is an integer identifier. + + Will return zero if the mode is unsupported, 1|2|3|4 if supported. + + The result is cached. + Cached values of 1 or 2 may not represent current state. + ${$B}dec_get_mode${$N} or ${$B}dec_has_mode -refresh${$N} should be + used if up-to-date current state of a supported mode is required. } @opts -console -type list -minsize 2 -default {stdin stdout} - -refresh -type none + -refresh -type none -help\ + "Force a re-test of the mode." + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } @values -min 1 -max 1 mode -type {int|string} -help\ "integer for DEC mode, or name as in the dict: @@ -1925,11 +1950,8 @@ namespace eval punk::console { lassign [dict values $argd] leaders opts values received set console [dict get $opts -console] set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 - } else { - set do_refresh 0 - } + set do_refresh [dict exists $received -refresh] + set return [dict get $opts -return] if {[string is integer -strict $num_or_name]} { set m $num_or_name @@ -1952,10 +1974,23 @@ namespace eval punk::console { set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[\?%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[?$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set dec_has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + #we can use the payload result as the response as non-zero responses evaluate to true + set has_mode $payload + if {$has_mode ne ""} { + dict set dec_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { set has_mode [dict get $dec_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } @@ -1971,64 +2006,148 @@ namespace eval punk::console { -console -type list -minsize 2 -default {stdin stdout} -test -type none -help\ "Test current value/support for each mode" - @values -min 0 -max 0 + -supported -type none -help\ + "Limit results to supported DEC modes" + @values -min 0 -max -1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] proc dec_modes {args} { set argd [punk::args::parse $args withid ::punk::console::dec_modes] lassign [dict values $argd] leaders opts values received set terminal [dict get $opts -console] - if {[dict exists $received -test]} { - set do_test 1 + set do_test [dict exists $received -test] + set only_supported [dict exists $received -supported] + if {[dict exists $values match]} { + set matches [dict get $values match] } else { - set do_test 0 + set matches {} } upvar ::punk::ansi::decmode_data decmode_data + upvar ::punk::ansi::decmode_names decmode_names set t [textblock::class::table new "Dec Modes"] $t configure -show_header 1 -show_hseps 1 $t add_column -headers Code $t add_column -headers Names $t add_column -headers Origin $t add_column -headers Description + $t add_column -headers set-state + $t add_column -headers reset-state if {$do_test} { $t add_column -headers Status } - dict for {code items} $decmode_data { + set names [dict keys $decmode_names] + if {[llength $matches] == 0} { + #show all entries + set codes [dict keys $decmode_data] + } else { + set codes [list] + foreach m $matches { + if {[punk::lib::is_indexset $m]} { + set defaultmax 10000 ;#some codes are very high e.g foot IME 737769 - but we don't want to default test/scan for hours + #todo set max to actual largest value from indexset + lappend codes {*}[punk::lib::indexset_resolve -base 1 $defaultmax $m] + } else { + foreach nm $names { + if {[string match -nocase $m $nm]} { + set code [dict get $decmode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $decmode_names $nm] + } + } + } + } + } + } + #dict for {code items} $decmode_data {} + foreach code $codes { + if {[dict exists $decmode_data $code]} { + set items [dict get $decmode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } set colour "" + set set_state_colour "" + set reset_state_colour "" set RST "" if {$do_test} { - set testresult [dec_get_mode $code] + #dec_has_mode can be cached - in which case only 0|3|4 can be relied upon without re-querying + set hasmode_dict [dec_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [dec_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + switch -- $testresult { 0 { set colour [punk::ansi::a+ red bold] } 1 { set colour [punk::ansi::a+ green] + set set_state_colour $colour } 2 { set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour } 3 { set colour [punk::ansi::a+ green] + set set_state_colour $colour } 4 { set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour } default { - #unexpected + #failedquery + set colour [punk::ansi::a+ red bold] } } if {$colour ne ""} { set RST "\x1b\[m" } set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #dec_has_mode still queries terminal - but is cached if a response was received + if {[dec_has_mode -console $terminal $code] == 0} { + continue + } + } } foreach itm $items { set code $colour$code$RST set names $colour[dict get $itm names]$RST set origin [dict get $itm origin] set desc [dict get $itm description] - set row [list $code [join $names \n] $origin $desc] + set set_state $set_state_colour[dict get $itm set-state]$RST + set reset_state $reset_state_colour[dict get $itm reset-state]$RST + set row [list $code [join $names \n] $origin $desc $set_state $reset_state] if {$do_test} { lappend row $testdisplay } @@ -2059,6 +2178,12 @@ namespace eval punk::console { #-console -type list -typesynopsis {{${$I}inputchan${$NI} ${$I}outputchan${$NI}}} -minsize 2 -default {stdin stdout} -console -type list -minsize 2 -default {stdin stdout} -refresh -type none + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } @values -min 1 -max 1 mode -type {int|string} -help\ "integer for ANSI mode, or name as in the dict: @@ -2070,11 +2195,8 @@ namespace eval punk::console { lassign [dict values $argd] leaders opts values received set console [dict get $opts -console] set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 - } else { - set do_refresh 0 - } + set return [dict get $opts -return] + set do_refresh [dict exists $received -refresh] if {[string is integer -strict $num_or_name]} { set m $num_or_name @@ -2097,10 +2219,22 @@ namespace eval punk::console { set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set ansi_has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + set has_mode $payload + if {$has_mode ne ""} { + dict set ansi_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { set has_mode [dict get $ansi_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } @@ -2272,23 +2406,23 @@ namespace eval punk::console { -console -type list -minsize 2 -default {stdin stdout} -test -type none -help\ "Test current value/support for each mode" + -supported -type none -help\ + "Limit results to supported ANSI modes" @values -min 0 -max -1 - match -type indexset|string -multiple 1 -optional 1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] proc ansi_modes {args} { set argd [punk::args::parse $args withid ::punk::console::ansi_modes] lassign [dict values $argd] leaders opts values received set terminal [dict get $opts -console] - if {[dict exists $received -test]} { - set do_test 1 - } else { - set do_test 0 - } + set do_test [dict exists $received -test] if {[dict exists $values match]} { set matches [dict get $values match] } else { set matches {} } + set only_supported [dict exists $received -supported] upvar ::punk::ansi::ansimode_data ansimode_data upvar ::punk::ansi::ansimode_names ansimode_names @@ -2312,11 +2446,42 @@ namespace eval punk::console { set codes [list] foreach m $matches { if {[punk::lib::is_indexset $m]} { - lappend codes {*}[punk::lib::indexset_resolve -base 1 [dict size $ansimode_data] $m] + set iparts [split $m ,] + #determine the largest index requested - which may be greater than the defined range if explicitly specified + #(allow scanning for unusual ansi modes above 22 - *some* known to exist - private modes should use DECSET DECRESET instead) + #above 22 values seem to be mainly from SCOANSI and Wyse terminals + + set maxint [dict size $ansimode_data] + foreach i $iparts { + if {[string is integer -strict $i]} { + if {$i > $maxint} { + set maxint $i + } + } else { + if {![string match *end* $i]} { + lassign [split [string map [list .. \uFFEF] $i] \uFFEF] a b + if {$a > $b} { + if {$a > $maxint} { + set maxint $a + } + } else { + if {$b > $maxint} { + set maxint $b + } + } + } + } + } + puts "---> maxint: $maxint" + lappend codes {*}[punk::lib::indexset_resolve -base 1 $maxint $m] } else { foreach nm $names { if {[string match -nocase $m $nm]} { - lappend codes [dict get $ansimode_names $nm] + set code [dict get $ansimode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $ansimode_names $nm] + } } } } @@ -2325,13 +2490,46 @@ namespace eval punk::console { #dict for {code items} $ansimode_data {} foreach code $codes { - set items [dict get $ansimode_data $code] + if {[dict exists $ansimode_data $code]} { + set items [dict get $ansimode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } set colour "" set set_state_colour "" set reset_state_colour "" set RST "" if {$do_test} { - set testresult [ansi_get_mode $code] + set hasmode_dict [ansi_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [ansi_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + + #set testresult [ansi_get_mode $code] switch -- $testresult { 0 { set colour [punk::ansi::a+ red bold] @@ -2360,6 +2558,13 @@ namespace eval punk::console { set RST "\x1b\[m" } set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #ansi_has_mode still queries terminal - but is cached if a response was received + if {[ansi_has_mode -console $terminal $code] == 0} { + continue + } + } } foreach itm $items { set code $colour$code$RST @@ -2381,35 +2586,47 @@ namespace eval punk::console { return $out } + #see https://vt100.net/dec/ek-vt520-rm.pdf set DECRQSS_DATA { + "Assign Color" DECAC ",|" + "Alternate Text Color" DECATC ",\}" + "CRT Saver Timing" DECCRTST "-q" + "Down Line Load Allocation" DECDLDA ",z" + "Energy Save Timing" DECSEST "-r" + "Select Auto Repeat Rate" DECARR "-p" "Select Active Status Display" DECSASD "$\}" "Select Attribute Change Extent" DECSACE "*x" "Set Character Attribute" DECSCA "\"q" - "Set Conformance Level" DECSCL "\"p" + "Select Communication Port" DECSCP "*u" "Set Columns Per Page" DECSCPP "\$|" + "Set Conformance Level" DECSCL "\"p" + "Select Communication Speed" DECSCS "*r" + "Set Cursor Style" DECSCUSR " q" + "Select Disconnect Delay Time" DECSDDT "\$q" + "Select Digital Printed Data Type" DECSDPT "(p" + "Select Flow Control Type" DECSFC "*s" + "Set Key Click Volume" DECSKCV " r" + "Set Lock Key Style" DECSLCK " v" + "Set Left and Right Margins" DECSLRM "s" "Set Lines Per Page" DECSLPP "t" + "Set Margin Bell Volume" DECSMBV " u" "Set Number of Lines per Screen" DECSNLS "*|" - "Set Status Line Type" DECSSDT "$~" - "Set Left and Right Margins" DECSLRM "s" - "Set Top and Bottom Margins" DECSTBM "r" - "Set Graphic Rendition" SGR "m" - "Select Set-Up Language" DECSSL "p" + "Session Page Memory Allocation" DECSPMA ",x" + "Set Port Parameter" DECSPP "\+w" + "Select ProPrinter Character Set" DECSPPCS "*p" "Select Printer Type" DECSPRTT "\$s" "Select Refresh Rate" DECSRFR "\"t" - "Select Digital Printed Data Type" DECSDPT "(p" - "Select ProPrinter Character Set" DECSPPCS "*p" - "Select Communication Speed" DECSCS "*r" - "Select Communication Port" DECSCP "*u" "Set Scroll Speed" DECSSCLS " p" - "Set Cursor Style" DECSCUSR " q" - "Set Key Click Volume" DECSKCV " r" - "Set Warning Bell Volume" DECSWBV " t" - "Set Margin Bell Volume" DECSMBV " u" - "Set Lock Key Style" DECSLCK " v" - "Select Flow Control Type" DECSFC "*s" - "Select Disconnect Delay Time" DECSDDT "\$q" + "Set Status Line Type" DECSSDT "$~" + "Select Set-Up Language" DECSSL "p" + "Set Top and Bottom Margins" DECSTBM "r" + "Select Color Lookup Table" DECSTGLT ")\{" "Set Transmit Rate Limit" DECSTRL "u" - "Set Port Parameter" DECSPP "\+w" + "Set Warning Bell Volume" DECSWBV " t" + "Select Zero Symbol" DECSZS ",\{" + "Terminal Mode Emulation" DECTME " ~" + "Set Graphic Rendition" SGR "m" + "invalid" invalid ".." } set DECRQSS_DICT [dict create] foreach {desc name str} $DECRQSS_DATA { @@ -2461,12 +2678,12 @@ namespace eval punk::console { set c1 [string index $payload 0] switch -- $c1 { 0 { - error "dec_request_setting - terminal doesn't recognise request '[punk::ansi::ansistring VIEW $request]' as valid" + error "dec_request_setting - terminal doesn't support querying settings for $name '[punk::ansi::ansistring VIEW $request]'" } 1 {} default { - #shouldn't get here - error "dec_request_setting - unrecognised response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" + #probable timeout - terminal doesn't recognise/respond + error "dec_request_setting - unrecognised or missing response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" } } #strip leading 1$r @@ -2772,7 +2989,12 @@ namespace eval punk::console { puts -nonewline stdout [punk::ansi::clear_above] } proc clear_below {} { - puts -nonewline stdout [punk::ansi::clear_below] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::clear_below] + } else { + puts -nonewline stdout [punk::ansi::vt52clear_below] + } } proc clear_all {} { puts -nonewline stdout [punk::ansi::clear_all] @@ -2785,7 +3007,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_on] } else { - puts -nonewline stdout [punk::ansi::cursor_on_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_on] } } proc cursor_off {} { @@ -2793,7 +3015,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_off] } else { - puts -nonewline stdout [punk::ansi::cursor_off_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_off] } } @@ -2858,10 +3080,20 @@ namespace eval punk::console { } } proc move_up {n} { - puts -nonewline stdout [punk::ansi::move_up $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_up $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_up $n] + } } proc move_down {n} { - puts -nonewline stdout [punk::ansi::move_down $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_down $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_down $n] + } } proc move_column {col} { upvar ::punk::console::is_vt52 is_vt52 @@ -2925,12 +3157,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] append commands [punk::ansi::vt52move_emit $row $col $data] foreach {row col data} $args { append commands [punk::ansi::vt52move_emit $row $col $data] } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands; flush stdout } @@ -2945,12 +3177,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] foreach ln [split $textblock \n] { append commands [punk::ansi::vt52move_emit $row $col $ln] incr row } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands;flush stdout return diff --git a/src/modules/punk/repl-999999.0a1.0.tm b/src/modules/punk/repl-999999.0a1.0.tm index 9268455c..e9ce7aef 100644 --- a/src/modules/punk/repl-999999.0a1.0.tm +++ b/src/modules/punk/repl-999999.0a1.0.tm @@ -1529,7 +1529,7 @@ proc repl::repl_handler {inputchan prompt_config} { #puts -nonewline stdout [punk::ansi::move $rows 4]$msg #use cursorsave_ version which avoids get_cursor_pos_list call set msglen [ansistring length $msg] - punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg + punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg ;#supports also vt52 } else { #no mechanism to get console dimensions #we are reduced to continuously spewing lines. diff --git a/src/modules/punk/sixel-999999.0a1.0.tm b/src/modules/punk/sixel-999999.0a1.0.tm index ea51b313..fef9356d 100644 --- a/src/modules/punk/sixel-999999.0a1.0.tm +++ b/src/modules/punk/sixel-999999.0a1.0.tm @@ -141,7 +141,7 @@ tcl::namespace::eval punk::sixel { set cell_size [punk::console::cell_size] } lassign [split $cell_size x] cwidth cheight - set height_cells [expr {int(ceil($height_pixels /double($cheight)))}] + set height_cells [expr {int(ceil($height_pixels /double($cheight)))}] set sixelparams "" set sixel_extents [list] ;#number of sixels in each line taking into account retraces due to $ diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/overtype-1.7.4.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/overtype-1.7.4.tm index d3a642da..d1d0dd44 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/overtype-1.7.4.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/overtype-1.7.4.tm @@ -417,6 +417,7 @@ tcl::namespace::eval overtype { set overblock [tcl::string::map {\r\n \n} $overblock] if {$opt_startrow > 1} { set down [expr {$opt_startrow -1}] + #when vt52? set overblock [punk::ansi::move_down $down]$overblock } @@ -4549,7 +4550,7 @@ tcl::namespace::eval overtype { #P3 horizontal grid size - ignored on VT300 - commonly set to zero - # ECMA-48 SSU (ESC Ps I) + # ECMA-48 SSU (ESC [ Ps I) # 0 - CHARACTER # 1 - MILLIMETRE # 2 - COMPUTER DECIPOINT 0.03528mm 1/720 of 25.4mm) diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm index 821af950..7bf4bf7c 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm @@ -4670,6 +4670,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun clear_below]] return \033\[0J } + proc vt52clear_below {} { + return \033J + } proc clear_all {} { # - doesn't work?? @@ -4688,10 +4691,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun cursor_off]] return "\033\[?25l" } - proc cursor_on_vt52 {} { + proc vt52cursor_on {} { return \x1be } - proc cursor_off_vt52 {} { + proc vt52cursor_off {} { return \x1bf } @@ -4781,7 +4784,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu Sequence is of the form: ESCY This sequence will generally not be understood by terminals - that are not in vt52 mode (e.g DECANM unset). + that are not in vt52 mode (i.e DECANM unset). } @values -min 2 -max 2 row -type integer -help\ @@ -4959,10 +4962,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[para] DECRC return \x1b8 } - proc cursor_save_vt52 {} { + proc vt52cursor_save {} { return \x1bj } - proc cursor_restore_vt52 {} { + proc vt52cursor_restore {} { return \x1bk } @@ -5021,29 +5024,19 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # \x1b\[?7\;2\$y #where 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset) + + #names for other alt_screen mechanismk: 1047,1048 vs 1049? + #https://wiki.tau.garden/dec-modes/ #(DEC,xterm,contour,mintty,kitty etc) #https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking - #names for other alt_screen mechanismk: 1047,1048 vs 1049? - #variable decmode_names [dict create\ - # DECANM 2\ - # origin 6\ - # DECCOLM 3\ - # line_wrap 7\ - # alt_screen 1049\ - # grapheme_clusters 2027\ - # bracketed_paste 2004\ - # mouse_sgr 1006\ - # mouse_urxvt 1015\ - # mouse_sgr_pixel 1016\ - #] - # # some more ansi mode/sequence info: #https://pkg.go.dev/github.com/charmbracelet/x/ansi #see also: https://ucs-detect.readthedocs.io/results.html#dec-private-modes-support + #REVIEW - these modes are sometimes used for different things on different terminals #For now we are assigning based on common usage in things like xterm & windows terminal #Proper handling would require tables for various terminals - review. @@ -5052,140 +5045,279 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #unset with DECRST ESC [ ? l variable decmode_data { 1 { - {origin DEC description "DECCKM - Cursor Keys Mode" names {DECCKM cursor_keys}} + {origin DEC reset-state "cursor sequences" set-state "application sequences" description "DECCKM - Arrow Keys Cursor Keys Mode" names {DECCKM cursor_keys}} } 2 { - {origin DEC description "DECANM - ANSI/VT52 Mode" names {DECANM} note { + {origin DEC reset-state "VT52" set-state "ANSI" description "DECANM - ANSI/VT52 Mode" names {DECANM} note { Disable to turn on VT52 emulation. In VT52 mode - use \x1b< to exit. } } } 3 { - {origin DEC description "DECCOLM - Column" names {DECCOLM}} + {origin DEC reset-state "80-column font" set-state "132-column font" + description "DECCOLM - Select 80 or 132 Columns per page +(obs - use DECSCPP)" + names {DECCOLM} + see "https://vt100.net/docs/vt510-rm/DECCOLM.html"} } 4 { - {origin DEC description "DECSCLM - Scrolling" names {DECSCLM}} + {origin DEC reset-state "jump scroll" set-state "smooth scroll" description "DECSCLM - Scrolling Mode" names {DECSCLM}} } 5 { - {origin DEC description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} + {origin DEC reset-state "dark mode" set-state "light mode" description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} } 6 { - {origin DEC description "DECOM - Origin Mode (whether cursor is restricted to within page margins)" names {DECOM}} + {origin DEC reset-state "upper-left corner" set-state "within margins" + description "DECOM - Origin Mode +(whether cursor is restricted to within page margins)" + names {DECOM}} } 7 { - {origin DEC description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} + {origin DEC reset-state "no autowrap" set-state "autowrap" description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} } 8 { - {origin DEC description "DECARM - Auto Repeat Mode" names {DECARM autorepeat}} + {origin DEC reset-state "disabled" set-state "0.5s keypress autorepeat" + description "DECARM - Auto Repeat Mode" + see "https://vt100.net/docs/vt510-rm/DECARM.html" + names {DECARM autorepeat}} } 9 { - {origin "xterm" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { + {origin "xterm" reset-state "disable" set-state "enable" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { Escape sequence on button press only. CSI M CbCxCy (6 chars) Coords limited to 223 (=255 - 32) } } - {origin DEC description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + {origin DEC reset-state "" set-state "" description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + } + 10 { + {origin DEC reset-state "" set-state "" description "DECEDM - Editing Mode" names {DECEDM}} + {origin rxvt reset-state "" set-state "" description "show toolbar" names {RXVTSHOWTOOLBAR}} + } + 11 { + {origin DEC reset-state "" set-state "" description "DECLTM - Line Transmit Mode" names {DECLTM}} } 12 { - {origin xterm description "Cursor blink" names {XTCBLINK cursorblink}} - {origin DEC description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} + {origin xterm reset-state "steady" set-state "blink" description "Cursor blink" names {XTCBLINK cursorblink}} + {origin DEC reset-state "" set-state "" description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} } 25 { - {origin DEC description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} + {origin DEC reset-state "invisible" set-state "visible" description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} } 40 { - {origin DEC description "New Line Mode" names {DECCRNLM newline_mode}} - {origin xterm description "Allow 80->132 mode" names {xt80-132}} + {origin DEC reset-state "" set-state "" description "New Line Mode" names {DECCRNLM newline_mode}} + {origin xterm reset-state "" set-state "" description "Allow 80->132 mode" names {xt80-132}} } 45 { - {origin DEC description "Graphics Print Color Syntax" names {DECGPCS}} - {origin xterm description "Reverse Wraparound Mode" names {reverse_wraparound}} + {origin DEC reset-state "" set-state "" description "Graphics Print Color Syntax" names {DECGPCS}} + {origin xterm reset-state "" set-state "" description "Reverse Wraparound Mode" names {reverse_wraparound}} } 47 { - {origin xterm description "xterm alternate buffer" names {xterm_altbuf}} - {origin DEC description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} + {origin xterm reset-state "" set-state "" description "xterm alternate buffer" names {xterm_altbuf}} + {origin DEC reset-state "" set-state "" description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} } 64 { - {origin DEC description "DECPCCM - Page Cursor Coupling Mode" names {DECPCCM}} + {origin DEC reset-state "uncoupled" set-state "coupled" description "DECPCCM - Page Cursor Coupling Mode" + see "https://vt100.net/docs/vt510-rm/DECPCCM.html" + names {DECPCCM}} } 66 { - {origin DEC description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} + {origin DEC reset-state "keypad chars" set-state "app sequences" description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} } 67 { - {origin DEC description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} + {origin DEC reset-state "delete key" set-state "backspace key" description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} } 69 { - {origin DEC description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} + {origin DEC reset-state "cannot set margins" set-state "can set margins" description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} } 80 { - {origin ??? description "Sixel Display Mode" see "" names {sixel_display}} + {origin DEC reset-state "scrolling enabled" set-state "scrolling disabled" description "Sixel Display Mode +(VT382 disable on set) +(VT330 manual error?) +some emulators may reverse" + see "https://github.com/dankamongmen/notcurses/issues/1782" + names {sixel_display DECSDM}} + } + 95 { + {origin DEC reset-state "clear screen on column change" set-state "no clear on column change" + description "No Clearing Screen on Column Change Mode" see "" + names {DECNCSM}} + } + 98 { + {origin DEC reset-state "disabled" set-state "enabled" description "Auto Resize Mode" see "" names {DECARSM}} + } + 115 { + {origin DEC reset-state "disabled" set-state "enabled" description "Alternate Text Color Blink Mode" see "" names {DECATCBM}} } 117 { - {origin ??? description "Erase Color Mode" see "" names {erase_color}} + {origin DEC reset-state "" set-state "" description "Erase Color Mode" see "" names {DECECM erase_color}} } 1000 { - {origin "xterm" description "VT200 compatibility mouse" names {SET_VT200_MOUSE} note { + {origin "xterm" reset-state "" set-state "" description "VT200 compatibility mouse" + names {MOUSE_REPORT_CLICK SET_VT200_MOUSE} + note { Escape sequence on both button press and release. CSI M CbCxCy } } } 1001 { - {origin "???" description "Use Hilite Mouse Tracking" names {mouse_tracking_hilite}} + {origin "xterm" reset-state "" set-state "" description "Use Hilite Mouse Tracking" + names {MOUSE_HILITE_TRACKING mouse_tracking_hilite}} } 1002 { - {origin "???" description "Use Cell Motion Mouse Tracking" names {mouse_tracking_cellmotion}} + {origin "xterm" reset-state "" set-state "" description "Use Cell Motion Mouse Tracking" + names {MOUSE_REPORT_DRAG mouse_tracking_cellmotion}} } 1003 { - {origin "???" description "Use All Motion Mouse Tracking" names {mouse_tracking_allmotion}} + {origin "xterm" reset-state "" set-state "" description "Use All Motion Mouse Tracking" + names {MOUSE_ALLMOTION mouse_tracking_allmotion}} } 1004 { - {origin "xterm" description "Send FocusIn/FocusOut events" names {mouse_focus_event}} + {origin "xterm" reset-state "" set-state "" description "Send FocusIn/FocusOut events" + names {FOCUS_IN_OUT_EVENTS mouse_focus_event}} } 1005 { - {origin "xterm" description "Enable UTF-8 Mouse Mode" names {mouse_utf8 mouse_utf8_extended}} + {origin "xterm" reset-state "" set-state "" + description "Enable UTF-8 Mouse Mode" + names {MOUSE_EXTENDED_UTF8 mouse_utf8 mouse_utf8_extended}} } 1006 { - {origin "xterm" description "Enable SGR Mouse Mode" names {mouse_sgr mouse_sgr_extended} note{ + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Mouse Mode" names + {MOUSE_EXTENDED_SGR mouse_sgr mouse_sgr_extended} + note{ SET_SGR_EXT_MODE_MOUSE - extended compared to x10 mouse protocol which limits x y coords to 223 (=255 - 32) } } } 1007 { - {origin "???" description "Enable Alternate Scroll Mode" names {alternate_scroll}} + {origin "xterm" reset-state "" set-state "" + description "Enable Alternate Scroll Mode" + names {ALT_SCROLL_XTERM alternate_scroll}} } 1015 { - {origin "urxvt" description "Enable urxvt Mouse Mode" names {mouse_urxvt}} + {origin "urxvt" reset-state "" set-state "" + description "Enable urxvt Mouse Mode" + names {MOUSE_URXVT}} } 1016 { - {origin "xterm" description "Enable SGR Pixel Mouse Mode" names {mouse_sgr_pixel}} + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Pixel Mouse Mode" + names {MOUSE_SGR_PIXELS}} + } + 1042 { + {origin "xterm" reset-state "" set-state "" + description "Enable Urgency window manager hint +when Control-G is received" + names {URGENCY_ON_CTRL_G xt1042}} + } + 1043 { + {origin "xterm" reset-state "" set-state "" + description "Enable raising of the window +when Control-G is received" + names {RAISE_ON_CTRL_G xt1043}} } 1047 { - {origin "xterm" description "Alternate Buffer" names {alt_buffer_only}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer" + names {ALT_SCREEN_BUFFER_XTERM alt_buffer_only}} } 1049 { - {origin "xterm" description "Alternate Buffer with save cursor" names {alt_buffer alt_screen}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer with save cursor" + names {ALT_SCREEN_AND_SAVE_CLEAR alt_buffer alt_screen}} + } + 1070 { + {origin "xterm" reset-state "" set-state "" + description "Use private color registers for each graphic" + names {SIXEL_PRIVATE_PALETTE}} } 2004 { - {origin "xterm" description "Set bracketed paste mode" names {bracketed_paste}} + {origin "xterm" reset-state "" set-state "" + description "Set bracketed paste mode" + names {BRACKETED_PASTE}} + } + 2026 { + {origin iTerm2 reset-state "screen updates" set-state "no screen updates" + description "Synchronized Output" + see "https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036" + names {SYNCHRONIZED_OUTPUT synch}} } 2027 { - {origin Contour description "Grapheme Cluster Processing" names {grapheme_clusters}} + {origin Contour reset-state "" set-state "" + description "Grapheme Cluster Processing" + names {GRAPHEME_CLUSTERING grapheme_clusters}} + } + 2028 { + {origin Contour reset-state "" set-state "" + description "Text reflow" + names {TEXT_REFLOW}} } 2048 { - {origin "rockorager" description "Enable in-band window resize notifications (modern VTEEWR analog)" see "https://rockorager.dev/misc/in-band-resize-notifications/" names {window_resize_reports}} + {origin "@rockorager" reset-state "" set-state "" + description "Enable in-band window resize notifications +(modern VTEEWR analog)" + names {IN_BAND_WINDOW_RESIZE window_resize_reports} + see "https://rockorager.dev/misc/in-band-resize-notifications/" + } } 5522 { - {origin "kitty rockorager" description "Automatic Paste notifications" see "https://rockorager.dev/misc/bracketed-paste-mime/" names {automatic_paste_reports}} + {origin "kitty @rockorager" + reset-state "" set-state "" + description "Automatic Paste notifications" + names {automatic_paste_reports} + see "https://rockorager.dev/misc/bracketed-paste-mime/"} + } + 7700 { + {origin mintty reset-state "disable reporting" set-state "enable reporting" + description "Ambiguous width reporting" + notes "When enabled, mintty sends ^[[1W for an 'ambiguous narrow' font +and ^[[2W for an 'ambiguous wide' font allowing the application +to adjust its layout accordingly." + names {AMBIGUOUS_WIDTH_REPORTING}} + } + 7766 { + {origin mintty reset-state "hide" set-state "show" + description "Show/hide scrollbar" + names {SHOW_HIDE_SCROLLBAR mintty_scrollbar}} + } + 8200 { + {origin TeraTerm reset-state "Don't move cursor" set-state "Cursor to home" + description "Move cursor when erase complete display (ED 2)" + names {TTCTH}} } 8452 { - {origin "rlogin xterm" description "Post sixel cursor position" names {RLSIXPOS}} + {origin "rlogin xterm" + reset-state "" set-state "" + description "Post sixel cursor position" + names {SIXEL_SCROLLING_LEAVES_CURSOR}} } 9001 { - {origin "windows" description "win32 input mode" names {win32-input}} + {origin "conpty" + reset-state "" set-state "" + description "win32 input mode" + names {WIN32_INPUT_MODE}} + } + 19997 { + {origin "kitty" + reset-state "" set-state "" + description "Handle Ctrl-C/Ctrl-Z mode" + names {KITTY_HANDLE_CTRL_C_Z kitty_ctrl_c_ctrl_z}} + } + 77096 { + {origin "mintty" + reset-state "" set-state "" + description "BiDi mode" + names {mintty_bidi}} + } + 737769 { + {origin "foot" + reset-state "" set-state "" + description "Input Method Editor (IME) mode" + names {foot_ime}} } } set decmode_names [dict create] @@ -5205,6 +5337,14 @@ to 223 (=255 - 32) #primary source for ansimode_data: # https://ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf + + #todo - different table for different terminal emulations + #modern terminal emulators generally don't support any modes above 22 + #but some such as SCOANSI, Wyse and S97801 go higher + #e.g + #Wyse wy370 uses CSI 33 h/l for steady vs blinking cursor + #SCOANSI uses 1048 for cursor on + variable ansimode_data { 1 { {origin "ECMA-48" reset-state "replacing" set-state "cumulative" description "Guarded Area Transfer Mode" names {GATM}} @@ -5228,10 +5368,10 @@ to 223 (=255 - 32) {origin "ECMA-48" reset-state "following" set-state "preceding" description "Line Editing Mode (Vertical Editing Mode)" names {VEM}} } 8 { - {origin "???" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} + {origin "ECMA-48" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} } 9 { - {origin "???" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} + {origin "ECMA-48" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} } 10 { {origin "ECMA-48" reset-state "following" set-state "preceding" description "Character Editing Mode (Horizontal Editing Mode)" names {HEM}} @@ -5271,10 +5411,10 @@ note - No control functions are affected. {origin "ECMA-48" reset-state "multiple" set-state "single" description "Tabulation Stop Mode" names {TSM}} } 19 { - {origin "???" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} + {origin "ECMA-48 deprecated" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} } 20 { - {origin "???" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { + {origin "ECMA-48 deprecated" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { For terminals that support LNM, the default is off meaning a lone CR respresents the character emitted when enter is pushed. Turning LNM on would mean that diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/console-0.1.1.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/console-0.1.1.tm index 21101c73..ff5c2904 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/console-0.1.1.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/console-0.1.1.tm @@ -1140,7 +1140,12 @@ namespace eval punk::console { return } #tailcall punk::ansi::a {*}$args - ::punk::ansi::a {*}$args + variable is_vt52 + if {$is_vt52} { + return + } else { + ::punk::ansi::a {*}$args + } } lappend PUNKARGS_aliases {::punk::console::code_a? ::punk::ansi::a?} proc code_a? {args} { @@ -1537,11 +1542,11 @@ namespace eval punk::console { set cell_size "" set cell_size_fallback 10x20 - #todo - change -inoutchannels to -terminalobject with prebuilt default + #todo - change -console to -terminalobject with prebuilt default punk::args::define { @id -id ::punk::console::cell_size - -inoutchannels -default {stdin stdout} -type list + -console -default {stdin stdout} -type list @values -min 0 -max 1 newsize -default "" -help\ "character cell pixel dimensions WxH @@ -1549,7 +1554,7 @@ namespace eval punk::console { } proc cell_size {args} { set argd [punk::args::parse $args -cache 1 withid ::punk::console::cell_size] - set inoutchannels [dict get $argd opts -inoutchannels] + set terminal [dict get $argd opts -console] set newsize [dict get $argd values newsize] variable cell_size @@ -1557,7 +1562,7 @@ namespace eval punk::console { #query existing setting if {$cell_size eq ""} { #not set - try to query terminal's overall dimensions - set pixeldict [punk::console::get_xterm_pixels $inoutchannels] + set pixeldict [punk::console::get_xterm_pixels $terminal] lassign $pixeldict _w sw _h sh if {[string is integer -strict $sw] && [string is integer -strict $sh]} { lassign [punk::console::get_size] _cols columns _rows rows @@ -1651,8 +1656,8 @@ namespace eval punk::console { set func_con "punk::ansi::cursor_on" } else { set movefunc "punk::ansi::vt52move" - set func_coff "punk::ansi::cursor_off_vt52" - set func_con "punk::ansi::cursor_on_vt52" + 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. @@ -1763,10 +1768,16 @@ namespace eval punk::console { 4 - mode is permanently unset The response is automatically retrieved from the terminal - and so should not be displayed unless there is such a + and so should not be displayed unless there is such a significant delay that the request times out. The value of is returned by the dec_get_mode function. + + The delay in retrieving a terminal response can range from + a few ms to 10s of ms depending on the terminal or other + runtime conditions. + In some cases it may be beneficial to call dec_has_mode first, + (which is cached) to reduce the number of queries to the terminal. } @opts -console -type list -minsize 2 -default {stdin stdout} @@ -1910,10 +1921,24 @@ namespace eval punk::console { the sequence: ESC [ ? $ p Where is an integer identifier. + + Will return zero if the mode is unsupported, 1|2|3|4 if supported. + + The result is cached. + Cached values of 1 or 2 may not represent current state. + ${$B}dec_get_mode${$N} or ${$B}dec_has_mode -refresh${$N} should be + used if up-to-date current state of a supported mode is required. } @opts -console -type list -minsize 2 -default {stdin stdout} - -refresh -type none + -refresh -type none -help\ + "Force a re-test of the mode." + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } @values -min 1 -max 1 mode -type {int|string} -help\ "integer for DEC mode, or name as in the dict: @@ -1925,11 +1950,8 @@ namespace eval punk::console { lassign [dict values $argd] leaders opts values received set console [dict get $opts -console] set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 - } else { - set do_refresh 0 - } + set do_refresh [dict exists $received -refresh] + set return [dict get $opts -return] if {[string is integer -strict $num_or_name]} { set m $num_or_name @@ -1952,10 +1974,23 @@ namespace eval punk::console { set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[\?%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[?$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set dec_has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + #we can use the payload result as the response as non-zero responses evaluate to true + set has_mode $payload + if {$has_mode ne ""} { + dict set dec_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { set has_mode [dict get $dec_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } @@ -1971,64 +2006,148 @@ namespace eval punk::console { -console -type list -minsize 2 -default {stdin stdout} -test -type none -help\ "Test current value/support for each mode" - @values -min 0 -max 0 + -supported -type none -help\ + "Limit results to supported DEC modes" + @values -min 0 -max -1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] proc dec_modes {args} { set argd [punk::args::parse $args withid ::punk::console::dec_modes] lassign [dict values $argd] leaders opts values received set terminal [dict get $opts -console] - if {[dict exists $received -test]} { - set do_test 1 + set do_test [dict exists $received -test] + set only_supported [dict exists $received -supported] + if {[dict exists $values match]} { + set matches [dict get $values match] } else { - set do_test 0 + set matches {} } upvar ::punk::ansi::decmode_data decmode_data + upvar ::punk::ansi::decmode_names decmode_names set t [textblock::class::table new "Dec Modes"] $t configure -show_header 1 -show_hseps 1 $t add_column -headers Code $t add_column -headers Names $t add_column -headers Origin $t add_column -headers Description + $t add_column -headers set-state + $t add_column -headers reset-state if {$do_test} { $t add_column -headers Status } - dict for {code items} $decmode_data { + set names [dict keys $decmode_names] + if {[llength $matches] == 0} { + #show all entries + set codes [dict keys $decmode_data] + } else { + set codes [list] + foreach m $matches { + if {[punk::lib::is_indexset $m]} { + set defaultmax 10000 ;#some codes are very high e.g foot IME 737769 - but we don't want to default test/scan for hours + #todo set max to actual largest value from indexset + lappend codes {*}[punk::lib::indexset_resolve -base 1 $defaultmax $m] + } else { + foreach nm $names { + if {[string match -nocase $m $nm]} { + set code [dict get $decmode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $decmode_names $nm] + } + } + } + } + } + } + #dict for {code items} $decmode_data {} + foreach code $codes { + if {[dict exists $decmode_data $code]} { + set items [dict get $decmode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } set colour "" + set set_state_colour "" + set reset_state_colour "" set RST "" if {$do_test} { - set testresult [dec_get_mode $code] + #dec_has_mode can be cached - in which case only 0|3|4 can be relied upon without re-querying + set hasmode_dict [dec_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [dec_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + switch -- $testresult { 0 { set colour [punk::ansi::a+ red bold] } 1 { set colour [punk::ansi::a+ green] + set set_state_colour $colour } 2 { set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour } 3 { set colour [punk::ansi::a+ green] + set set_state_colour $colour } 4 { set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour } default { - #unexpected + #failedquery + set colour [punk::ansi::a+ red bold] } } if {$colour ne ""} { set RST "\x1b\[m" } set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #dec_has_mode still queries terminal - but is cached if a response was received + if {[dec_has_mode -console $terminal $code] == 0} { + continue + } + } } foreach itm $items { set code $colour$code$RST set names $colour[dict get $itm names]$RST set origin [dict get $itm origin] set desc [dict get $itm description] - set row [list $code [join $names \n] $origin $desc] + set set_state $set_state_colour[dict get $itm set-state]$RST + set reset_state $reset_state_colour[dict get $itm reset-state]$RST + set row [list $code [join $names \n] $origin $desc $set_state $reset_state] if {$do_test} { lappend row $testdisplay } @@ -2059,6 +2178,12 @@ namespace eval punk::console { #-console -type list -typesynopsis {{${$I}inputchan${$NI} ${$I}outputchan${$NI}}} -minsize 2 -default {stdin stdout} -console -type list -minsize 2 -default {stdin stdout} -refresh -type none + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } @values -min 1 -max 1 mode -type {int|string} -help\ "integer for ANSI mode, or name as in the dict: @@ -2070,11 +2195,8 @@ namespace eval punk::console { lassign [dict values $argd] leaders opts values received set console [dict get $opts -console] set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 - } else { - set do_refresh 0 - } + set return [dict get $opts -return] + set do_refresh [dict exists $received -refresh] if {[string is integer -strict $num_or_name]} { set m $num_or_name @@ -2097,10 +2219,22 @@ namespace eval punk::console { set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set ansi_has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + set has_mode $payload + if {$has_mode ne ""} { + dict set ansi_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { set has_mode [dict get $ansi_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } @@ -2272,23 +2406,23 @@ namespace eval punk::console { -console -type list -minsize 2 -default {stdin stdout} -test -type none -help\ "Test current value/support for each mode" + -supported -type none -help\ + "Limit results to supported ANSI modes" @values -min 0 -max -1 - match -type indexset|string -multiple 1 -optional 1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] proc ansi_modes {args} { set argd [punk::args::parse $args withid ::punk::console::ansi_modes] lassign [dict values $argd] leaders opts values received set terminal [dict get $opts -console] - if {[dict exists $received -test]} { - set do_test 1 - } else { - set do_test 0 - } + set do_test [dict exists $received -test] if {[dict exists $values match]} { set matches [dict get $values match] } else { set matches {} } + set only_supported [dict exists $received -supported] upvar ::punk::ansi::ansimode_data ansimode_data upvar ::punk::ansi::ansimode_names ansimode_names @@ -2312,11 +2446,42 @@ namespace eval punk::console { set codes [list] foreach m $matches { if {[punk::lib::is_indexset $m]} { - lappend codes {*}[punk::lib::indexset_resolve -base 1 [dict size $ansimode_data] $m] + set iparts [split $m ,] + #determine the largest index requested - which may be greater than the defined range if explicitly specified + #(allow scanning for unusual ansi modes above 22 - *some* known to exist - private modes should use DECSET DECRESET instead) + #above 22 values seem to be mainly from SCOANSI and Wyse terminals + + set maxint [dict size $ansimode_data] + foreach i $iparts { + if {[string is integer -strict $i]} { + if {$i > $maxint} { + set maxint $i + } + } else { + if {![string match *end* $i]} { + lassign [split [string map [list .. \uFFEF] $i] \uFFEF] a b + if {$a > $b} { + if {$a > $maxint} { + set maxint $a + } + } else { + if {$b > $maxint} { + set maxint $b + } + } + } + } + } + puts "---> maxint: $maxint" + lappend codes {*}[punk::lib::indexset_resolve -base 1 $maxint $m] } else { foreach nm $names { if {[string match -nocase $m $nm]} { - lappend codes [dict get $ansimode_names $nm] + set code [dict get $ansimode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $ansimode_names $nm] + } } } } @@ -2325,13 +2490,46 @@ namespace eval punk::console { #dict for {code items} $ansimode_data {} foreach code $codes { - set items [dict get $ansimode_data $code] + if {[dict exists $ansimode_data $code]} { + set items [dict get $ansimode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } set colour "" set set_state_colour "" set reset_state_colour "" set RST "" if {$do_test} { - set testresult [ansi_get_mode $code] + set hasmode_dict [ansi_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [ansi_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + + #set testresult [ansi_get_mode $code] switch -- $testresult { 0 { set colour [punk::ansi::a+ red bold] @@ -2360,6 +2558,13 @@ namespace eval punk::console { set RST "\x1b\[m" } set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #ansi_has_mode still queries terminal - but is cached if a response was received + if {[ansi_has_mode -console $terminal $code] == 0} { + continue + } + } } foreach itm $items { set code $colour$code$RST @@ -2381,35 +2586,47 @@ namespace eval punk::console { return $out } + #see https://vt100.net/dec/ek-vt520-rm.pdf set DECRQSS_DATA { + "Assign Color" DECAC ",|" + "Alternate Text Color" DECATC ",\}" + "CRT Saver Timing" DECCRTST "-q" + "Down Line Load Allocation" DECDLDA ",z" + "Energy Save Timing" DECSEST "-r" + "Select Auto Repeat Rate" DECARR "-p" "Select Active Status Display" DECSASD "$\}" "Select Attribute Change Extent" DECSACE "*x" "Set Character Attribute" DECSCA "\"q" - "Set Conformance Level" DECSCL "\"p" + "Select Communication Port" DECSCP "*u" "Set Columns Per Page" DECSCPP "\$|" + "Set Conformance Level" DECSCL "\"p" + "Select Communication Speed" DECSCS "*r" + "Set Cursor Style" DECSCUSR " q" + "Select Disconnect Delay Time" DECSDDT "\$q" + "Select Digital Printed Data Type" DECSDPT "(p" + "Select Flow Control Type" DECSFC "*s" + "Set Key Click Volume" DECSKCV " r" + "Set Lock Key Style" DECSLCK " v" + "Set Left and Right Margins" DECSLRM "s" "Set Lines Per Page" DECSLPP "t" + "Set Margin Bell Volume" DECSMBV " u" "Set Number of Lines per Screen" DECSNLS "*|" - "Set Status Line Type" DECSSDT "$~" - "Set Left and Right Margins" DECSLRM "s" - "Set Top and Bottom Margins" DECSTBM "r" - "Set Graphic Rendition" SGR "m" - "Select Set-Up Language" DECSSL "p" + "Session Page Memory Allocation" DECSPMA ",x" + "Set Port Parameter" DECSPP "\+w" + "Select ProPrinter Character Set" DECSPPCS "*p" "Select Printer Type" DECSPRTT "\$s" "Select Refresh Rate" DECSRFR "\"t" - "Select Digital Printed Data Type" DECSDPT "(p" - "Select ProPrinter Character Set" DECSPPCS "*p" - "Select Communication Speed" DECSCS "*r" - "Select Communication Port" DECSCP "*u" "Set Scroll Speed" DECSSCLS " p" - "Set Cursor Style" DECSCUSR " q" - "Set Key Click Volume" DECSKCV " r" - "Set Warning Bell Volume" DECSWBV " t" - "Set Margin Bell Volume" DECSMBV " u" - "Set Lock Key Style" DECSLCK " v" - "Select Flow Control Type" DECSFC "*s" - "Select Disconnect Delay Time" DECSDDT "\$q" + "Set Status Line Type" DECSSDT "$~" + "Select Set-Up Language" DECSSL "p" + "Set Top and Bottom Margins" DECSTBM "r" + "Select Color Lookup Table" DECSTGLT ")\{" "Set Transmit Rate Limit" DECSTRL "u" - "Set Port Parameter" DECSPP "\+w" + "Set Warning Bell Volume" DECSWBV " t" + "Select Zero Symbol" DECSZS ",\{" + "Terminal Mode Emulation" DECTME " ~" + "Set Graphic Rendition" SGR "m" + "invalid" invalid ".." } set DECRQSS_DICT [dict create] foreach {desc name str} $DECRQSS_DATA { @@ -2461,12 +2678,12 @@ namespace eval punk::console { set c1 [string index $payload 0] switch -- $c1 { 0 { - error "dec_request_setting - terminal doesn't recognise request '[punk::ansi::ansistring VIEW $request]' as valid" + error "dec_request_setting - terminal doesn't support querying settings for $name '[punk::ansi::ansistring VIEW $request]'" } 1 {} default { - #shouldn't get here - error "dec_request_setting - unrecognised response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" + #probable timeout - terminal doesn't recognise/respond + error "dec_request_setting - unrecognised or missing response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" } } #strip leading 1$r @@ -2772,7 +2989,12 @@ namespace eval punk::console { puts -nonewline stdout [punk::ansi::clear_above] } proc clear_below {} { - puts -nonewline stdout [punk::ansi::clear_below] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::clear_below] + } else { + puts -nonewline stdout [punk::ansi::vt52clear_below] + } } proc clear_all {} { puts -nonewline stdout [punk::ansi::clear_all] @@ -2785,7 +3007,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_on] } else { - puts -nonewline stdout [punk::ansi::cursor_on_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_on] } } proc cursor_off {} { @@ -2793,7 +3015,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_off] } else { - puts -nonewline stdout [punk::ansi::cursor_off_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_off] } } @@ -2858,10 +3080,20 @@ namespace eval punk::console { } } proc move_up {n} { - puts -nonewline stdout [punk::ansi::move_up $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_up $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_up $n] + } } proc move_down {n} { - puts -nonewline stdout [punk::ansi::move_down $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_down $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_down $n] + } } proc move_column {col} { upvar ::punk::console::is_vt52 is_vt52 @@ -2925,12 +3157,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] append commands [punk::ansi::vt52move_emit $row $col $data] foreach {row col data} $args { append commands [punk::ansi::vt52move_emit $row $col $data] } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands; flush stdout } @@ -2945,12 +3177,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] foreach ln [split $textblock \n] { append commands [punk::ansi::vt52move_emit $row $col $ln] incr row } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands;flush stdout return diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm index 5a351c2b..0272500d 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm @@ -1529,7 +1529,7 @@ proc repl::repl_handler {inputchan prompt_config} { #puts -nonewline stdout [punk::ansi::move $rows 4]$msg #use cursorsave_ version which avoids get_cursor_pos_list call set msglen [ansistring length $msg] - punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg + punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg ;#supports also vt52 } else { #no mechanism to get console dimensions #we are reduced to continuously spewing lines. diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/overtype-1.7.4.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/overtype-1.7.4.tm index d3a642da..d1d0dd44 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/overtype-1.7.4.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/overtype-1.7.4.tm @@ -417,6 +417,7 @@ tcl::namespace::eval overtype { set overblock [tcl::string::map {\r\n \n} $overblock] if {$opt_startrow > 1} { set down [expr {$opt_startrow -1}] + #when vt52? set overblock [punk::ansi::move_down $down]$overblock } @@ -4549,7 +4550,7 @@ tcl::namespace::eval overtype { #P3 horizontal grid size - ignored on VT300 - commonly set to zero - # ECMA-48 SSU (ESC Ps I) + # ECMA-48 SSU (ESC [ Ps I) # 0 - CHARACTER # 1 - MILLIMETRE # 2 - COMPUTER DECIPOINT 0.03528mm 1/720 of 25.4mm) diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm index 821af950..7bf4bf7c 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/ansi-0.1.1.tm @@ -4670,6 +4670,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun clear_below]] return \033\[0J } + proc vt52clear_below {} { + return \033J + } proc clear_all {} { # - doesn't work?? @@ -4688,10 +4691,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun cursor_off]] return "\033\[?25l" } - proc cursor_on_vt52 {} { + proc vt52cursor_on {} { return \x1be } - proc cursor_off_vt52 {} { + proc vt52cursor_off {} { return \x1bf } @@ -4781,7 +4784,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu Sequence is of the form: ESCY This sequence will generally not be understood by terminals - that are not in vt52 mode (e.g DECANM unset). + that are not in vt52 mode (i.e DECANM unset). } @values -min 2 -max 2 row -type integer -help\ @@ -4959,10 +4962,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[para] DECRC return \x1b8 } - proc cursor_save_vt52 {} { + proc vt52cursor_save {} { return \x1bj } - proc cursor_restore_vt52 {} { + proc vt52cursor_restore {} { return \x1bk } @@ -5021,29 +5024,19 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # \x1b\[?7\;2\$y #where 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset) + + #names for other alt_screen mechanismk: 1047,1048 vs 1049? + #https://wiki.tau.garden/dec-modes/ #(DEC,xterm,contour,mintty,kitty etc) #https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking - #names for other alt_screen mechanismk: 1047,1048 vs 1049? - #variable decmode_names [dict create\ - # DECANM 2\ - # origin 6\ - # DECCOLM 3\ - # line_wrap 7\ - # alt_screen 1049\ - # grapheme_clusters 2027\ - # bracketed_paste 2004\ - # mouse_sgr 1006\ - # mouse_urxvt 1015\ - # mouse_sgr_pixel 1016\ - #] - # # some more ansi mode/sequence info: #https://pkg.go.dev/github.com/charmbracelet/x/ansi #see also: https://ucs-detect.readthedocs.io/results.html#dec-private-modes-support + #REVIEW - these modes are sometimes used for different things on different terminals #For now we are assigning based on common usage in things like xterm & windows terminal #Proper handling would require tables for various terminals - review. @@ -5052,140 +5045,279 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #unset with DECRST ESC [ ? l variable decmode_data { 1 { - {origin DEC description "DECCKM - Cursor Keys Mode" names {DECCKM cursor_keys}} + {origin DEC reset-state "cursor sequences" set-state "application sequences" description "DECCKM - Arrow Keys Cursor Keys Mode" names {DECCKM cursor_keys}} } 2 { - {origin DEC description "DECANM - ANSI/VT52 Mode" names {DECANM} note { + {origin DEC reset-state "VT52" set-state "ANSI" description "DECANM - ANSI/VT52 Mode" names {DECANM} note { Disable to turn on VT52 emulation. In VT52 mode - use \x1b< to exit. } } } 3 { - {origin DEC description "DECCOLM - Column" names {DECCOLM}} + {origin DEC reset-state "80-column font" set-state "132-column font" + description "DECCOLM - Select 80 or 132 Columns per page +(obs - use DECSCPP)" + names {DECCOLM} + see "https://vt100.net/docs/vt510-rm/DECCOLM.html"} } 4 { - {origin DEC description "DECSCLM - Scrolling" names {DECSCLM}} + {origin DEC reset-state "jump scroll" set-state "smooth scroll" description "DECSCLM - Scrolling Mode" names {DECSCLM}} } 5 { - {origin DEC description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} + {origin DEC reset-state "dark mode" set-state "light mode" description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} } 6 { - {origin DEC description "DECOM - Origin Mode (whether cursor is restricted to within page margins)" names {DECOM}} + {origin DEC reset-state "upper-left corner" set-state "within margins" + description "DECOM - Origin Mode +(whether cursor is restricted to within page margins)" + names {DECOM}} } 7 { - {origin DEC description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} + {origin DEC reset-state "no autowrap" set-state "autowrap" description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} } 8 { - {origin DEC description "DECARM - Auto Repeat Mode" names {DECARM autorepeat}} + {origin DEC reset-state "disabled" set-state "0.5s keypress autorepeat" + description "DECARM - Auto Repeat Mode" + see "https://vt100.net/docs/vt510-rm/DECARM.html" + names {DECARM autorepeat}} } 9 { - {origin "xterm" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { + {origin "xterm" reset-state "disable" set-state "enable" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { Escape sequence on button press only. CSI M CbCxCy (6 chars) Coords limited to 223 (=255 - 32) } } - {origin DEC description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + {origin DEC reset-state "" set-state "" description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + } + 10 { + {origin DEC reset-state "" set-state "" description "DECEDM - Editing Mode" names {DECEDM}} + {origin rxvt reset-state "" set-state "" description "show toolbar" names {RXVTSHOWTOOLBAR}} + } + 11 { + {origin DEC reset-state "" set-state "" description "DECLTM - Line Transmit Mode" names {DECLTM}} } 12 { - {origin xterm description "Cursor blink" names {XTCBLINK cursorblink}} - {origin DEC description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} + {origin xterm reset-state "steady" set-state "blink" description "Cursor blink" names {XTCBLINK cursorblink}} + {origin DEC reset-state "" set-state "" description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} } 25 { - {origin DEC description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} + {origin DEC reset-state "invisible" set-state "visible" description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} } 40 { - {origin DEC description "New Line Mode" names {DECCRNLM newline_mode}} - {origin xterm description "Allow 80->132 mode" names {xt80-132}} + {origin DEC reset-state "" set-state "" description "New Line Mode" names {DECCRNLM newline_mode}} + {origin xterm reset-state "" set-state "" description "Allow 80->132 mode" names {xt80-132}} } 45 { - {origin DEC description "Graphics Print Color Syntax" names {DECGPCS}} - {origin xterm description "Reverse Wraparound Mode" names {reverse_wraparound}} + {origin DEC reset-state "" set-state "" description "Graphics Print Color Syntax" names {DECGPCS}} + {origin xterm reset-state "" set-state "" description "Reverse Wraparound Mode" names {reverse_wraparound}} } 47 { - {origin xterm description "xterm alternate buffer" names {xterm_altbuf}} - {origin DEC description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} + {origin xterm reset-state "" set-state "" description "xterm alternate buffer" names {xterm_altbuf}} + {origin DEC reset-state "" set-state "" description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} } 64 { - {origin DEC description "DECPCCM - Page Cursor Coupling Mode" names {DECPCCM}} + {origin DEC reset-state "uncoupled" set-state "coupled" description "DECPCCM - Page Cursor Coupling Mode" + see "https://vt100.net/docs/vt510-rm/DECPCCM.html" + names {DECPCCM}} } 66 { - {origin DEC description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} + {origin DEC reset-state "keypad chars" set-state "app sequences" description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} } 67 { - {origin DEC description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} + {origin DEC reset-state "delete key" set-state "backspace key" description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} } 69 { - {origin DEC description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} + {origin DEC reset-state "cannot set margins" set-state "can set margins" description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} } 80 { - {origin ??? description "Sixel Display Mode" see "" names {sixel_display}} + {origin DEC reset-state "scrolling enabled" set-state "scrolling disabled" description "Sixel Display Mode +(VT382 disable on set) +(VT330 manual error?) +some emulators may reverse" + see "https://github.com/dankamongmen/notcurses/issues/1782" + names {sixel_display DECSDM}} + } + 95 { + {origin DEC reset-state "clear screen on column change" set-state "no clear on column change" + description "No Clearing Screen on Column Change Mode" see "" + names {DECNCSM}} + } + 98 { + {origin DEC reset-state "disabled" set-state "enabled" description "Auto Resize Mode" see "" names {DECARSM}} + } + 115 { + {origin DEC reset-state "disabled" set-state "enabled" description "Alternate Text Color Blink Mode" see "" names {DECATCBM}} } 117 { - {origin ??? description "Erase Color Mode" see "" names {erase_color}} + {origin DEC reset-state "" set-state "" description "Erase Color Mode" see "" names {DECECM erase_color}} } 1000 { - {origin "xterm" description "VT200 compatibility mouse" names {SET_VT200_MOUSE} note { + {origin "xterm" reset-state "" set-state "" description "VT200 compatibility mouse" + names {MOUSE_REPORT_CLICK SET_VT200_MOUSE} + note { Escape sequence on both button press and release. CSI M CbCxCy } } } 1001 { - {origin "???" description "Use Hilite Mouse Tracking" names {mouse_tracking_hilite}} + {origin "xterm" reset-state "" set-state "" description "Use Hilite Mouse Tracking" + names {MOUSE_HILITE_TRACKING mouse_tracking_hilite}} } 1002 { - {origin "???" description "Use Cell Motion Mouse Tracking" names {mouse_tracking_cellmotion}} + {origin "xterm" reset-state "" set-state "" description "Use Cell Motion Mouse Tracking" + names {MOUSE_REPORT_DRAG mouse_tracking_cellmotion}} } 1003 { - {origin "???" description "Use All Motion Mouse Tracking" names {mouse_tracking_allmotion}} + {origin "xterm" reset-state "" set-state "" description "Use All Motion Mouse Tracking" + names {MOUSE_ALLMOTION mouse_tracking_allmotion}} } 1004 { - {origin "xterm" description "Send FocusIn/FocusOut events" names {mouse_focus_event}} + {origin "xterm" reset-state "" set-state "" description "Send FocusIn/FocusOut events" + names {FOCUS_IN_OUT_EVENTS mouse_focus_event}} } 1005 { - {origin "xterm" description "Enable UTF-8 Mouse Mode" names {mouse_utf8 mouse_utf8_extended}} + {origin "xterm" reset-state "" set-state "" + description "Enable UTF-8 Mouse Mode" + names {MOUSE_EXTENDED_UTF8 mouse_utf8 mouse_utf8_extended}} } 1006 { - {origin "xterm" description "Enable SGR Mouse Mode" names {mouse_sgr mouse_sgr_extended} note{ + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Mouse Mode" names + {MOUSE_EXTENDED_SGR mouse_sgr mouse_sgr_extended} + note{ SET_SGR_EXT_MODE_MOUSE - extended compared to x10 mouse protocol which limits x y coords to 223 (=255 - 32) } } } 1007 { - {origin "???" description "Enable Alternate Scroll Mode" names {alternate_scroll}} + {origin "xterm" reset-state "" set-state "" + description "Enable Alternate Scroll Mode" + names {ALT_SCROLL_XTERM alternate_scroll}} } 1015 { - {origin "urxvt" description "Enable urxvt Mouse Mode" names {mouse_urxvt}} + {origin "urxvt" reset-state "" set-state "" + description "Enable urxvt Mouse Mode" + names {MOUSE_URXVT}} } 1016 { - {origin "xterm" description "Enable SGR Pixel Mouse Mode" names {mouse_sgr_pixel}} + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Pixel Mouse Mode" + names {MOUSE_SGR_PIXELS}} + } + 1042 { + {origin "xterm" reset-state "" set-state "" + description "Enable Urgency window manager hint +when Control-G is received" + names {URGENCY_ON_CTRL_G xt1042}} + } + 1043 { + {origin "xterm" reset-state "" set-state "" + description "Enable raising of the window +when Control-G is received" + names {RAISE_ON_CTRL_G xt1043}} } 1047 { - {origin "xterm" description "Alternate Buffer" names {alt_buffer_only}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer" + names {ALT_SCREEN_BUFFER_XTERM alt_buffer_only}} } 1049 { - {origin "xterm" description "Alternate Buffer with save cursor" names {alt_buffer alt_screen}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer with save cursor" + names {ALT_SCREEN_AND_SAVE_CLEAR alt_buffer alt_screen}} + } + 1070 { + {origin "xterm" reset-state "" set-state "" + description "Use private color registers for each graphic" + names {SIXEL_PRIVATE_PALETTE}} } 2004 { - {origin "xterm" description "Set bracketed paste mode" names {bracketed_paste}} + {origin "xterm" reset-state "" set-state "" + description "Set bracketed paste mode" + names {BRACKETED_PASTE}} + } + 2026 { + {origin iTerm2 reset-state "screen updates" set-state "no screen updates" + description "Synchronized Output" + see "https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036" + names {SYNCHRONIZED_OUTPUT synch}} } 2027 { - {origin Contour description "Grapheme Cluster Processing" names {grapheme_clusters}} + {origin Contour reset-state "" set-state "" + description "Grapheme Cluster Processing" + names {GRAPHEME_CLUSTERING grapheme_clusters}} + } + 2028 { + {origin Contour reset-state "" set-state "" + description "Text reflow" + names {TEXT_REFLOW}} } 2048 { - {origin "rockorager" description "Enable in-band window resize notifications (modern VTEEWR analog)" see "https://rockorager.dev/misc/in-band-resize-notifications/" names {window_resize_reports}} + {origin "@rockorager" reset-state "" set-state "" + description "Enable in-band window resize notifications +(modern VTEEWR analog)" + names {IN_BAND_WINDOW_RESIZE window_resize_reports} + see "https://rockorager.dev/misc/in-band-resize-notifications/" + } } 5522 { - {origin "kitty rockorager" description "Automatic Paste notifications" see "https://rockorager.dev/misc/bracketed-paste-mime/" names {automatic_paste_reports}} + {origin "kitty @rockorager" + reset-state "" set-state "" + description "Automatic Paste notifications" + names {automatic_paste_reports} + see "https://rockorager.dev/misc/bracketed-paste-mime/"} + } + 7700 { + {origin mintty reset-state "disable reporting" set-state "enable reporting" + description "Ambiguous width reporting" + notes "When enabled, mintty sends ^[[1W for an 'ambiguous narrow' font +and ^[[2W for an 'ambiguous wide' font allowing the application +to adjust its layout accordingly." + names {AMBIGUOUS_WIDTH_REPORTING}} + } + 7766 { + {origin mintty reset-state "hide" set-state "show" + description "Show/hide scrollbar" + names {SHOW_HIDE_SCROLLBAR mintty_scrollbar}} + } + 8200 { + {origin TeraTerm reset-state "Don't move cursor" set-state "Cursor to home" + description "Move cursor when erase complete display (ED 2)" + names {TTCTH}} } 8452 { - {origin "rlogin xterm" description "Post sixel cursor position" names {RLSIXPOS}} + {origin "rlogin xterm" + reset-state "" set-state "" + description "Post sixel cursor position" + names {SIXEL_SCROLLING_LEAVES_CURSOR}} } 9001 { - {origin "windows" description "win32 input mode" names {win32-input}} + {origin "conpty" + reset-state "" set-state "" + description "win32 input mode" + names {WIN32_INPUT_MODE}} + } + 19997 { + {origin "kitty" + reset-state "" set-state "" + description "Handle Ctrl-C/Ctrl-Z mode" + names {KITTY_HANDLE_CTRL_C_Z kitty_ctrl_c_ctrl_z}} + } + 77096 { + {origin "mintty" + reset-state "" set-state "" + description "BiDi mode" + names {mintty_bidi}} + } + 737769 { + {origin "foot" + reset-state "" set-state "" + description "Input Method Editor (IME) mode" + names {foot_ime}} } } set decmode_names [dict create] @@ -5205,6 +5337,14 @@ to 223 (=255 - 32) #primary source for ansimode_data: # https://ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf + + #todo - different table for different terminal emulations + #modern terminal emulators generally don't support any modes above 22 + #but some such as SCOANSI, Wyse and S97801 go higher + #e.g + #Wyse wy370 uses CSI 33 h/l for steady vs blinking cursor + #SCOANSI uses 1048 for cursor on + variable ansimode_data { 1 { {origin "ECMA-48" reset-state "replacing" set-state "cumulative" description "Guarded Area Transfer Mode" names {GATM}} @@ -5228,10 +5368,10 @@ to 223 (=255 - 32) {origin "ECMA-48" reset-state "following" set-state "preceding" description "Line Editing Mode (Vertical Editing Mode)" names {VEM}} } 8 { - {origin "???" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} + {origin "ECMA-48" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} } 9 { - {origin "???" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} + {origin "ECMA-48" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} } 10 { {origin "ECMA-48" reset-state "following" set-state "preceding" description "Character Editing Mode (Horizontal Editing Mode)" names {HEM}} @@ -5271,10 +5411,10 @@ note - No control functions are affected. {origin "ECMA-48" reset-state "multiple" set-state "single" description "Tabulation Stop Mode" names {TSM}} } 19 { - {origin "???" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} + {origin "ECMA-48 deprecated" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} } 20 { - {origin "???" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { + {origin "ECMA-48 deprecated" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { For terminals that support LNM, the default is off meaning a lone CR respresents the character emitted when enter is pushed. Turning LNM on would mean that diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/console-0.1.1.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/console-0.1.1.tm index 21101c73..ff5c2904 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/console-0.1.1.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/console-0.1.1.tm @@ -1140,7 +1140,12 @@ namespace eval punk::console { return } #tailcall punk::ansi::a {*}$args - ::punk::ansi::a {*}$args + variable is_vt52 + if {$is_vt52} { + return + } else { + ::punk::ansi::a {*}$args + } } lappend PUNKARGS_aliases {::punk::console::code_a? ::punk::ansi::a?} proc code_a? {args} { @@ -1537,11 +1542,11 @@ namespace eval punk::console { set cell_size "" set cell_size_fallback 10x20 - #todo - change -inoutchannels to -terminalobject with prebuilt default + #todo - change -console to -terminalobject with prebuilt default punk::args::define { @id -id ::punk::console::cell_size - -inoutchannels -default {stdin stdout} -type list + -console -default {stdin stdout} -type list @values -min 0 -max 1 newsize -default "" -help\ "character cell pixel dimensions WxH @@ -1549,7 +1554,7 @@ namespace eval punk::console { } proc cell_size {args} { set argd [punk::args::parse $args -cache 1 withid ::punk::console::cell_size] - set inoutchannels [dict get $argd opts -inoutchannels] + set terminal [dict get $argd opts -console] set newsize [dict get $argd values newsize] variable cell_size @@ -1557,7 +1562,7 @@ namespace eval punk::console { #query existing setting if {$cell_size eq ""} { #not set - try to query terminal's overall dimensions - set pixeldict [punk::console::get_xterm_pixels $inoutchannels] + set pixeldict [punk::console::get_xterm_pixels $terminal] lassign $pixeldict _w sw _h sh if {[string is integer -strict $sw] && [string is integer -strict $sh]} { lassign [punk::console::get_size] _cols columns _rows rows @@ -1651,8 +1656,8 @@ namespace eval punk::console { set func_con "punk::ansi::cursor_on" } else { set movefunc "punk::ansi::vt52move" - set func_coff "punk::ansi::cursor_off_vt52" - set func_con "punk::ansi::cursor_on_vt52" + 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. @@ -1763,10 +1768,16 @@ namespace eval punk::console { 4 - mode is permanently unset The response is automatically retrieved from the terminal - and so should not be displayed unless there is such a + and so should not be displayed unless there is such a significant delay that the request times out. The value of is returned by the dec_get_mode function. + + The delay in retrieving a terminal response can range from + a few ms to 10s of ms depending on the terminal or other + runtime conditions. + In some cases it may be beneficial to call dec_has_mode first, + (which is cached) to reduce the number of queries to the terminal. } @opts -console -type list -minsize 2 -default {stdin stdout} @@ -1910,10 +1921,24 @@ namespace eval punk::console { the sequence: ESC [ ? $ p Where is an integer identifier. + + Will return zero if the mode is unsupported, 1|2|3|4 if supported. + + The result is cached. + Cached values of 1 or 2 may not represent current state. + ${$B}dec_get_mode${$N} or ${$B}dec_has_mode -refresh${$N} should be + used if up-to-date current state of a supported mode is required. } @opts -console -type list -minsize 2 -default {stdin stdout} - -refresh -type none + -refresh -type none -help\ + "Force a re-test of the mode." + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } @values -min 1 -max 1 mode -type {int|string} -help\ "integer for DEC mode, or name as in the dict: @@ -1925,11 +1950,8 @@ namespace eval punk::console { lassign [dict values $argd] leaders opts values received set console [dict get $opts -console] set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 - } else { - set do_refresh 0 - } + set do_refresh [dict exists $received -refresh] + set return [dict get $opts -return] if {[string is integer -strict $num_or_name]} { set m $num_or_name @@ -1952,10 +1974,23 @@ namespace eval punk::console { set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[\?%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[?$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set dec_has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + #we can use the payload result as the response as non-zero responses evaluate to true + set has_mode $payload + if {$has_mode ne ""} { + dict set dec_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { set has_mode [dict get $dec_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } @@ -1971,64 +2006,148 @@ namespace eval punk::console { -console -type list -minsize 2 -default {stdin stdout} -test -type none -help\ "Test current value/support for each mode" - @values -min 0 -max 0 + -supported -type none -help\ + "Limit results to supported DEC modes" + @values -min 0 -max -1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] proc dec_modes {args} { set argd [punk::args::parse $args withid ::punk::console::dec_modes] lassign [dict values $argd] leaders opts values received set terminal [dict get $opts -console] - if {[dict exists $received -test]} { - set do_test 1 + set do_test [dict exists $received -test] + set only_supported [dict exists $received -supported] + if {[dict exists $values match]} { + set matches [dict get $values match] } else { - set do_test 0 + set matches {} } upvar ::punk::ansi::decmode_data decmode_data + upvar ::punk::ansi::decmode_names decmode_names set t [textblock::class::table new "Dec Modes"] $t configure -show_header 1 -show_hseps 1 $t add_column -headers Code $t add_column -headers Names $t add_column -headers Origin $t add_column -headers Description + $t add_column -headers set-state + $t add_column -headers reset-state if {$do_test} { $t add_column -headers Status } - dict for {code items} $decmode_data { + set names [dict keys $decmode_names] + if {[llength $matches] == 0} { + #show all entries + set codes [dict keys $decmode_data] + } else { + set codes [list] + foreach m $matches { + if {[punk::lib::is_indexset $m]} { + set defaultmax 10000 ;#some codes are very high e.g foot IME 737769 - but we don't want to default test/scan for hours + #todo set max to actual largest value from indexset + lappend codes {*}[punk::lib::indexset_resolve -base 1 $defaultmax $m] + } else { + foreach nm $names { + if {[string match -nocase $m $nm]} { + set code [dict get $decmode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $decmode_names $nm] + } + } + } + } + } + } + #dict for {code items} $decmode_data {} + foreach code $codes { + if {[dict exists $decmode_data $code]} { + set items [dict get $decmode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } set colour "" + set set_state_colour "" + set reset_state_colour "" set RST "" if {$do_test} { - set testresult [dec_get_mode $code] + #dec_has_mode can be cached - in which case only 0|3|4 can be relied upon without re-querying + set hasmode_dict [dec_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [dec_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + switch -- $testresult { 0 { set colour [punk::ansi::a+ red bold] } 1 { set colour [punk::ansi::a+ green] + set set_state_colour $colour } 2 { set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour } 3 { set colour [punk::ansi::a+ green] + set set_state_colour $colour } 4 { set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour } default { - #unexpected + #failedquery + set colour [punk::ansi::a+ red bold] } } if {$colour ne ""} { set RST "\x1b\[m" } set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #dec_has_mode still queries terminal - but is cached if a response was received + if {[dec_has_mode -console $terminal $code] == 0} { + continue + } + } } foreach itm $items { set code $colour$code$RST set names $colour[dict get $itm names]$RST set origin [dict get $itm origin] set desc [dict get $itm description] - set row [list $code [join $names \n] $origin $desc] + set set_state $set_state_colour[dict get $itm set-state]$RST + set reset_state $reset_state_colour[dict get $itm reset-state]$RST + set row [list $code [join $names \n] $origin $desc $set_state $reset_state] if {$do_test} { lappend row $testdisplay } @@ -2059,6 +2178,12 @@ namespace eval punk::console { #-console -type list -typesynopsis {{${$I}inputchan${$NI} ${$I}outputchan${$NI}}} -minsize 2 -default {stdin stdout} -console -type list -minsize 2 -default {stdin stdout} -refresh -type none + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } @values -min 1 -max 1 mode -type {int|string} -help\ "integer for ANSI mode, or name as in the dict: @@ -2070,11 +2195,8 @@ namespace eval punk::console { lassign [dict values $argd] leaders opts values received set console [dict get $opts -console] set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 - } else { - set do_refresh 0 - } + set return [dict get $opts -return] + set do_refresh [dict exists $received -refresh] if {[string is integer -strict $num_or_name]} { set m $num_or_name @@ -2097,10 +2219,22 @@ namespace eval punk::console { set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set ansi_has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + set has_mode $payload + if {$has_mode ne ""} { + dict set ansi_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { set has_mode [dict get $ansi_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } @@ -2272,23 +2406,23 @@ namespace eval punk::console { -console -type list -minsize 2 -default {stdin stdout} -test -type none -help\ "Test current value/support for each mode" + -supported -type none -help\ + "Limit results to supported ANSI modes" @values -min 0 -max -1 - match -type indexset|string -multiple 1 -optional 1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] proc ansi_modes {args} { set argd [punk::args::parse $args withid ::punk::console::ansi_modes] lassign [dict values $argd] leaders opts values received set terminal [dict get $opts -console] - if {[dict exists $received -test]} { - set do_test 1 - } else { - set do_test 0 - } + set do_test [dict exists $received -test] if {[dict exists $values match]} { set matches [dict get $values match] } else { set matches {} } + set only_supported [dict exists $received -supported] upvar ::punk::ansi::ansimode_data ansimode_data upvar ::punk::ansi::ansimode_names ansimode_names @@ -2312,11 +2446,42 @@ namespace eval punk::console { set codes [list] foreach m $matches { if {[punk::lib::is_indexset $m]} { - lappend codes {*}[punk::lib::indexset_resolve -base 1 [dict size $ansimode_data] $m] + set iparts [split $m ,] + #determine the largest index requested - which may be greater than the defined range if explicitly specified + #(allow scanning for unusual ansi modes above 22 - *some* known to exist - private modes should use DECSET DECRESET instead) + #above 22 values seem to be mainly from SCOANSI and Wyse terminals + + set maxint [dict size $ansimode_data] + foreach i $iparts { + if {[string is integer -strict $i]} { + if {$i > $maxint} { + set maxint $i + } + } else { + if {![string match *end* $i]} { + lassign [split [string map [list .. \uFFEF] $i] \uFFEF] a b + if {$a > $b} { + if {$a > $maxint} { + set maxint $a + } + } else { + if {$b > $maxint} { + set maxint $b + } + } + } + } + } + puts "---> maxint: $maxint" + lappend codes {*}[punk::lib::indexset_resolve -base 1 $maxint $m] } else { foreach nm $names { if {[string match -nocase $m $nm]} { - lappend codes [dict get $ansimode_names $nm] + set code [dict get $ansimode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $ansimode_names $nm] + } } } } @@ -2325,13 +2490,46 @@ namespace eval punk::console { #dict for {code items} $ansimode_data {} foreach code $codes { - set items [dict get $ansimode_data $code] + if {[dict exists $ansimode_data $code]} { + set items [dict get $ansimode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } set colour "" set set_state_colour "" set reset_state_colour "" set RST "" if {$do_test} { - set testresult [ansi_get_mode $code] + set hasmode_dict [ansi_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [ansi_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + + #set testresult [ansi_get_mode $code] switch -- $testresult { 0 { set colour [punk::ansi::a+ red bold] @@ -2360,6 +2558,13 @@ namespace eval punk::console { set RST "\x1b\[m" } set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #ansi_has_mode still queries terminal - but is cached if a response was received + if {[ansi_has_mode -console $terminal $code] == 0} { + continue + } + } } foreach itm $items { set code $colour$code$RST @@ -2381,35 +2586,47 @@ namespace eval punk::console { return $out } + #see https://vt100.net/dec/ek-vt520-rm.pdf set DECRQSS_DATA { + "Assign Color" DECAC ",|" + "Alternate Text Color" DECATC ",\}" + "CRT Saver Timing" DECCRTST "-q" + "Down Line Load Allocation" DECDLDA ",z" + "Energy Save Timing" DECSEST "-r" + "Select Auto Repeat Rate" DECARR "-p" "Select Active Status Display" DECSASD "$\}" "Select Attribute Change Extent" DECSACE "*x" "Set Character Attribute" DECSCA "\"q" - "Set Conformance Level" DECSCL "\"p" + "Select Communication Port" DECSCP "*u" "Set Columns Per Page" DECSCPP "\$|" + "Set Conformance Level" DECSCL "\"p" + "Select Communication Speed" DECSCS "*r" + "Set Cursor Style" DECSCUSR " q" + "Select Disconnect Delay Time" DECSDDT "\$q" + "Select Digital Printed Data Type" DECSDPT "(p" + "Select Flow Control Type" DECSFC "*s" + "Set Key Click Volume" DECSKCV " r" + "Set Lock Key Style" DECSLCK " v" + "Set Left and Right Margins" DECSLRM "s" "Set Lines Per Page" DECSLPP "t" + "Set Margin Bell Volume" DECSMBV " u" "Set Number of Lines per Screen" DECSNLS "*|" - "Set Status Line Type" DECSSDT "$~" - "Set Left and Right Margins" DECSLRM "s" - "Set Top and Bottom Margins" DECSTBM "r" - "Set Graphic Rendition" SGR "m" - "Select Set-Up Language" DECSSL "p" + "Session Page Memory Allocation" DECSPMA ",x" + "Set Port Parameter" DECSPP "\+w" + "Select ProPrinter Character Set" DECSPPCS "*p" "Select Printer Type" DECSPRTT "\$s" "Select Refresh Rate" DECSRFR "\"t" - "Select Digital Printed Data Type" DECSDPT "(p" - "Select ProPrinter Character Set" DECSPPCS "*p" - "Select Communication Speed" DECSCS "*r" - "Select Communication Port" DECSCP "*u" "Set Scroll Speed" DECSSCLS " p" - "Set Cursor Style" DECSCUSR " q" - "Set Key Click Volume" DECSKCV " r" - "Set Warning Bell Volume" DECSWBV " t" - "Set Margin Bell Volume" DECSMBV " u" - "Set Lock Key Style" DECSLCK " v" - "Select Flow Control Type" DECSFC "*s" - "Select Disconnect Delay Time" DECSDDT "\$q" + "Set Status Line Type" DECSSDT "$~" + "Select Set-Up Language" DECSSL "p" + "Set Top and Bottom Margins" DECSTBM "r" + "Select Color Lookup Table" DECSTGLT ")\{" "Set Transmit Rate Limit" DECSTRL "u" - "Set Port Parameter" DECSPP "\+w" + "Set Warning Bell Volume" DECSWBV " t" + "Select Zero Symbol" DECSZS ",\{" + "Terminal Mode Emulation" DECTME " ~" + "Set Graphic Rendition" SGR "m" + "invalid" invalid ".." } set DECRQSS_DICT [dict create] foreach {desc name str} $DECRQSS_DATA { @@ -2461,12 +2678,12 @@ namespace eval punk::console { set c1 [string index $payload 0] switch -- $c1 { 0 { - error "dec_request_setting - terminal doesn't recognise request '[punk::ansi::ansistring VIEW $request]' as valid" + error "dec_request_setting - terminal doesn't support querying settings for $name '[punk::ansi::ansistring VIEW $request]'" } 1 {} default { - #shouldn't get here - error "dec_request_setting - unrecognised response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" + #probable timeout - terminal doesn't recognise/respond + error "dec_request_setting - unrecognised or missing response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" } } #strip leading 1$r @@ -2772,7 +2989,12 @@ namespace eval punk::console { puts -nonewline stdout [punk::ansi::clear_above] } proc clear_below {} { - puts -nonewline stdout [punk::ansi::clear_below] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::clear_below] + } else { + puts -nonewline stdout [punk::ansi::vt52clear_below] + } } proc clear_all {} { puts -nonewline stdout [punk::ansi::clear_all] @@ -2785,7 +3007,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_on] } else { - puts -nonewline stdout [punk::ansi::cursor_on_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_on] } } proc cursor_off {} { @@ -2793,7 +3015,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_off] } else { - puts -nonewline stdout [punk::ansi::cursor_off_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_off] } } @@ -2858,10 +3080,20 @@ namespace eval punk::console { } } proc move_up {n} { - puts -nonewline stdout [punk::ansi::move_up $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_up $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_up $n] + } } proc move_down {n} { - puts -nonewline stdout [punk::ansi::move_down $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_down $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_down $n] + } } proc move_column {col} { upvar ::punk::console::is_vt52 is_vt52 @@ -2925,12 +3157,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] append commands [punk::ansi::vt52move_emit $row $col $data] foreach {row col data} $args { append commands [punk::ansi::vt52move_emit $row $col $data] } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands; flush stdout } @@ -2945,12 +3177,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] foreach ln [split $textblock \n] { append commands [punk::ansi::vt52move_emit $row $col $ln] incr row } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands;flush stdout return diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm index 5a351c2b..0272500d 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/bootsupport/modules/punk/repl-0.1.2.tm @@ -1529,7 +1529,7 @@ proc repl::repl_handler {inputchan prompt_config} { #puts -nonewline stdout [punk::ansi::move $rows 4]$msg #use cursorsave_ version which avoids get_cursor_pos_list call set msglen [ansistring length $msg] - punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg + punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg ;#supports also vt52 } else { #no mechanism to get console dimensions #we are reduced to continuously spewing lines. diff --git a/src/vfs/_vfscommon.vfs/modules/overtype-1.7.4.tm b/src/vfs/_vfscommon.vfs/modules/overtype-1.7.4.tm index d3a642da..d1d0dd44 100644 --- a/src/vfs/_vfscommon.vfs/modules/overtype-1.7.4.tm +++ b/src/vfs/_vfscommon.vfs/modules/overtype-1.7.4.tm @@ -417,6 +417,7 @@ tcl::namespace::eval overtype { set overblock [tcl::string::map {\r\n \n} $overblock] if {$opt_startrow > 1} { set down [expr {$opt_startrow -1}] + #when vt52? set overblock [punk::ansi::move_down $down]$overblock } @@ -4549,7 +4550,7 @@ tcl::namespace::eval overtype { #P3 horizontal grid size - ignored on VT300 - commonly set to zero - # ECMA-48 SSU (ESC Ps I) + # ECMA-48 SSU (ESC [ Ps I) # 0 - CHARACTER # 1 - MILLIMETRE # 2 - COMPUTER DECIPOINT 0.03528mm 1/720 of 25.4mm) diff --git a/src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm b/src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm index 69affd9b..7bf4bf7c 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/ansi-0.1.1.tm @@ -4670,6 +4670,9 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun clear_below]] return \033\[0J } + proc vt52clear_below {} { + return \033J + } proc clear_all {} { # - doesn't work?? @@ -4688,10 +4691,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[call [fun cursor_off]] return "\033\[?25l" } - proc cursor_on_vt52 {} { + proc vt52cursor_on {} { return \x1be } - proc cursor_off_vt52 {} { + proc vt52cursor_off {} { return \x1bf } @@ -4781,7 +4784,7 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu Sequence is of the form: ESCY This sequence will generally not be understood by terminals - that are not in vt52 mode (e.g DECANM unset). + that are not in vt52 mode (i.e DECANM unset). } @values -min 2 -max 2 row -type integer -help\ @@ -4959,10 +4962,10 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu #[para] DECRC return \x1b8 } - proc cursor_save_vt52 {} { + proc vt52cursor_save {} { return \x1bj } - proc cursor_restore_vt52 {} { + proc vt52cursor_restore {} { return \x1bk } @@ -5021,127 +5024,300 @@ Brightblack 100 Brightred 101 Brightgreen 102 Brightyellow 103 Brightblu # \x1b\[?7\;2\$y #where 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset) + + #names for other alt_screen mechanismk: 1047,1048 vs 1049? + #https://wiki.tau.garden/dec-modes/ #(DEC,xterm,contour,mintty,kitty etc) #https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking - #names for other alt_screen mechanismk: 1047,1048 vs 1049? - #variable decmode_names [dict create\ - # DECANM 2\ - # origin 6\ - # DECCOLM 3\ - # line_wrap 7\ - # LNM 20\ - # alt_screen 1049\ - # grapheme_clusters 2027\ - # bracketed_paste 2004\ - # mouse_sgr 1006\ - # mouse_urxvt 1015\ - # mouse_sgr_pixel 1016\ - #] - # # some more ansi mode/sequence info: #https://pkg.go.dev/github.com/charmbracelet/x/ansi + #see also: https://ucs-detect.readthedocs.io/results.html#dec-private-modes-support + + + #REVIEW - these modes are sometimes used for different things on different terminals + #For now we are assigning based on common usage in things like xterm & windows terminal + #Proper handling would require tables for various terminals - review. + + #set with DECSET ESC [ ? h + #unset with DECRST ESC [ ? l variable decmode_data { 1 { - {origin DEC description "DECCKM - Cursor Keys Mode" names {DECCKM cursor_keys}} + {origin DEC reset-state "cursor sequences" set-state "application sequences" description "DECCKM - Arrow Keys Cursor Keys Mode" names {DECCKM cursor_keys}} } 2 { - {origin DEC description "DECANM - ANSI/VT52 Mode" names {DECANM} note { + {origin DEC reset-state "VT52" set-state "ANSI" description "DECANM - ANSI/VT52 Mode" names {DECANM} note { Disable to turn on VT52 emulation. In VT52 mode - use \x1b< to exit. } } } 3 { - {origin DEC description "DECCOLM - Column" names {DECCOLM}} + {origin DEC reset-state "80-column font" set-state "132-column font" + description "DECCOLM - Select 80 or 132 Columns per page +(obs - use DECSCPP)" + names {DECCOLM} + see "https://vt100.net/docs/vt510-rm/DECCOLM.html"} } 4 { - {origin DEC description "DECSCLM - Scrolling" names {DECSCLM}} + {origin DEC reset-state "jump scroll" set-state "smooth scroll" description "DECSCLM - Scrolling Mode" names {DECSCLM}} } 5 { - {origin DEC description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} + {origin DEC reset-state "dark mode" set-state "light mode" description "DECSCNM - Screen Mode (light or dark screen)" names {DECSNM lightmode}} } 6 { - {origin DEC description "DECOM - Origin Mode (whether cursor is restricted to within page margins)" names {DECOM}} + {origin DEC reset-state "upper-left corner" set-state "within margins" + description "DECOM - Origin Mode +(whether cursor is restricted to within page margins)" + names {DECOM}} } 7 { - {origin DEC description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} + {origin DEC reset-state "no autowrap" set-state "autowrap" description "DECAWM - Auto Wrap Mode" names {DECAWM line_wrap}} + } + 8 { + {origin DEC reset-state "disabled" set-state "0.5s keypress autorepeat" + description "DECARM - Auto Repeat Mode" + see "https://vt100.net/docs/vt510-rm/DECARM.html" + names {DECARM autorepeat}} } 9 { - {origin "xterm" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking} note { + {origin "xterm" reset-state "disable" set-state "enable" description "X10 compatibility mouse" names {SET_X10_MOUSE mouse_tracking XTMOSREP} note { Escape sequence on button press only. CSI M CbCxCy (6 chars) Coords limited to 223 (=255 - 32) } } - {origin DEC description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} + {origin DEC reset-state "" set-state "" description "DECINLM - Interlace Mode (obsolete?)" names {DECINLM}} } - 20 { - {origin DEC description "LNM - Line Feed/New Line Mode" names {LNM} note { -For terminals that support LNM, the default is off -meaning a lone CR respresents the character emitted -when enter is pushed. Turning LNM on would mean that -CR LF is sent when hitting enter. This feature is -not commonly supported, and the default will normally -be as if this was off - ie lone CR. - } - } + 10 { + {origin DEC reset-state "" set-state "" description "DECEDM - Editing Mode" names {DECEDM}} + {origin rxvt reset-state "" set-state "" description "show toolbar" names {RXVTSHOWTOOLBAR}} + } + 11 { + {origin DEC reset-state "" set-state "" description "DECLTM - Line Transmit Mode" names {DECLTM}} + } + 12 { + {origin xterm reset-state "steady" set-state "blink" description "Cursor blink" names {XTCBLINK cursorblink}} + {origin DEC reset-state "" set-state "" description "DECKANAM - Katakana Shift Mode (obsolete?)" names {DECKANAM}} } 25 { - {origin DEC description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} + {origin DEC reset-state "invisible" set-state "visible" description "DECTCEM - Text Cursor Enable Mode" names {DECTCEM cursor_enable}} + } + 40 { + {origin DEC reset-state "" set-state "" description "New Line Mode" names {DECCRNLM newline_mode}} + {origin xterm reset-state "" set-state "" description "Allow 80->132 mode" names {xt80-132}} + } + 45 { + {origin DEC reset-state "" set-state "" description "Graphics Print Color Syntax" names {DECGPCS}} + {origin xterm reset-state "" set-state "" description "Reverse Wraparound Mode" names {reverse_wraparound}} } 47 { - {origin xterm description "xterm alternate buffer" names {xterm_altbuf}} - {origin DEC description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} + {origin xterm reset-state "" set-state "" description "xterm alternate buffer" names {xterm_altbuf}} + {origin DEC reset-state "" set-state "" description "DECGRPM - Graphics Rotated Print Mode (obsolete?)" names {DECGRPM}} + } + 64 { + {origin DEC reset-state "uncoupled" set-state "coupled" description "DECPCCM - Page Cursor Coupling Mode" + see "https://vt100.net/docs/vt510-rm/DECPCCM.html" + names {DECPCCM}} } 66 { - {origin DEC description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} + {origin DEC reset-state "keypad chars" set-state "app sequences" description "DECNKM - Numeric Keypad Mode" see "https://vt100.net/docs/vt510-rm/DECNKM.html" names {DECNKM}} } 67 { - {origin DEC description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} + {origin DEC reset-state "delete key" set-state "backspace key" description "DECBKM - Backarrow Key Mode" see "https://vt100.net/docs/vt510-rm/DECBKM.html" names {DECBKM}} } 69 { - {origin DEC description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} + {origin DEC reset-state "cannot set margins" set-state "can set margins" description "DECLRMM - Left Right Margin Mode" see "https://vt100.net/docs/vt510-rm/DECLRMM.html" names {DECLRMM}} + } + 80 { + {origin DEC reset-state "scrolling enabled" set-state "scrolling disabled" description "Sixel Display Mode +(VT382 disable on set) +(VT330 manual error?) +some emulators may reverse" + see "https://github.com/dankamongmen/notcurses/issues/1782" + names {sixel_display DECSDM}} + } + 95 { + {origin DEC reset-state "clear screen on column change" set-state "no clear on column change" + description "No Clearing Screen on Column Change Mode" see "" + names {DECNCSM}} + } + 98 { + {origin DEC reset-state "disabled" set-state "enabled" description "Auto Resize Mode" see "" names {DECARSM}} + } + 115 { + {origin DEC reset-state "disabled" set-state "enabled" description "Alternate Text Color Blink Mode" see "" names {DECATCBM}} + } + 117 { + {origin DEC reset-state "" set-state "" description "Erase Color Mode" see "" names {DECECM erase_color}} } 1000 { - {origin "xterm" description "VT200 compatibility mouse" names {SET_VT200_MOUSE} note { + {origin "xterm" reset-state "" set-state "" description "VT200 compatibility mouse" + names {MOUSE_REPORT_CLICK SET_VT200_MOUSE} + note { Escape sequence on both button press and release. CSI M CbCxCy } } } + 1001 { + {origin "xterm" reset-state "" set-state "" description "Use Hilite Mouse Tracking" + names {MOUSE_HILITE_TRACKING mouse_tracking_hilite}} + } + 1002 { + {origin "xterm" reset-state "" set-state "" description "Use Cell Motion Mouse Tracking" + names {MOUSE_REPORT_DRAG mouse_tracking_cellmotion}} + } + 1003 { + {origin "xterm" reset-state "" set-state "" description "Use All Motion Mouse Tracking" + names {MOUSE_ALLMOTION mouse_tracking_allmotion}} + } 1004 { - {origin "xterm" description "Send FocusIn/FocusOut events" names {mouse_focus_event}} + {origin "xterm" reset-state "" set-state "" description "Send FocusIn/FocusOut events" + names {FOCUS_IN_OUT_EVENTS mouse_focus_event}} } 1005 { - {origin "xterm" description "Enable UTF-8 Mouse Mode" names {mouse_utf8 mouse_utf8_extended}} + {origin "xterm" reset-state "" set-state "" + description "Enable UTF-8 Mouse Mode" + names {MOUSE_EXTENDED_UTF8 mouse_utf8 mouse_utf8_extended}} } 1006 { - {origin "xterm" description "Enable SGR Mouse Mode" names {mouse_sgr mouse_sgr_extended} note{ + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Mouse Mode" names + {MOUSE_EXTENDED_SGR mouse_sgr mouse_sgr_extended} + note{ SET_SGR_EXT_MODE_MOUSE - extended compared to x10 mouse protocol which limits x y coords to 223 (=255 - 32) } } } + 1007 { + {origin "xterm" reset-state "" set-state "" + description "Enable Alternate Scroll Mode" + names {ALT_SCROLL_XTERM alternate_scroll}} + } 1015 { - {origin "urxvt" description "Enable urxvt Mouse Mode" names {mouse_urxvt}} + {origin "urxvt" reset-state "" set-state "" + description "Enable urxvt Mouse Mode" + names {MOUSE_URXVT}} } 1016 { - {origin "xterm" description "Enable SGR Pixel Mouse Mode" names {mouse_sgr_pixel}} + {origin "xterm" reset-state "" set-state "" + description "Enable SGR Pixel Mouse Mode" + names {MOUSE_SGR_PIXELS}} + } + 1042 { + {origin "xterm" reset-state "" set-state "" + description "Enable Urgency window manager hint +when Control-G is received" + names {URGENCY_ON_CTRL_G xt1042}} + } + 1043 { + {origin "xterm" reset-state "" set-state "" + description "Enable raising of the window +when Control-G is received" + names {RAISE_ON_CTRL_G xt1043}} } 1047 { - {origin "xterm" description "Alternate Buffer" names {alt_buffer_only}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer" + names {ALT_SCREEN_BUFFER_XTERM alt_buffer_only}} } 1049 { - {origin "xterm" description "Alternate Buffer with save cursor" names {alt_buffer alt_screen}} + {origin "xterm" reset-state "" set-state "" + description "Alternate Buffer with save cursor" + names {ALT_SCREEN_AND_SAVE_CLEAR alt_buffer alt_screen}} + } + 1070 { + {origin "xterm" reset-state "" set-state "" + description "Use private color registers for each graphic" + names {SIXEL_PRIVATE_PALETTE}} } 2004 { - {origin "xterm" description "Set bracketed paste mode" names {bracketed_paste}} + {origin "xterm" reset-state "" set-state "" + description "Set bracketed paste mode" + names {BRACKETED_PASTE}} + } + 2026 { + {origin iTerm2 reset-state "screen updates" set-state "no screen updates" + description "Synchronized Output" + see "https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036" + names {SYNCHRONIZED_OUTPUT synch}} } 2027 { - {origin Contour description "Grapheme Cluster Processing" names {grapheme_clusters}} + {origin Contour reset-state "" set-state "" + description "Grapheme Cluster Processing" + names {GRAPHEME_CLUSTERING grapheme_clusters}} + } + 2028 { + {origin Contour reset-state "" set-state "" + description "Text reflow" + names {TEXT_REFLOW}} + } + 2048 { + {origin "@rockorager" reset-state "" set-state "" + description "Enable in-band window resize notifications +(modern VTEEWR analog)" + names {IN_BAND_WINDOW_RESIZE window_resize_reports} + see "https://rockorager.dev/misc/in-band-resize-notifications/" + } + } + 5522 { + {origin "kitty @rockorager" + reset-state "" set-state "" + description "Automatic Paste notifications" + names {automatic_paste_reports} + see "https://rockorager.dev/misc/bracketed-paste-mime/"} + } + 7700 { + {origin mintty reset-state "disable reporting" set-state "enable reporting" + description "Ambiguous width reporting" + notes "When enabled, mintty sends ^[[1W for an 'ambiguous narrow' font +and ^[[2W for an 'ambiguous wide' font allowing the application +to adjust its layout accordingly." + names {AMBIGUOUS_WIDTH_REPORTING}} + } + 7766 { + {origin mintty reset-state "hide" set-state "show" + description "Show/hide scrollbar" + names {SHOW_HIDE_SCROLLBAR mintty_scrollbar}} + } + 8200 { + {origin TeraTerm reset-state "Don't move cursor" set-state "Cursor to home" + description "Move cursor when erase complete display (ED 2)" + names {TTCTH}} + } + 8452 { + {origin "rlogin xterm" + reset-state "" set-state "" + description "Post sixel cursor position" + names {SIXEL_SCROLLING_LEAVES_CURSOR}} + } + 9001 { + {origin "conpty" + reset-state "" set-state "" + description "win32 input mode" + names {WIN32_INPUT_MODE}} + } + 19997 { + {origin "kitty" + reset-state "" set-state "" + description "Handle Ctrl-C/Ctrl-Z mode" + names {KITTY_HANDLE_CTRL_C_Z kitty_ctrl_c_ctrl_z}} + } + 77096 { + {origin "mintty" + reset-state "" set-state "" + description "BiDi mode" + names {mintty_bidi}} + } + 737769 { + {origin "foot" + reset-state "" set-state "" + description "Input Method Editor (IME) mode" + names {foot_ime}} } } set decmode_names [dict create] @@ -5154,6 +5330,117 @@ to 223 (=255 - 32) } } + #set with SM: ESC [ h + #unset with RM: ESC [ l + # https://chromium.googlesource.com/apps/libapps/+/HEAD/hterm/docs/ControlSequences.md#SM + # https://wezfurlong.org/ecma48/07-control.html?highlight=Erasure#72-definition-of-control-functions + + #primary source for ansimode_data: + # https://ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf + + #todo - different table for different terminal emulations + #modern terminal emulators generally don't support any modes above 22 + #but some such as SCOANSI, Wyse and S97801 go higher + #e.g + #Wyse wy370 uses CSI 33 h/l for steady vs blinking cursor + #SCOANSI uses 1048 for cursor on + + variable ansimode_data { + 1 { + {origin "ECMA-48" reset-state "replacing" set-state "cumulative" description "Guarded Area Transfer Mode" names {GATM}} + } + 2 { + {origin "ECMA-48" reset-state "enabled" set-state "disabled" description "Lock the keyboard" names {KAM}} + } + 3 { + {origin "ECMA-48" reset-state "control" set-state "graphic" description "Control Representation Mode" names {CRM}} + } + 4 { + {origin "ECMA-48" reset-state "replace" set-state "insert" description "Insert mode" names {IRM}} + } + 5 { + {origin "ECMA-48" reset-state "normal" set-state "diagnostic" description "Status Report Transfer Mode" names {SRTM}} + } + 6 { + {origin "ECMA-48" reset-state "protect" set-state "all" description "Erasure Mode" names {EM}} + } + 7 { + {origin "ECMA-48" reset-state "following" set-state "preceding" description "Line Editing Mode (Vertical Editing Mode)" names {VEM}} + } + 8 { + {origin "ECMA-48" reset-state "disabled" set-state "enabled" description "Bidirectional support" names {BDSM}} + } + 9 { + {origin "ECMA-48" reset-state "" set-state "" description "Device Component Select Mode" names {DCSM}} + } + 10 { + {origin "ECMA-48" reset-state "following" set-state "preceding" description "Character Editing Mode (Horizontal Editing Mode)" names {HEM}} + } + 11 { + {origin "ECMA-48" reset-state "character" set-state "size" description "Positioning Unit Mode" names {PUM}} + } + 12 { + {origin "???" reset-state "disabled" set-state "enabled" description "Local echo" names {local_echo}} + {origin "ECMA-48" reset-state "monitor" set-state "simultaneous" description "Send/Receive Mode" names {SRM}} + } + 13 { + {origin "ECMA-48" reset-state "execute" set-state "store" description "Format Effector Action Mode" names {FEAM}} + } + 14 { + {origin "ECMA-48" reset-state "insert" set-state "exclude" description "Format Effector Transfer Mode" names {FETM}} + } + 15 { + {origin "ECMA-48" reset-state "single" set-state "multiple" description "Multiple Area Transfer Mode" names {MATM}} + } + 16 { + {origin "ECMA-48" reset-state "cursor" set-state "all" description "Transfer Termination Mode" names {TTM} note { +cursor: +Only the contents of the character positions preceding the active presentation position in the presentation +component are eligible to be transmitted or transferred. +all: +The contents of character positions preceding, following, and at the active presentation position are +eligible to be transmitted or transferred. +note - No control functions are affected. + } + } + } + 17 { + {origin "ECMA-48" reset-state "select" set-state "all" description "Selected Area Transfer Mode" names {SATM}} + } + 18 { + {origin "ECMA-48" reset-state "multiple" set-state "single" description "Tabulation Stop Mode" names {TSM}} + } + 19 { + {origin "ECMA-48 deprecated" reset-state "" set-state "" description "Editing Boundary Mode" names {EBM}} + } + 20 { + {origin "ECMA-48 deprecated" reset-state "cr" set-state "crlf" description "LNM - Line Feed/New Line Mode (Automatic Newline)" names {LNM} note { +For terminals that support LNM, the default is off +meaning a lone CR respresents the character emitted +when enter is pushed. Turning LNM on would mean that +CR LF is sent when hitting enter. This feature is +not commonly supported, and the default will normally +be as if this was off - ie lone CR. + } + } + } + 21 { + {origin "ECMA-48" reset-state "replacing" set-state "cumulative" description "Graphic Rendition Combination Mode" names {GRCM}} + } + 22 { + {origin "ECMA-48" reset-state "zero" set-state "default" description "Zero Default Mode" names {ZDM}} + } + } + set ansimode_names [dict create] + dict for {code items} $ansimode_data { + foreach itm $items { + set names [dict get $itm names] + foreach nm $names { + dict set ansimode_names $nm $code + } + } + } + diff --git a/src/vfs/_vfscommon.vfs/modules/punk/basictelnet-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/basictelnet-0.1.0.tm index b1d6e22a..973cedc9 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/basictelnet-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/basictelnet-0.1.0.tm @@ -494,7 +494,7 @@ namespace eval punk::basictelnet { ::mode $tmode } } - if {[catch {set priormouse [punk::console::get_mode mouse_sgr]}]} { + if {[catch {set priormouse [punk::console::dec_get_mode mouse_sgr]}]} { set priormouse -1 if {$mouse} { puts stderr "Cannot determine mouse_sgr mode - assuming terminal doesn't support mouse" diff --git a/src/vfs/_vfscommon.vfs/modules/punk/console-0.1.1.tm b/src/vfs/_vfscommon.vfs/modules/punk/console-0.1.1.tm index b62c497b..ff5c2904 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/console-0.1.1.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/console-0.1.1.tm @@ -129,6 +129,17 @@ namespace eval punk::console { #e.g external utils system API's. namespace export * } + namespace eval argdoc { + variable PUNKARGS + #non-colour SGR codes + set I "\x1b\[3m" ;# [a+ italic] + set NI "\x1b\[23m" ;# [a+ noitalic] + set B "\x1b\[1m" ;# [a+ bold] + set N "\x1b\[22m" ;# [a+ normal] + set T "\x1b\[1\;4m" ;# [a+ bold underline] + set NT "\x1b\[22\;24m\x1b\[4:0m" ;# [a+ normal nounderline] + interp alias "" ::overtype::example "" ::punk::args::helpers::example + } if {"windows" eq $::tcl_platform(platform)} { #accept args for all dummy/load functions so we don't have to match/update argument signatures here @@ -1129,7 +1140,12 @@ namespace eval punk::console { return } #tailcall punk::ansi::a {*}$args - ::punk::ansi::a {*}$args + variable is_vt52 + if {$is_vt52} { + return + } else { + ::punk::ansi::a {*}$args + } } lappend PUNKARGS_aliases {::punk::console::code_a? ::punk::ansi::a?} proc code_a? {args} { @@ -1236,7 +1252,7 @@ namespace eval punk::console { } } else { if {$onoff} { - unset_mode DECANM + dec_unset_mode DECANM set is_vt52 1 colour off } else { @@ -1526,11 +1542,11 @@ namespace eval punk::console { set cell_size "" set cell_size_fallback 10x20 - #todo - change -inoutchannels to -terminalobject with prebuilt default + #todo - change -console to -terminalobject with prebuilt default punk::args::define { @id -id ::punk::console::cell_size - -inoutchannels -default {stdin stdout} -type list + -console -default {stdin stdout} -type list @values -min 0 -max 1 newsize -default "" -help\ "character cell pixel dimensions WxH @@ -1538,7 +1554,7 @@ namespace eval punk::console { } proc cell_size {args} { set argd [punk::args::parse $args -cache 1 withid ::punk::console::cell_size] - set inoutchannels [dict get $argd opts -inoutchannels] + set terminal [dict get $argd opts -console] set newsize [dict get $argd values newsize] variable cell_size @@ -1546,7 +1562,7 @@ namespace eval punk::console { #query existing setting if {$cell_size eq ""} { #not set - try to query terminal's overall dimensions - set pixeldict [punk::console::get_xterm_pixels $inoutchannels] + set pixeldict [punk::console::get_xterm_pixels $terminal] lassign $pixeldict _w sw _h sh if {[string is integer -strict $sw] && [string is integer -strict $sh]} { lassign [punk::console::get_size] _cols columns _rows rows @@ -1640,8 +1656,8 @@ namespace eval punk::console { set func_con "punk::ansi::cursor_on" } else { set movefunc "punk::ansi::vt52move" - set func_coff "punk::ansi::cursor_off_vt52" - set func_con "punk::ansi::cursor_on_vt52" + 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. @@ -1708,55 +1724,235 @@ namespace eval punk::console { } - proc get_mode_line_wrap {{inoutchannels {stdin stdout}}} { + + + + proc dec_get_mode_line_wrap {{inoutchannels {stdin stdout}}} { set capturingregex {(.*)(\x1b\[\?7;([0-9]+)\$y)$} ;#must capture prefix,entire-response,response-payload set request "\x1b\[?7\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $inoutchannels $request $capturingregex] return $payload } #Terminals generally default to LNM being reset (off) ie enter key sends a lone - #Terminals tested on windows either don't respond to this query, or respond with 0 (meaning mode not understood) - #I presume from this that almost nobody is using LNM 1 (which sends both and ) - proc get_mode_LNM {{inoutchannels {stdin stdout}}} { - set capturingregex {(.*)(\x1b\[\?20;([0-9]+)\$y)$} ;#must capture prefix,entire-response,response-payload - set request "\x1b\[?20\$p" + #windows terminal defaults to LNM on, but wezterm on windows default to LNM off + #LNM on sends both and ?? + proc ansi_get_mode_LNM {{inoutchannels {stdin stdout}}} { + set capturingregex {(.*)(\x1b\[20;([0-9]+)\$y)$} ;#must capture prefix,entire-response,response-payload + set request "\x1b\[20\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $inoutchannels $request $capturingregex] return $payload } + + + + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punk::console::dec_get_mode + @cmd -name punk::console::dec_get_mode\ + -summary\ + {Get DEC mode}\ + -help\ + {Get DEC mode by sending to the console + the sequence: + ESC [ ? $ p + Where is an integer + formed from the supplied ${$I}mode${$NI} value. + + The terminal should respond with a sequence of the form: + ESC [ ? ; $ y + where is one of the statuses: + 0 - mode not recognised + 1 - mode is set + 2 - mode is unset + 3 - mode is permanently set + 4 - mode is permanently unset + + The response is automatically retrieved from the terminal + and so should not be displayed unless there is such a + significant delay that the request times out. + + The value of is returned by the dec_get_mode function. + + The delay in retrieving a terminal response can range from + a few ms to 10s of ms depending on the terminal or other + runtime conditions. + In some cases it may be beneficial to call dec_has_mode first, + (which is cached) to reduce the number of queries to the terminal. + } + @opts + -console -type list -minsize 2 -default {stdin stdout} + @values -min 1 -max 1 + mode -type {int|string} -multiple 0 -help\ + "integer for DEC mode, or name as in the dict: + ::punk::ansi::decmode_names + See also the command: ${$B}dec_modes${$N}" + }] + } #DECRPM responses e.g: # \x1b\[?7\;1\$y # \x1b\[?7\;2\$y #where 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset) - proc get_mode {num_or_name {inoutchannels {stdin stdout}}} { - if {[string is integer -strict $num_or_name]} { - set m $num_or_name + proc dec_get_mode {args} { + set argd [punk::args::parse $args withid ::punk::console::dec_get_mode] + lassign [dict values $argd] leaders opts values + set terminal [dict get $opts -console] + set mode [dict get $values mode] + + if {[string is integer -strict $mode]} { + set m $mode } else { upvar ::punk::ansi::decmode_names decmode_names - if {[dict exists $decmode_names $num_or_name]} { - set m [dict get $decmode_names $num_or_name] + if {[dict exists $decmode_names $mode]} { + set m [dict get $decmode_names $mode] } else { - error "punk::console::get_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $decmode_names]" + error "punk::console::dec_get_mode unrecognised mode '$mode'. Known mode names: [dict keys $decmode_names]" } } set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[\?%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload set request "\x1b\[?$m\$p" - set payload [punk::console::internal::get_ansi_response_payload -terminal $inoutchannels $request $capturingregex] + set payload [punk::console::internal::get_ansi_response_payload -terminal $terminal $request $capturingregex] return $payload } - proc set_mode {num_or_name {inoutchannels {stdin stdout}}} { - if {[string is integer -strict $num_or_name]} { - set m $num_or_name - } else { - upvar ::punk::ansi::decmode_names decmode_names - if {[dict exists $decmode_names $num_or_name]} { - set m [dict get $decmode_names $num_or_name] + + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punk::console::dec_set_mode + @cmd -name punk::console::dec_set_mode\ + -summary\ + {Set DEC mode(s) (DECSET)}\ + -help\ + {Set DEC mode(s) by sending to the console + the DECSET sequence: + ESC [ ? h + Where is a colon delimited set of integers + formed from the supplied ${$I}mode${$NI} values. + } + @opts + -console -type list -minsize 2 -default {stdin stdout} + @values -min 1 -max -1 + mode -type {int|string} -multiple 1 -help\ + "integer for DEC mode, or name as in the dict: + ::punk::ansi::decmode_names + See also the command: ${$B}dec_modes${$N}" + }] + } + #todo - should accept multiple mode nums/names at once + proc dec_set_mode {args} { + set argd [punk::args::parse $args withid ::punk::console::dec_set_mode] + lassign [dict values $argd] leaders opts values + set terminal [dict get $opts -console] + set modes [dict get $values mode] ;#multiple + + set modelist [list] + foreach num_or_name $modes { + if {[string is integer -strict $num_or_name]} { + set m $num_or_name } else { - error "punk::console::set_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $decmode_names]" + upvar ::punk::ansi::decmode_names decmode_names + if {[dict exists $decmode_names $num_or_name]} { + set m [dict get $decmode_names $num_or_name] + } else { + error "punk::console::dec_set_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $decmode_names]" + } } + lappend modelist $m } - puts -nonewline "\x1b\[?${m}h" + set modes_string [join $modelist {;}] + set term_out [lindex $terminal 1] + puts -nonewline $term_out "\x1b\[?${modes_string}h" + } + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punk::console::dec_unset_mode + @cmd -name punk::console::dec_unset_mode\ + -summary\ + {Unset DEC mode(s) (DECRST)}\ + -help\ + {Unset DEC mode(s) by sending to the console + the DECRST sequence: + ESC [ ? l + Where is a colon delimited set of integers + formed from the supplied ${$I}mode${$NI} values. + } + @opts + -console -type list -minsize 2 -default {stdin stdout} + @values -min 1 -max -1 + mode -type {int|string} -multiple 1 -help\ + "integer for DEC mode, or name as in the dict: + ::punk::ansi::decmode_names + See also the command: ${$B}dec_modes${$N}" + }] } - proc unset_mode {num_or_name {inoutchannels {stdin stdout}}} { + proc dec_unset_mode {args} { + set argd [punk::args::parse $args withid ::punk::console::dec_unset_mode] + lassign [dict values $argd] leaders opts values + set terminal [dict get $opts -console] + set modes [dict get $values mode] ;#multiple + + set modelist [list] + foreach num_or_name $modes { + if {[string is integer -strict $num_or_name]} { + set m $num_or_name + } else { + upvar ::punk::ansi::decmode_names decmode_names + if {[dict exists $decmode_names $num_or_name]} { + set m [dict get $decmode_names $num_or_name] + } else { + error "punk::console::dec_unset_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $decmode_names]" + } + } + lappend modelist $m + } + set modes_string [join $modelist {;}] + set term_out [lindex $terminal 1] + puts -nonewline $term_out "\x1b\[?${modes_string}l" + } + + variable dec_has_mode_cache + set dec_has_mode_cache [dict create] + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punk::console::dec_has_mode + @cmd -name punk::console::dec_has_mode\ + -summary\ + {Check if console supports a particular DEC mode}\ + -help\ + {Check if console supports a particular DEC mode by emitting query to the console using + the sequence: + ESC [ ? $ p + Where is an integer identifier. + + Will return zero if the mode is unsupported, 1|2|3|4 if supported. + + The result is cached. + Cached values of 1 or 2 may not represent current state. + ${$B}dec_get_mode${$N} or ${$B}dec_has_mode -refresh${$N} should be + used if up-to-date current state of a supported mode is required. + } + @opts + -console -type list -minsize 2 -default {stdin stdout} + -refresh -type none -help\ + "Force a re-test of the mode." + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } + @values -min 1 -max 1 + mode -type {int|string} -help\ + "integer for DEC mode, or name as in the dict: + ::punk::ansi::decmode_names" + }] + } + proc dec_has_mode {args} { + set argd [punk::args::parse $args withid ::punk::console::dec_has_mode] + lassign [dict values $argd] leaders opts values received + set console [dict get $opts -console] + set num_or_name [dict get $values mode] + set do_refresh [dict exists $received -refresh] + set return [dict get $opts -return] + if {[string is integer -strict $num_or_name]} { set m $num_or_name } else { @@ -1764,102 +1960,673 @@ namespace eval punk::console { if {[dict exists $decmode_names $num_or_name]} { set m [dict get $decmode_names $num_or_name] } else { - error "punk::console::unset_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $decmode_names]" + error "punk::console::dec_get_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $decmode_names]" + } + } + variable dec_has_mode_cache + if {$do_refresh} { + if {[dict exists $dec_has_mode_cache $console $m]} { + dict unset dec_has_mode_cache $console $m + } + } + + if {![dict exists $dec_has_mode_cache $console $m]} { + set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[\?%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload + set request "\x1b\[?$m\$p" + set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] + #set has_mode [expr {$payload != 0}] + #we can use the payload result as the response as non-zero responses evaluate to true + set has_mode $payload + if {$has_mode ne ""} { + dict set dec_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" } + } else { + set has_mode [dict get $dec_has_mode_cache $console $m] + set source "cache" } - puts -nonewline "\x1b\[?${m}l" + if {$return eq "dict"} { + return [dict create result $has_mode source $source] + } + return $has_mode } - variable has_mode_cache - set has_mode_cache [dict create] lappend PUNKARGS [list { - @id -id ::punk::console::has_mode - @cmd -name punk::console::has_mode\ + @id -id ::punk::console::dec_modes + @cmd -name punk::console::dec_modes\ -summary\ - {Check if console supports DEC mode}\ + {Show table of DEC modes}\ -help\ - {Check if console supports DEC mode by emitting query to the console using - the sequence: - ESC [ ? $ p - Where is an integer identifier. - } + {Show table of DEC modes with basic information.} @opts -console -type list -minsize 2 -default {stdin stdout} - -refresh -type none - @values -min 1 -max 1 - mode -type {int|string} -help\ - "integer for DEC mode, or name as in the dict: - ::punk::ansi::decmode_names" + -test -type none -help\ + "Test current value/support for each mode" + -supported -type none -help\ + "Limit results to supported DEC modes" + @values -min 0 -max -1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" }] - proc has_mode {args} { - set argd [punk::args::parse $args withid ::punk::console::has_mode] + proc dec_modes {args} { + set argd [punk::args::parse $args withid ::punk::console::dec_modes] lassign [dict values $argd] leaders opts values received - set console [dict get $opts -console] - set num_or_name [dict get $values mode] - if {[dict exists $received -refresh]} { - set do_refresh 1 + set terminal [dict get $opts -console] + set do_test [dict exists $received -test] + set only_supported [dict exists $received -supported] + if {[dict exists $values match]} { + set matches [dict get $values match] + } else { + set matches {} + } + + upvar ::punk::ansi::decmode_data decmode_data + upvar ::punk::ansi::decmode_names decmode_names + set t [textblock::class::table new "Dec Modes"] + $t configure -show_header 1 -show_hseps 1 + $t add_column -headers Code + $t add_column -headers Names + $t add_column -headers Origin + $t add_column -headers Description + $t add_column -headers set-state + $t add_column -headers reset-state + if {$do_test} { + $t add_column -headers Status + } + set names [dict keys $decmode_names] + if {[llength $matches] == 0} { + #show all entries + set codes [dict keys $decmode_data] } else { - set do_refresh 0 + set codes [list] + foreach m $matches { + if {[punk::lib::is_indexset $m]} { + set defaultmax 10000 ;#some codes are very high e.g foot IME 737769 - but we don't want to default test/scan for hours + #todo set max to actual largest value from indexset + lappend codes {*}[punk::lib::indexset_resolve -base 1 $defaultmax $m] + } else { + foreach nm $names { + if {[string match -nocase $m $nm]} { + set code [dict get $decmode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $decmode_names $nm] + } + } + } + } + } + } + #dict for {code items} $decmode_data {} + foreach code $codes { + if {[dict exists $decmode_data $code]} { + set items [dict get $decmode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } + set colour "" + set set_state_colour "" + set reset_state_colour "" + set RST "" + if {$do_test} { + #dec_has_mode can be cached - in which case only 0|3|4 can be relied upon without re-querying + set hasmode_dict [dec_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [dec_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + + switch -- $testresult { + 0 { + set colour [punk::ansi::a+ red bold] + } + 1 { + set colour [punk::ansi::a+ green] + set set_state_colour $colour + } + 2 { + set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour + } + 3 { + set colour [punk::ansi::a+ green] + set set_state_colour $colour + } + 4 { + set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour + } + default { + #failedquery + set colour [punk::ansi::a+ red bold] + } + } + if {$colour ne ""} { + set RST "\x1b\[m" + } + set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #dec_has_mode still queries terminal - but is cached if a response was received + if {[dec_has_mode -console $terminal $code] == 0} { + continue + } + } + } + foreach itm $items { + set code $colour$code$RST + set names $colour[dict get $itm names]$RST + set origin [dict get $itm origin] + set desc [dict get $itm description] + set set_state $set_state_colour[dict get $itm set-state]$RST + set reset_state $reset_state_colour[dict get $itm reset-state]$RST + set row [list $code [join $names \n] $origin $desc $set_state $reset_state] + if {$do_test} { + lappend row $testdisplay + } + $t add_row $row + } } + set out [$t print] + $t destroy + return $out + } + + variable ansi_has_mode_cache + set ansi_has_mode_cache [dict create] + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punk::console::ansi_has_mode + @cmd -name punk::console::ansi_has_mode\ + -summary\ + {Check if console supports a particular ANSI mode}\ + -help\ + {Check if console supports a particular ANSI mode by emitting query to the console using + the sequence: + ${$B}ESC [ $ p${$N} + Where ${$B}${$N} is an integer identifier. + } + @opts + #review - problem with 's ansi_has_mode' + #-console -type list -typesynopsis {{${$I}inputchan${$NI} ${$I}outputchan${$NI}}} -minsize 2 -default {stdin stdout} + -console -type list -minsize 2 -default {stdin stdout} + -refresh -type none + -return -type string -choices {dict result} -default result -choicelabels { + dict\ + "Return a dict with keys source and result + source indicates whether the result came + from query or cache." + } + @values -min 1 -max 1 + mode -type {int|string} -help\ + "integer for ANSI mode, or name as in the dict: + ::punk::ansi::ansimode_names" + }] + } + proc ansi_has_mode {args} { + set argd [punk::args::parse $args withid ::punk::console::ansi_has_mode] + lassign [dict values $argd] leaders opts values received + set console [dict get $opts -console] + set num_or_name [dict get $values mode] + set return [dict get $opts -return] + set do_refresh [dict exists $received -refresh] if {[string is integer -strict $num_or_name]} { set m $num_or_name } else { - upvar ::punk::ansi::decmode_names decmode_names - if {[dict exists $decmode_names $num_or_name]} { - set m [dict get $decmode_names $num_or_name] + upvar ::punk::ansi::ansimode_names ansimode_names + if {[dict exists $ansimode_names $num_or_name]} { + set m [dict get $ansimode_names $num_or_name] } else { - error "punk::console::get_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $decmode_names]" + error "punk::console::ansi_has_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $ansimode_names]" } } - variable has_mode_cache + variable ansi_has_mode_cache if {$do_refresh} { - if {[dict exists $has_mode_cache $console $m]} { - dict unset has_mode_cache $console $m + if {[dict exists $ansi_has_mode_cache $console $m]} { + dict unset ansi_has_mode_cache $console $m } } - if {![dict exists $has_mode_cache $console $m]} { - set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[\?%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload - set request "\x1b\[?$m\$p" + if {![dict exists $ansi_has_mode_cache $console $m]} { + set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload + set request "\x1b\[$m\$p" set payload [punk::console::internal::get_ansi_response_payload -terminal $console $request $capturingregex] - set has_mode [expr {$payload != 0}] - dict set has_mode_cache $console $m $has_mode + #set has_mode [expr {$payload != 0}] + set has_mode $payload + if {$has_mode ne ""} { + dict set ansi_has_mode_cache $console $m $has_mode + set source "query" + } else { + #don't cache an empty/failed response - review + set has_mode 0 + set source "failedquery" + } } else { - set has_mode [dict get $has_mode_cache $console $m] + set has_mode [dict get $ansi_has_mode_cache $console $m] + set source "cache" + } + if {$return eq "dict"} { + return [dict create result $has_mode source $source] } return $has_mode } + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punk::console::ansi_set_mode + @cmd -name punk::console::ansi_set_mode\ + -summary\ + {Set ANSI mode(s) (SM)}\ + -help\ + {Set ANSI mode(s) by sending to the console + the SM sequence: + ESC [ h + Where is a colon delimited set of integers + formed from the supplied ${$I}mode${$NI} values. + } + @opts + -console -type list -minsize 2 -default {stdin stdout} + @values -min 1 -max -1 + mode -type {int|string} -multiple 1 -help\ + "integer for ANSI mode, or name as in the dict: + ::punk::ansi::ansimode_names + See also the command: ${$B}ansi_modes${$N}" + }] + } + proc ansi_set_mode {args} { + set argd [punk::args::parse $args withid ::punk::console::ansi_set_mode] + lassign [dict values $argd] leaders opts values + set terminal [dict get $opts -console] + set modes [dict get $values mode] ;#multiple + + set modelist [list] + foreach num_or_name $modes { + if {[string is integer -strict $num_or_name]} { + set m $num_or_name + } else { + upvar ::punk::ansi::ansimode_names ansimode_names + if {[dict exists $ansimode_names $num_or_name]} { + set m [dict get $ansimode_names $num_or_name] + } else { + error "punk::console::ansi_set_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $ansimode_names]" + } + } + lappend modelist $m + } + set modes_string [join $modelist {;}] + set term_out [lindex $terminal 1] + puts -nonewline $term_out "\x1b\[${modes_string}h" + } + + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punk::console::ansi_unset_mode + @cmd -name punk::console::ansi_unset_mode\ + -summary\ + {Unset ANSI mode(s) (RM)}\ + -help\ + {Unset ANSI mode(s) by sending to the console + the RM sequence: + ESC [ l + Where is a colon delimited set of integers + formed from the supplied ${$I}mode${$NI} values. + } + @opts + -console -type list -minsize 2 -default {stdin stdout} + @values -min 1 -max -1 + mode -type {int|string} -multiple 1 -help\ + "integer for ANSI mode, or name as in the dict: + ::punk::ansi::ansimode_names + See also the command: ${$B}ansi_modes${$N}" + }] + } + proc ansi_unset_mode {args} { + set argd [punk::args::parse $args withid ::punk::console::ansi_unset_mode] + lassign [dict values $argd] leaders opts values + set terminal [dict get $opts -console] + set modes [dict get $values mode] ;#multiple + + set modelist [list] + foreach num_or_name $modes { + if {[string is integer -strict $num_or_name]} { + set m $num_or_name + } else { + upvar ::punk::ansi::ansimode_names ansimode_names + if {[dict exists $ansimode_names $num_or_name]} { + set m [dict get $ansimode_names $num_or_name] + } else { + error "punk::console::ansi_unset_mode unrecognised mode '$num_or_name'. Known mode names: [dict keys $ansimode_names]" + } + } + lappend modelist $m + } + set modes_string [join $modelist {;}] + set term_out [lindex $terminal 1] + puts -nonewline $term_out "\x1b\[${modes_string}l" + } + namespace eval argdoc { + lappend PUNKARGS [list { + @id -id ::punk::console::ansi_get_mode + @cmd -name punk::console::ansi_get_mode\ + -summary\ + {Get ANSI mode}\ + -help\ + {Get ANSI mode by sending to the console + the sequence: + ESC [ $ p + Where is an integer + formed from the supplied ${$I}mode${$NI} value. + + The terminal should respond with a sequence of the form: + ESC [ ; $ y + where is one of the statuses: + 0 - mode not recognised + 1 - mode is set + 2 - mode is unset + 3 - mode is permanently set + 4 - mode is permanently unset + + The response is automatically retrieved from the terminal + and so should not be displayed unless there is such a + significant delay that the request times out. + + The value of is returned by the ansi_get_mode function. + } + @opts + -console -type list -minsize 2 -default {stdin stdout} + @values -min 1 -max 1 + mode -type {int|string} -multiple 0 -help\ + "integer for ANSI mode, or name as in the dict: + ::punk::ansi::ansimode_names + See also the command: ${$B}ansi_modes${$N}" + }] + } + #DECRPM responses e.g: + # \x1b\[12\;1\$y + # \x1b\[?7\;2\$y + #where 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset) + proc ansi_get_mode {args} { + set argd [punk::args::parse $args withid ::punk::console::ansi_get_mode] + lassign [dict values $argd] leaders opts values + set terminal [dict get $opts -console] + set mode [dict get $values mode] + + if {[string is integer -strict $mode]} { + set m $mode + } else { + upvar ::punk::ansi::ansimode_names ansimode_names + if {[dict exists $ansimode_names $mode]} { + set m [dict get $ansimode_names $mode] + } else { + error "punk::console::ansi_get_mode unrecognised mode '$mode'. Known mode names: [dict keys $ansimode_names]" + } + } + set capturingregex [string map [list %MODE% $m] {(.*)(\x1b\[%MODE%;([0-9]+)\$y)$}] ;#must capture prefix,entire-response,response-payload + set request "\x1b\[$m\$p" + set payload [punk::console::internal::get_ansi_response_payload -terminal $terminal $request $capturingregex] + return $payload + } + #todo ansi_unset_mode + lappend PUNKARGS [list { + @id -id ::punk::console::ansi_modes + @cmd -name punk::console::ansi_modes\ + -summary\ + {Show table of ANSI modes}\ + -help\ + {Show table of ANSI modes with basic information.} + @opts + -console -type list -minsize 2 -default {stdin stdout} + -test -type none -help\ + "Test current value/support for each mode" + -supported -type none -help\ + "Limit results to supported ANSI modes" + @values -min 0 -max -1 + match -type indexset|string -multiple 1 -optional 1 -help\ + "Match code or name" + }] + proc ansi_modes {args} { + set argd [punk::args::parse $args withid ::punk::console::ansi_modes] + lassign [dict values $argd] leaders opts values received + set terminal [dict get $opts -console] + set do_test [dict exists $received -test] + if {[dict exists $values match]} { + set matches [dict get $values match] + } else { + set matches {} + } + set only_supported [dict exists $received -supported] + + upvar ::punk::ansi::ansimode_data ansimode_data + upvar ::punk::ansi::ansimode_names ansimode_names + + set t [textblock::class::table new "ANSI Modes"] + $t configure -show_header 1 -show_hseps 1 + $t add_column -headers Code + $t add_column -headers Names + $t add_column -headers Origin + $t add_column -headers Description + $t add_column -headers set-state + $t add_column -headers reset-state + if {$do_test} { + $t add_column -headers Status + } + set names [dict keys $ansimode_names] + if {[llength $matches] == 0} { + #show all entries + set codes [dict keys $ansimode_data] + } else { + set codes [list] + foreach m $matches { + if {[punk::lib::is_indexset $m]} { + set iparts [split $m ,] + #determine the largest index requested - which may be greater than the defined range if explicitly specified + #(allow scanning for unusual ansi modes above 22 - *some* known to exist - private modes should use DECSET DECRESET instead) + #above 22 values seem to be mainly from SCOANSI and Wyse terminals + + set maxint [dict size $ansimode_data] + foreach i $iparts { + if {[string is integer -strict $i]} { + if {$i > $maxint} { + set maxint $i + } + } else { + if {![string match *end* $i]} { + lassign [split [string map [list .. \uFFEF] $i] \uFFEF] a b + if {$a > $b} { + if {$a > $maxint} { + set maxint $a + } + } else { + if {$b > $maxint} { + set maxint $b + } + } + } + } + } + puts "---> maxint: $maxint" + lappend codes {*}[punk::lib::indexset_resolve -base 1 $maxint $m] + } else { + foreach nm $names { + if {[string match -nocase $m $nm]} { + set code [dict get $ansimode_names $nm] + #only suppress dupes if sequential (display loop will still show multiple entries for the code if it has multiple origin entries) + if {[lindex $codes end] ne $code} { + lappend codes [dict get $ansimode_names $nm] + } + } + } + } + } + } + + #dict for {code items} $ansimode_data {} + foreach code $codes { + if {[dict exists $ansimode_data $code]} { + set items [dict get $ansimode_data $code] + } else { + set items [list [dict create origin ? description "" names "" set-state "" reset-state ""]] + } + set colour "" + set set_state_colour "" + set reset_state_colour "" + set RST "" + if {$do_test} { + set hasmode_dict [ansi_has_mode -console $terminal -return dict $code] + switch -- [dict get $hasmode_dict result] { + 0 { + if {$only_supported} { + continue + } + if {[dict get $hasmode_dict source] eq "failedquery"} { + set testresult "no response" + } else { + set testresult 0 + } + } + 1 - 2 { + if {[dict get $hasmode_dict source] eq "cache"} { + #a terminal query is required + set testresult [ansi_get_mode -console $terminal $code] + } else { + set testresult [dict get $hasmode_dict result] + if {![string is integer -strict $testresult]} { + set testresult "No response" + } + } + } + default { + #3|4 - permanently enabled or permanently disabled + set testresult [dict get $hasmode_dict result] + } + } + + #set testresult [ansi_get_mode $code] + switch -- $testresult { + 0 { + set colour [punk::ansi::a+ red bold] + } + 1 { + set colour [punk::ansi::a+ green] + set set_state_colour $colour + } + 2 { + set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour + } + 3 { + set colour [punk::ansi::a+ green] + set set_state_colour $colour + } + 4 { + set colour [punk::ansi::a+ yellow bold] + set reset_state_colour $colour + } + default { + #unexpected + } + } + if {$colour ne ""} { + set RST "\x1b\[m" + } + set testdisplay $colour$testresult$RST + } else { + if {$only_supported} { + #ansi_has_mode still queries terminal - but is cached if a response was received + if {[ansi_has_mode -console $terminal $code] == 0} { + continue + } + } + } + foreach itm $items { + set code $colour$code$RST + set names $colour[dict get $itm names]$RST + set origin [dict get $itm origin] + set desc [dict get $itm description] + set set_state $set_state_colour[dict get $itm set-state]$RST + set reset_state $reset_state_colour[dict get $itm reset-state]$RST + + set row [list $code [join $names \n] $origin $desc $set_state $reset_state] + if {$do_test} { + lappend row $testdisplay + } + $t add_row $row + } + } + set out [$t print] + $t destroy + return $out + } + + #see https://vt100.net/dec/ek-vt520-rm.pdf set DECRQSS_DATA { + "Assign Color" DECAC ",|" + "Alternate Text Color" DECATC ",\}" + "CRT Saver Timing" DECCRTST "-q" + "Down Line Load Allocation" DECDLDA ",z" + "Energy Save Timing" DECSEST "-r" + "Select Auto Repeat Rate" DECARR "-p" "Select Active Status Display" DECSASD "$\}" "Select Attribute Change Extent" DECSACE "*x" "Set Character Attribute" DECSCA "\"q" - "Set Conformance Level" DECSCL "\"p" + "Select Communication Port" DECSCP "*u" "Set Columns Per Page" DECSCPP "\$|" + "Set Conformance Level" DECSCL "\"p" + "Select Communication Speed" DECSCS "*r" + "Set Cursor Style" DECSCUSR " q" + "Select Disconnect Delay Time" DECSDDT "\$q" + "Select Digital Printed Data Type" DECSDPT "(p" + "Select Flow Control Type" DECSFC "*s" + "Set Key Click Volume" DECSKCV " r" + "Set Lock Key Style" DECSLCK " v" + "Set Left and Right Margins" DECSLRM "s" "Set Lines Per Page" DECSLPP "t" + "Set Margin Bell Volume" DECSMBV " u" "Set Number of Lines per Screen" DECSNLS "*|" - "Set Status Line Type" DECSSDT "$~" - "Set Left and Right Margins" DECSLRM "s" - "Set Top and Bottom Margins" DECSTBM "r" - "Set Graphic Rendition" SGR "m" - "Select Set-Up Language" DECSSL "p" + "Session Page Memory Allocation" DECSPMA ",x" + "Set Port Parameter" DECSPP "\+w" + "Select ProPrinter Character Set" DECSPPCS "*p" "Select Printer Type" DECSPRTT "\$s" "Select Refresh Rate" DECSRFR "\"t" - "Select Digital Printed Data Type" DECSDPT "(p" - "Select ProPrinter Character Set" DECSPPCS "*p" - "Select Communication Speed" DECSCS "*r" - "Select Communication Port" DECSCP "*u" "Set Scroll Speed" DECSSCLS " p" - "Set Cursor Style" DECSCUSR " q" - "Set Key Click Volume" DECSKCV " r" - "Set Warning Bell Volume" DECSWBV " t" - "Set Margin Bell Volume" DECSMBV " u" - "Set Lock Key Style" DECSLCK " v" - "Select Flow Control Type" DECSFC "*s" - "Select Disconnect Delay Time" DECSDDT "\$q" + "Set Status Line Type" DECSSDT "$~" + "Select Set-Up Language" DECSSL "p" + "Set Top and Bottom Margins" DECSTBM "r" + "Select Color Lookup Table" DECSTGLT ")\{" "Set Transmit Rate Limit" DECSTRL "u" - "Set Port Parameter" DECSPP "\+w" + "Set Warning Bell Volume" DECSWBV " t" + "Select Zero Symbol" DECSZS ",\{" + "Terminal Mode Emulation" DECTME " ~" + "Set Graphic Rendition" SGR "m" + "invalid" invalid ".." } set DECRQSS_DICT [dict create] foreach {desc name str} $DECRQSS_DATA { @@ -1878,8 +2645,8 @@ namespace eval punk::console { #(no leading DCS) - this lappend PUNKARGS [list { - @id -id ::punk::console::request_dec_setting - @cmd -name punk::console::request_dec_setting\ + @id -id ::punk::console::dec_request_setting + @cmd -name punk::console::dec_request_setting\ -summary\ {Perform DECRQSS query to get DECRPSS response}\ -help\ @@ -1891,15 +2658,15 @@ namespace eval punk::console { @values -min 1 -max 1 name -type string }] - proc request_dec_setting {args} { - set argd [punk::args::parse $args withid ::punk::console::request_dec_setting] + proc dec_request_setting {args} { + set argd [punk::args::parse $args withid ::punk::console::dec_request_setting] lassign [dict values $argd] leaders opts values set console [dict get $opts -console] set name [dict get $values name] variable DECRQSS_DICT if {![dict exists $DECRQSS_DICT $name]} { - error "request_dec_setting unrecognised name $name. Known values: [dict keys $DECRQSS_DICT]" + error "dec_request_setting unrecognised name $name. Known values: [dict keys $DECRQSS_DICT]" } set str [dict get $DECRQSS_DICT $name] set re_str [string map [list * \\* \$ \\\$ + \\+ ( \\(] $str] ;#regex escaped @@ -1911,12 +2678,12 @@ namespace eval punk::console { set c1 [string index $payload 0] switch -- $c1 { 0 { - error "request_dec_setting - terminal doesn't recognise request '[punk::ansi::ansistring VIEW $request]' as valid" + error "dec_request_setting - terminal doesn't support querying settings for $name '[punk::ansi::ansistring VIEW $request]'" } 1 {} default { - #shouldn't get here - error "request_dec_setting - unrecognised response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" + #probable timeout - terminal doesn't recognise/respond + error "dec_request_setting - unrecognised or missing response to request '[punk::ansi::ansistring VIEW $request]'. payload: [punk::ansi::ansistring VIEW $payload]" } } #strip leading 1$r @@ -2091,8 +2858,7 @@ namespace eval punk::console { if {[info exists ::env(TERM_PROGRAM)]} { #terminals known to support grapheme clusters, but unable to respond to decmode request 2027 - #wezterm (on windows as at 2024-12 decmode 2027 doesn't work) - #REVIEW - what if terminal is remote wezterm? can/will this env variable + #wezterm (on windows as at 2024-12 decmode 2027 doesn't work) (2025-12 update - 2027 request works) # iterm and apple terminal also set TERM_PROGRAM if {[string tolower $::env(TERM_PROGRAM)] in [list wezterm]} { set is_available 1 @@ -2100,7 +2866,7 @@ namespace eval punk::console { } } #where 1 = set, 2 = unset. (0 = mode not recognised, 3 = permanently set, 4 = permanently unset) - set state [get_mode grapheme_clusters] ;#decmode 2027 extension + set state [dec_get_mode grapheme_clusters] ;#decmode 2027 extension set is_available 0 switch -- $state { 0 { @@ -2223,7 +2989,12 @@ namespace eval punk::console { puts -nonewline stdout [punk::ansi::clear_above] } proc clear_below {} { - puts -nonewline stdout [punk::ansi::clear_below] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::clear_below] + } else { + puts -nonewline stdout [punk::ansi::vt52clear_below] + } } proc clear_all {} { puts -nonewline stdout [punk::ansi::clear_all] @@ -2236,7 +3007,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_on] } else { - puts -nonewline stdout [punk::ansi::cursor_on_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_on] } } proc cursor_off {} { @@ -2244,7 +3015,7 @@ namespace eval punk::console { if {!$is_vt52} { puts -nonewline stdout [punk::ansi::cursor_off] } else { - puts -nonewline stdout [punk::ansi::cursor_off_vt52] + puts -nonewline stdout [punk::ansi::vt52cursor_off] } } @@ -2257,7 +3028,7 @@ namespace eval punk::console { The sequence emitted will depend on the mode of the terminal as stored in the consolehandle. Directly setting the mode via raw escape sequences: - e.g unset_mode DECANM for vt52 + e.g dec_unset_mode DECANM for vt52 or puts \x1b< to return to ANSI will not necessarily update the application of the change in terminal state. Major state changes @@ -2309,10 +3080,20 @@ namespace eval punk::console { } } proc move_up {n} { - puts -nonewline stdout [punk::ansi::move_up $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_up $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_up $n] + } } proc move_down {n} { - puts -nonewline stdout [punk::ansi::move_down $n] + upvar ::punk::console::is_vt52 is_vt52 + if {!$is_vt52} { + puts -nonewline stdout [punk::ansi::move_down $n] + } else { + puts -nonewline stdout [punk::ansi::vt52move_down $n] + } } proc move_column {col} { upvar ::punk::console::is_vt52 is_vt52 @@ -2376,12 +3157,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] append commands [punk::ansi::vt52move_emit $row $col $data] foreach {row col data} $args { append commands [punk::ansi::vt52move_emit $row $col $data] } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands; flush stdout } @@ -2396,12 +3177,12 @@ namespace eval punk::console { } append commands [punk::ansi::cursor_restore_dec] } else { - append commands [punk::ansi::cursor_save_vt52] + append commands [punk::ansi::vt52cursor_save] foreach ln [split $textblock \n] { append commands [punk::ansi::vt52move_emit $row $col $ln] incr row } - append commands [punk::ansi::cursor_restore_vt52] + append commands [punk::ansi::vt52cursor_restore] } puts -nonewline stdout $commands;flush stdout return @@ -2885,7 +3666,7 @@ namespace eval punk::console::check { namespace eval ::punk::args::register { #use fully qualified so 8.6 doesn't find existing var in global namespace - lappend ::punk::args::register::NAMESPACES ::punk::console ::punk::console::internal ::punk::console::local ::punk::console::ansi + lappend ::punk::args::register::NAMESPACES ::punk::console ::punk::console::argdoc ::punk::console::internal ::punk::console::local ::punk::console::ansi } diff --git a/src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.2.tm b/src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.2.tm index 5a351c2b..0272500d 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.2.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/repl-0.1.2.tm @@ -1529,7 +1529,7 @@ proc repl::repl_handler {inputchan prompt_config} { #puts -nonewline stdout [punk::ansi::move $rows 4]$msg #use cursorsave_ version which avoids get_cursor_pos_list call set msglen [ansistring length $msg] - punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg + punk::console::cursorsave_move_emitblock_return $rows [expr {$cols - $msglen -1}] $msg ;#supports also vt52 } else { #no mechanism to get console dimensions #we are reduced to continuously spewing lines. diff --git a/src/vfs/_vfscommon.vfs/modules/punk/sixel-0.1.0.tm b/src/vfs/_vfscommon.vfs/modules/punk/sixel-0.1.0.tm index 0cb5be16..cb5721d0 100644 --- a/src/vfs/_vfscommon.vfs/modules/punk/sixel-0.1.0.tm +++ b/src/vfs/_vfscommon.vfs/modules/punk/sixel-0.1.0.tm @@ -141,7 +141,7 @@ tcl::namespace::eval punk::sixel { set cell_size [punk::console::cell_size] } lassign [split $cell_size x] cwidth cheight - set height_cells [expr {int(ceil($height_pixels /double($cheight)))}] + set height_cells [expr {int(ceil($height_pixels /double($cheight)))}] set sixelparams "" set sixel_extents [list] ;#number of sixels in each line taking into account retraces due to $