Windows 10 users have the joy of Virtual terminal escape sequences, However they take a bit of learning, and unlike most windows commands, there's no usage information for them available from the command line. Deleting lines, or even a number of characters from the current cursor position, is quite simple, However, when using multiple VT codes in combination, say to: Hide the cursor, shift it's position, change FG/BG color and remove a numer of cells from the current line, the sequences used can rapidly become a nearly unreadable command line that's difficult to maintain.
Virtual Terminal sequences do however provide a means to fine control of display properties and prettyfied output, and are well worth using.
To simplify the use of Virtual Terminal sequences and make code maintenance easier, I created the below Batch Macro for handling multiple aspects of Cursor output in the windows 10 command prompt, complete with help file for usage to assist new batch scripters in learning what can be done with Virtual Terminal Sequences.
Supports and Shows examples of:
- Cursor Positioning
- Absolute
- Relative to last Cursor Position ; left right by n columns ; up down by n lines
- Combinations of Relative and Absolute Position.
- Show / Hide Cursor
- Cursor Graphics properties [ Color ; Foreground and Background ]
- Same line multicolor output
- Easily chain multiple VT graphics sequences.
- Clearing of all text on a line from a given position.
- Deletion of a number of characters to the right of the cursor on the current line.
- Optionally Save the Cursor position at the time of expansion as independent Y and X values.
- Switching between Screen buffers.
Note:
I've included below the final usage example a command line that uses
VT codes to achieve the same result as that example, to illustrate the
difference in readability when using multiple Terminal sequences in
the same Cursor output.
NOTES On changing Buffers:
Cursor position is tied to the active buffer; It is not availale when switching to an alternate buffer.
When reverting to the main buffer:
The cursor position originally occupied in the main buffer is restored, and the content of the alternate buffer is discarded.
This macro completes the it's execution in 1/100th of a second - That's with the maximum number of args & switches (on the slowest of my pc's, with a clock speed of 1.6Ghz). In the examples, a delay is effected between each expansion to allow time to observe the changes in output.
::: Cout cursor Macro. Author: T3RRY ::: Filename: Cout.bat
::: OS requirement: Windows 10
::: Purpose: Facilitate advanced console display output with the easy use of Virtual terminal codes
::: Uses a macro function to effect display without users needing to memorise or learn specific
::: virtual terminal sequences.
::: Enables output of text in 255 bit color at absolute or relative Y;X positions.
::: Allows cursor to be hidden or shown during and after text output. See help for more info.
@Echo off & Setlocal EnableExtensions
============================================== :# Usage
If not "%~1" == "" Echo/%~1.|findstr /LIC:"/?" > nul && (
Cls & Mode 1000,50 & Color 30
If "%~2" == "DE" ( Color 04 & Echo/ --- Delayed expansion detected^^^! Must not be enabled prior to calling %~n0 ---&( Echo/n|choice /n /C:o 2> nul ))
If not Exist "%TEMP%\%~n0helpfile.~tmp" (For /F "Delims=" %%G in ('Type "%~f0"^| Findstr.exe /BLIC:":::" 2^> nul ')Do (
For /F "Tokens=2* Delims=[]" %%v in ("%%G")Do Echo(^|%%v^|
))>"%TEMP%\%~n0helpfile.~tmp"
Type "%TEMP%\%~n0helpfile.~tmp" | More
timeout /T 60 > nul
Color 07
If "%~2" == "DE" (Exit)Else Exit /B 0
)
If "!![" == "[" Call "%~f0" "/?" "DE"
:::[=====================================================================================================================]
:::[ cout /? ]
:::[ %COUT% Cursor output macro. ]
:::[ * Valid Args for COUT: {/Y:Argvalue} {/X:Argvalue} {/S:Argvalue} {/C:Argvalue} ]
:::[ - Args Must be encased in curly braces. Arg order does not matter ; Each Arg is optional. ]
:::[ * Valid Switches for COUT: /Save /Alt /Main ]
:::[ /Save - Stores the Y and X position at the start of the current expansion to .lY and .lX variables ]
:::[ /Alt - Switch console to alternate screen Buffer. Persists until /Main switch is used. ]
:::[ /Main - Restore console to main screen Buffer. Console default is the main buffer. ]
:::[ ]
:::[ USAGE: ]
:::[ * ArgValue Options ; '#' is an integer: ]
:::[ {/Y:up|down|#} {/Y:up#|down#|#} {/Y:#up|#down|#} {/X:left|right|#} {/X:left#|right#|#} {/X:#left|#right|#} ]
:::[ * note: {/Y:option} {/X:option} - 1 option only per Arg. ]
:::[ - directions: 'up' 'down' 'left' 'right' are relative to the cursors last position. ]
:::[ - /Y and /X options - #direction or direction#: ]
:::[ Positions the cursor a number of cells from the current position in the given direction. ]
:::[ Example; To move the cursor 5 rows up in the same column, without displaying any new text: ]
:::[ %COUT%{/Y:5up} ]
:::[ - '#' (Absolute position) is the column number {/X:#} or row number {/Y:#} the cursor ]
:::[ * Integers for absolute positions contained in variables must be Expanded: {/Y:%varname%} ]
:::[ is to be positioned at, allowing cursor position to be set on single or multiple axis. ]
:::[ * Absolute Y and X positions capped at line and column maximum of the console display. ]
:::[ * Exceeding the maximum Y positions the cursor at the start of the last line in the console display. ]
:::[ * Exceeding the maximum X positions the cursor at the start of the next line ]
:::[ ]
:::[ {/S:Output String} {/S:(-)Output String} {/S:Output String(+)} {/S:Output String(K)} {/S:Output String(.#.)} ]
:::[ * note: (-) Hide or (+) Show the Cursor during output of the string. ]
:::[ (K) Clears the row of text from the position (K) occurs. ]
:::[ Example; Delete 5 characters from the current row to the right of the curser: ]
:::[ %COUT%{/S:(.5.)} ]
:::[ {/C:VTcode} {/C:VTcode-VTcode} {/C:VTcode-VTcode-VTcode} ]
:::[ * note: Chain multiple graphics rendition codes using '-' ]
:::[ See: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#text-formatting ]
:::[ See also: https://www.rapidtables.com/web/color/RGB_Color.html ]
:::[=====================================================================================================================]
============================================== :# PreScript variable definitions
================== :# Screen Dimensions
Mode 100,30 & Cls
rem /* Get screen dimensions [lines] [columns]. Must be done before delayed expansion is enabled. */
For /F "tokens=1,2 Delims=:" %%G in ('Mode')Do For %%b in (%%H)Do For %%a in (%%G)Do Set "%%a=%%b"
rem /* NON ENGLISH VERSIONS : Users will need to Adjust to their desired console size */
If not defined columns (Set "columns=100"& Set "lines=30")
rem /* generate Vitual Terminal Escape Control Character */
For /F %%a in ( 'Echo prompt $E ^| cmd' )Do Set "\E=%%a"
rem /* https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences */
(Set \n=^^^
%= Newline variable for macro definitions. DO NOT MODIFY this line or above 2 lines. =%)
rem /* Cursor position codes - https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#simple-cursor-positioning */
Set "left=D"&Set "right=C"&Set "up=A"&set "down=B"
For /L %%n in (1 1 60)Do (Set "%%ndown=[%%nB"&Set "down%%n=[%%nB"& set "%%nup=[%%nA"&Set "up%%n=[%%nA")
For /L %%n in (1 1 240)Do (Set "%%nleft=[%%nD"&Set "left%%n=[%%nD"&set "%%nright=[%%nC"&set "right%%n=[%%nC")
Set COUT=For %%n in (1 2)Do If %%n==2 ( %\n%
If "!Args:}=!" == "!Args!" (CLS^&Echo/Usage Error. Args must be enclosed in curly braces ^& Exit /B 0) %\n%
Set ".Y=" ^& Set ".X=" ^& Set ".Str=" ^& Set ".C=" %\n%
Set "Arg1=" ^& Set "Arg2=" ^& Set "Arg3=" ^& Set "Arg4=" %\n%
For /F "Tokens=1,2,3,4 Delims={}" %%1 in ("!Args!")Do ( %\n%
Set "Arg1=%%~1" %\n%
Set "Arg2=%%~2" %\n%
Set "Arg3=%%~3" %\n%
Set "Arg4=%%~4" %\n%
) %\n%
If not "!Args:/Save=!" == "!Args!" (%\n%
Set "Cpos=" ^&Set "Char="%\n%
For /L %%l in (2 1 12)Do (%\n%
If not "!Char!" == "R" (%\n%
^<nul set /p "=%\E%[6n" %\n%
FOR /L %%z in (1 1 %%l) DO pause ^< CON ^> NUL%\n%
Set "Char=;"%\n%
for /F "tokens=1 skip=1 delims=*" %%C in ('"REPLACE /W ? . < con"') DO (Set "char=%%C")%\n%
If "!Cpos!" == "" (Set "Cpos=!Char!")Else set "Cpos=!Cpos!!char:R=!"%\n%
)%\n%
)%\n%
For /F "tokens=1,2 Delims=;" %%X in ("!Cpos!")Do Set ".lY=%%X" ^& Set ".LX=%%Y" %\n%
)%\n%
For %%i in (1 2 3 4)Do For %%S in (Y X C S)Do If not "!Arg%%i!" == "" ( %\n%
If not "!Arg%%i:/%%S:=!" == "!Arg%%i!" ( %\n%
Set "Arg%%i=!Arg%%i:/%%S:=!" %\n%
For %%v in ("!Arg%%i!")Do ( %\n%
If "%%S" == "Y" ( %\n%
If Not "!%%~v!" == "" ( %\n%
Set ".Y=%\E%!%%~v!" %\n%
)Else ( %\n%
Set /A ".Y=!Arg%%i!" %\n%
If !.Y! GEQ !Lines! (Set /A ".Y=Lines-1") %\n%
Set ".Y=%\E%[!.Y!d" %\n%
)) %\n%
If "%%S" == "X" ( %\n%
If Not "!%%~v!" == "" ( %\n%
Set ".X=%\E%!%%~v!" %\n%
)Else ( %\n%
Set /A ".X=!Arg%%i!" %\n%
If !.X! GEQ !Columns! (Set ".X=1"^& Set ".Y=%\E%!Down!") %\n%
Set ".X=%\E%[!.X!G" %\n%
)) %\n%
If "%%S" == "C" ( %\n%
Set ".C=%\E%[!Arg%%i!" %\n%
Set ".C=!.C:-=m%\E%[!" %\n%
Set ".C=!.C!m" %\n%
) %\n%
If "%%S" == "S" ( %\n%
Set ".Str=!Arg%%i!" %\n%
Set ".Str=!.Str:(-)=%\E%[?25l!" %\n%
Set ".Str=!.Str:(+)=%\E%[?25h!" %\n%
Set ".Str=!.Str:(K)=%\E%[K!" %\n%
Set ".Str=!.Str:(.=%\E%[!" %\n%
Set ".Str=!.Str:.)=P!" %\n%
) %\n%
))) %\n%
If not "!Args:/Main=!" == "!Args!" ( %\n%
^< nul Set /P "=%\E%[?1049l!.Y!!.X!!.C!!.Str!%\E%[0m" %\n%
)Else If not "!Args:/Alt=!" == "!Args!" ( %\n%
^< nul Set /P "=%\E%[?1049h!.Y!!.X!!.C!!.Str!%\E%[0m" %\n%
)Else ( ^< nul Set /P "=!.Y!!.X!!.C!!.Str!%\E%[0m" ) %\n%
)Else Set Args=
rem /* Simple subsecond delay macro. Uses call to a non existentent label # number of times to delay script execution. */
For /F "tokens=1,2 delims==" %%G in ('wmic cpu get maxclockspeed /format:value')Do Set /A "%%G=%%H/20" 2> nul
If not defined Maxclockspeed Set "Maxclockspeed=200"
Set "Hash=#"& Set "delay=(If "!Hash!" == "#" (Set /A "Delay.len=Maxclockspeed")Else Set "Delay.len=#")& For /L %%i in (1 1 !Delay.Len!)Do call :[_false-label_] 2> Nul"
============================================== :# Script Body [Demo]
rem /* Enable Delayed Expansion after macro definiton in order to expand macro. */
Setlocal EnableDelayedExpansion & CD "%TEMP%"
rem /* Usage examples */
%COUT%{/X:10}{/Y:5}{/C:34}{"/S:(-)hello there^^^!"}
%Delay%
rem /* Example use of mixed foreground / background color and other graphics rendition properties */
%COUT%{"/C:31-1-4-48;2;0;80;130"}{/S:Bye for now.}{/Y:down}
%Delay%
%COUT%{/Y:up}{/C:35}{/S:again}{/X:16}
%Delay%
%COUT%{"/S:(K)^_^"}{/X:right}{/C:32}{/Y:down} /Save
%Delay%
rem /* Switch to Alternate screen buffer: /Alt */
%COUT%{"/S:(-)(K)^_^"}{/X:.lX+1}{/Y:6}{/C:33}{/Y:down} /Alt
%COUT%{"/S:They'll be back"}{/Y:down}{/X:left}{/C:31}
%Delay%
rem /* Return to Main screen buffer: /Main */
%COUT%{/X:3left}{/Y:5up}{"/S:That's all folks."} /Save /Main
%Delay%
rem /* restore cursor position /Save .lX value with +7 offset ; Overwrite all and delete 6 following characters:(.6.) ; restore cursor: (+) */
%COUT%{/X:10left}{/S:How(.6.)(+)}{/C:32}
rem /* The same as the above line using VT codes manually. */
::: <nul Set /P "=%\E%[10D%\E%[32mHow%\E%[6P%\E%[?25l"
%Delay%
%COUT%{/Y:100}
Endlocal
Goto :eof