1
0

Initial commit

This commit is contained in:
Herwin Bozet (NibblePoker) 2021-07-01 21:24:06 +02:00
commit 83f1a1f3e1
7 changed files with 590 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.exe
*.pbi.cfg
*.pb.cfg
*.txt
.idea/

83
AinsiEscapeCode.pbi Normal file
View File

@ -0,0 +1,83 @@
;{- Code Header
; ==- Basic Info -================================
; Name: AinsiEscapeCode.pbi
; Version: N/A
; Author: Herwin Bozet
;
; ==- Compatibility -=============================
; Compiler version: PureBasic 5.70 (x86/x64)
; Operating system: Windows 10 21H1 (Previous versions untested)
;
; ==- Links & License -===========================
; License: Unlicense
;}
;- Module Declaration
DeclareModule AinsiEscapeCode
;-> Documentation
; * https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
; * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
; * https://en.wikipedia.org/wiki/ANSI_escape_code
;-> Constants
;-> > Control characters
#BEL$ = #BEL$
#BS$ = #BS$
#HT$ = #TAB$
#LF$ = #LF$
#FF$ = #FF$
#CR$ = #CR$
#ESC$ = #ESC$
;-> > Operators
#CSI$ = "["
#OSC$ = "]"
;-> > Cursor
#CUU$ = "A" ; Cursor Up by n
#CUD$ = "B" ; Cursor Down by n
#CUF$ = "C" ; Cursor Forward (Right) by n
#CUB$ = "D" ; Cursor Backward (Left) by n
#CNL$ = "E" ; Cursor Next Line by n
#CPL$ = "F" ; Cursor Previous Line by n
;-> Buffer ?
#ED$ = "J" ; Erase in Display
#ED_0 = 0 ; 0 from cursor to end of screen
#ED_1 = 1 ; 1 from cursor to beginning of the screen
#ED_2 = 2 ; 2 clear entire screen (May move to 0:0)
#ED_3 = 3 ; 3 clear entire screen and lines saved in the scrollback buffer. (Does not work on Windows as of 21H1 in Windows Terminal)
;-> Queries
#DECXCPR$ = "6n" ; Emit the cursor position as: ESC [ <r> ; <c> R Where <r> = cursor row and <c> = cursor column
#DECSC$ = "7" ; Save Cursor Position in Memory**
#DECSR$ = "8" ; Restore Cursor Position from Memory**
#RI$ = "M" ; Reverse Index Performs the reverse operation of \n, moves cursor up one line, maintains horizontal position, scrolls buffer if necessary*
; https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
; https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#cursor-positioning
#CHA$ = "G" ; Cursor Horizontal Absolute Cursor moves to <n>th position horizontally in the current line
#VPA$ = "d" ; Vertical Line Position Absolute Cursor moves to the <n>th position vertically in the current column
#CUP$ = "H" ; Cursor Position *Cursor moves to <x>; <y> coordinate within the viewport, where <x> is the column of the <y> line
#HVP$ = "f" ; Horizontal Vertical Position *Cursor moves to <x>; <y> coordinate within the viewport, where <x> is the column of the <y> line
#ANSISYSSC_DECSC$ = "s" ; Save Cursor Ansi.sys emulation **With no parameters, performs a save cursor operation like DECSC
#ANSISYSSC_DECRC$ = "u" ; Restore Cursor Ansi.sys emulation **With no parameters, performs a restore cursor operation like DECRC
EndDeclareModule
;- Module Definition
Module AinsiEscapeCode
; Nothing...
EndModule

188
Console.pbi Normal file
View File

@ -0,0 +1,188 @@
;{- Code Header
; ==- Basic Info -================================
; Name: Console.pbi
; Version: N/A
; Author: Herwin Bozet
;
; ==- Compatibility -=============================
; Compiler version: PureBasic 5.70 (x86/x64)
; Operating system: Windows 10 21H1 (Previous versions untested)
;
; ==- Links & License -===========================
; License: Unlicense
; MSDN: https://docs.microsoft.com/en-us/windows/console/console-functions
;}
;- Module declaration
DeclareModule Console
;-> Structures
Structure CursorPosition
X.l
Y.l
EndStructure
;-> Constants
CompilerIf #PB_Compiler_Console
#IsConsoleApp = #True
CompilerElse
#IsConsoleApp = #False
CompilerEndIf
; https://docs.microsoft.com/en-us/windows/console/setconsolemode
; https://docs.microsoft.com/en-us/windows/console/getconsolemode
; For input handles
#ENABLE_PROCESSED_INPUT = $0001
#ENABLE_LINE_INPUT = $0002
#ENABLE_ECHO_INPUT = $0004
#ENABLE_WINDOW_INPUT = $0008
#ENABLE_MOUSE_INPUT = $0010
#ENABLE_INSERT_MODE = $0020
#ENABLE_QUICK_EDIT_MODE = $0040
#ENABLE_VIRTUAL_TERMINAL_INPUT = $0200
; For buffer/output? handles
#ENABLE_PROCESSED_OUTPUT = $0001
#ENABLE_WRAP_AT_EOL_OUTPUT = $0002
#ENABLE_VIRTUAL_TERMINAL_PROCESSING = $0004
#DISABLE_NEWLINE_AUTO_RETURN = $0008
#ENABLE_LVB_GRID_WORLDWIDE = $0010
;-> Typedefs Macros
; DWORD and HANDLE types from MS libraries
Macro DWORD : l : EndMacro
Macro HANDLE : i : EndMacro
;-> Procedure Declaration
Declare.s GetConsoleTitle(MaxLength.i = 255)
Macro SetConsoleTitle(Title) : ConsoleTitle(Title) : EndMacro
Declare.b AddConsoleModeFlag(Flags.DWORD = #Null, ConsoleHandle.Console::HANDLE = #Null)
Declare.b RemoveConsoleModeFlag(Flags.DWORD = #Null, ConsoleHandle.Console::HANDLE = #Null)
; May fail on Windows if the program's output is piped.
Macro EnableVirtualTerminalProcessing() : Console::AddConsoleModeFlag(Console::#ENABLE_VIRTUAL_TERMINAL_PROCESSING, #Null) : EndMacro
Macro DisableVirtualTerminalProcessing() : Console::RemoveConsoleModeFlag(Console::#ENABLE_VIRTUAL_TERMINAL_PROCESSING, #Null) : EndMacro
;-> Generic Macros
; Macro for #IsConsoleApp
Macro IsConsoleApp() : #IsConsoleApp : EndMacro
; https://docs.microsoft.com/en-us/windows/console/getstdhandle
Macro GetOutputHandle() : GetStdHandle_( #STD_OUTPUT_HANDLE ) : EndMacro
Macro GetInputHandle() : GetStdHandle_( #STD_INPUT_HANDLE ) : EndMacro
Macro GetErrorHandle() : GetStdHandle_( #STD_ERROR_HANDLE ) : EndMacro
EndDeclareModule
;- Module Definition
Module Console
;-> Compiler Directives
EnableExplicit
;-> Procedure Definition
Procedure.s GetConsoleTitle(MaxLength.i = 255)
Protected *ConsoleTitleBuffer = AllocateMemory(MaxLength)
Protected ConsoleTitle$ = #Null$
If *ConsoleTitleBuffer
Protected ConsoleTitleLength.l = GetConsoleTitle_(*ConsoleTitleBuffer, MaxLength)
ConsoleTitle$ = PeekS(*ConsoleTitleBuffer, ConsoleTitleLength)
FreeMemory(*ConsoleTitleBuffer)
EndIf
ProcedureReturn ConsoleTitle$
EndProcedure
Procedure.b AddConsoleModeFlag(Flags.DWORD = #Null, ConsoleHandle.Console::HANDLE = #Null)
Protected ConsoleMode.DWORD = 0
If ConsoleHandle = #Null
ConsoleHandle = GetOutputHandle()
EndIf
If GetConsoleMode_(ConsoleHandle, @ConsoleMode)
ConsoleMode = ConsoleMode | Flags
If SetConsoleMode_(ConsoleHandle, ConsoleMode)
ProcedureReturn #True
EndIf
EndIf
Debug GetLastError_()
ProcedureReturn #False
EndProcedure
Procedure.b RemoveConsoleModeFlag(Flags.DWORD = #Null, ConsoleHandle.Console::HANDLE = #Null)
Protected ConsoleMode.DWORD = 0
If ConsoleHandle = #Null
ConsoleHandle = GetOutputHandle()
EndIf
If GetConsoleMode_(ConsoleHandle, @ConsoleMode)
ConsoleMode = ConsoleMode & ~Flags
If SetConsoleMode_(ConsoleHandle, ConsoleMode)
ProcedureReturn #True
EndIf
EndIf
Debug GetLastError_()
ProcedureReturn #False
EndProcedure
; ???
EndModule
;- Tests
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
If Not OpenConsole("Console.pbi")
Debug "Can't open console !"
End 1
EndIf
;Debug "Console title: "+Console::GetConsoleTitle()
If Console::EnableVirtualTerminalProcessing()
PrintN("Virtual terminal processing activated !"+#CRLF$)
Else
PrintN("Virtual terminal processing couldn't be activated !"+#CRLF$)
EndIf
Console::CursorSave()
PrintN("#####")
PrintN("#####")
PrintN("#####")
Console::CursorUp()
Print("A")
Console::CursorRestore()
Print("B")
If Console::DisableVirtualTerminalProcessing()
PrintN("Virtual terminal processing deactivated !"+#CRLF$)
Else
PrintN("Virtual terminal processing couldn't be deactivated !"+#CRLF$)
EndIf
Print("Press enter key to exit...")
Input()
CompilerEndIf

View File

@ -0,0 +1,68 @@
;{- Code Header
; ==- Basic Info -================================
; Name: VirtualTerminal/Basic.pb
; Author: Herwin Bozet
;
; ==- Links & License -===========================
; License: Unlicense
;}
;- Compiler Directives
EnableExplicit
XIncludeFile "../../VirtualTerminal.pbi"
CompilerIf #PB_Compiler_ExecutableFormat <> #PB_Compiler_Console
CompilerWarning "You should preferably compile the example as a console app !"
CompilerEndIf
;- Code
; Safely openning the console
If OpenConsole("My first title")
If Not VirtualTerminal::EnableVirtualTerminalProcessing()
VirtualTerminal::WriteErrorN("Unable to activate virtual terminal processing, output is likely piped !")
CloseConsole()
End 2
EndIf
Else
Debug "Can't open console !"
End 1
EndIf
; Clearing the console
VirtualTerminal::ClearDisplay(AinsiEscapeCode::#ED_2) ; 2 or +ClearDisplayFull() also works just fine
VirtualTerminal::CursorTo(0, 0)
; Intro
VirtualTerminal::WriteOutputN("-===- Welcome to the 'Basic' demo for 'VirtualTerminal' -===-")
VirtualTerminal::WriteOutputN("Press enter to start...")
Input()
; Basic info
VirtualTerminal::WriteOutputN("-=- Basic info -=-")
VirtualTerminal::WriteOutputN("Terminal size: "+Str(VirtualTerminal::GetTerminalWidth())+"x"+Str(VirtualTerminal::GetTerminalHeight()))
VirtualTerminal::WriteOutputN("Press enter to continue...")
Input()
; Title demo
VirtualTerminal::WriteOutputN("-=- Title -=-")
VirtualTerminal::WriteOutputN("Press enter to change the title...")
Input()
VirtualTerminal::SetWindowTitle("My new title")
; ???
; End
VirtualTerminal::WriteOutputN("-=- End of the demo -=-")
Print("Press enter to clear the terminal and exit...")
Input()
VirtualTerminal::ClearDisplayFull()
VirtualTerminal::CursorTo(0, 0)
; Quitting safely
VirtualTerminal::DisableVirtualTerminalProcessing()
CloseConsole()
End 0

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

215
VirtualTerminal.pbi Normal file
View File

@ -0,0 +1,215 @@
;{- Code Header
; ==- Basic Info -================================
; Name: VirtualTerminal.pbi
; Version: N/A
; Author: Herwin Bozet
;
; ==- Compatibility -=============================
; Compiler version: PureBasic 5.70 (x86/x64)
; Operating system: Windows 10 21H1 (Previous versions untested)
;
; ==- Links & License -===========================
; License: Unlicense
;}
;- Compiler directives
XIncludeFile "./Console.pbi"
XIncludeFile "./AinsiEscapeCode.pbi"
;- Module declaration
DeclareModule VirtualTerminal
;-> Macros
;-> > Console.pbi macros
Macro IsConsoleApp() : Console::#IsConsoleApp : EndMacro
Macro GetOutputHandle() : Console::GetOutputHandle() : EndMacro
Macro GetInputHandle() : Console::GetInputHandle() : EndMacro
Macro GetErrorHandle() : Console::GetErrorHandle() : EndMacro
;-> > Platform-dependant Macros
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
Macro EnableVirtualTerminalProcessing() : Console::EnableVirtualTerminalProcessing() : EndMacro
Macro DisableVirtualTerminalProcessing() : Console::DisableVirtualTerminalProcessing() : EndMacro
CompilerElse
Declare.b EnableVirtualTerminalProcessing()
Declare.b DisableVirtualTerminalProcessing()
CompilerEndIf
;-> > Generic Macros
Macro WriteOutput(Message) : Print(Message) : EndMacro
Macro WriteOutputN(Message) : PrintN(Message) : EndMacro
Macro WriteErrorN(Message) : ConsoleError(Message) : EndMacro
;-> > Ainsi Escape Code Macros
;-> > > Cursor
Macro CursorUp() : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CUU$) : EndMacro
Macro CursorDown() : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CUD$) : EndMacro
Macro CursorRight() : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CUF$) : EndMacro
Macro CursorLeft() : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CUB$) : EndMacro
Macro CursorNextLine(Amount) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CNL$) : EndMacro
Macro CursorPreviousLine(Amount) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CPL$) : EndMacro
Macro CursorUpBy(Amount) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(Amount) + AinsiEscapeCode::#CUU$) : EndMacro
Macro CursorDownBy(Amount) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(Amount) + AinsiEscapeCode::#CUD$) : EndMacro
Macro CursorRightBy(Amount) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(Amount) + AinsiEscapeCode::#CUF$) : EndMacro
Macro CursorLeftBy(Amount) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(Amount) + AinsiEscapeCode::#CUB$) : EndMacro
Macro CursorLineDownBy(Amount) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(Amount) + AinsiEscapeCode::#CNL$) : EndMacro
Macro CursorLineUpBy(Amount) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(Amount) + AinsiEscapeCode::#CPL$) : EndMacro
Macro CursorToColumn(ColumnNumber) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(ColumnNumber) + AinsiEscapeCode::#CHA$) : EndMacro
Macro CursorToLine(LineNumber) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(LineNumber) + AinsiEscapeCode::#VPA$) : EndMacro
Macro CursorTo(X, Y) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(Y) + ";" + Str(X) + AinsiEscapeCode::#CUP$) : EndMacro
Macro CursorFlip() : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#RI$) : EndMacro
Macro CursorSave() : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#DECSC$) : EndMacro
Macro CursorRestore() : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#DECSR$) : EndMacro
;-> > > Others
Macro ClearDisplay(Mode = AinsiEscapeCode::#ED_0) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + Str(Mode) + AinsiEscapeCode::#ED$) : EndMacro
Macro ClearDisplayToEnd() : VirtualTerminal::ClearDisplay(AinsiEscapeCode::#ED_0) : EndMacro
Macro ClearDisplayToStart() : VirtualTerminal::ClearDisplay(AinsiEscapeCode::#ED_1) : EndMacro
Macro ClearDisplayFull() : VirtualTerminal::ClearDisplay(AinsiEscapeCode::#ED_2) : EndMacro
Macro ClearDisplayAbsolute() : VirtualTerminal::ClearDisplay(AinsiEscapeCode::#ED_3) : EndMacro
;-> > > Window
Macro SetWindowTitleAndIcon(Title) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#OSC$ + "0;" + Left(Title, 255) + AinsiEscapeCode::#BEL$) : EndMacro
Macro SetWindowTitle(Title) : VirtualTerminal::WriteOutput(#ESC$ + AinsiEscapeCode::#OSC$ + "2;" + Left(Title, 255) + AinsiEscapeCode::#BEL$) : EndMacro
;-> Procedure Declaration (See the "Platform-dependant Macros" section)
Declare.b GetCursorPosition(*CursorPosition.Console::CursorPosition, TimeoutMs.i = 1)
Declare.i GetTerminalWidth(RestorePosition.b = #True)
Declare.i GetTerminalHeight(RestorePosition.b = #True)
EndDeclareModule
;- Module Definition
Module VirtualTerminal
;-> Compiler Directives
EnableExplicit
;-> Procedure Definition
CompilerIf Not #PB_Compiler_OS = #PB_OS_Windows
; FIXME: May not work if piped, not sure !
Procedure.b EnableVirtualTerminalProcessing() : ProcedureReturn #True : EndIf
Procedure.b DisableVirtualTerminalProcessing() : ProcedureReturn #True : EndIf
CompilerEndIf
Procedure.b GetCursorPosition(*CursorPosition.Console::CursorPosition, TimeoutMs.i = 1)
Protected StartTime.q
Protected Input$ = #Null$
If *CursorPosition
WriteOutput(#ESC$ + AinsiEscapeCode::#CSI$ + AinsiEscapeCode::#DECXCPR$)
; Waiting for stuff to come in...
StartTime = ElapsedMilliseconds()
Repeat
Input$ = Inkey()
Until Input$ <> #Null$ Or ((TimeoutMs <> -1 And (ElapsedMilliseconds() - StartTime > TimeoutMs)) Or (TimeoutMs = -1))
Repeat
Protected NewChar$ = Inkey()
If NewChar$ <> #Null$
Input$ = Input$ + NewChar$
Else
Break
EndIf
ForEver
Protected.i ResponseCodeStart = FindString(Input$, "[") + 1
Input$ = Mid(Input$, ResponseCodeStart, FindString(Input$, "R", ResponseCodeStart) - ResponseCodeStart)
If FindString(Input$, ";") >= 2
*CursorPosition\Y = Val(StringField(Input$, 1, ";"))
*CursorPosition\X = Val(StringField(Input$, 2, ";"))
ProcedureReturn #True
EndIf
EndIf
ProcedureReturn #False
EndProcedure
Procedure.i GetTerminalWidth(RestorePosition.b = #True)
Define OriginalCursorPosition.Console::CursorPosition
Define MeasuringCursorPosition.Console::CursorPosition
If RestorePosition
VirtualTerminal::GetCursorPosition(@OriginalCursorPosition)
EndIf
VirtualTerminal::CursorTo(32767, 0)
VirtualTerminal::GetCursorPosition(@MeasuringCursorPosition)
If RestorePosition
VirtualTerminal::CursorTo(OriginalCursorPosition\X, OriginalCursorPosition\Y)
EndIf
ProcedureReturn MeasuringCursorPosition\X
EndProcedure
Procedure.i GetTerminalHeight(RestorePosition.b = #True)
Define OriginalCursorPosition.Console::CursorPosition
Define MeasuringCursorPosition.Console::CursorPosition
If RestorePosition
VirtualTerminal::GetCursorPosition(@OriginalCursorPosition)
EndIf
VirtualTerminal::CursorTo(0, 32767)
VirtualTerminal::GetCursorPosition(@MeasuringCursorPosition)
If RestorePosition
VirtualTerminal::CursorTo(OriginalCursorPosition\X, OriginalCursorPosition\Y)
EndIf
ProcedureReturn MeasuringCursorPosition\Y
EndProcedure
EndModule
;- Tests
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
If OpenConsole("Console.pbi")
If Not VirtualTerminal::EnableVirtualTerminalProcessing()
VirtualTerminal::WriteErrorN("Unable to activate virtual terminal processing, output is likely piped !")
CloseConsole()
End 2
EndIf
Else
Debug "Can't open console !"
End 1
EndIf
VirtualTerminal::ClearDisplay(AinsiEscapeCode::#ED_2)
VirtualTerminal::CursorTo(0, 0)
PrintN("-==- Title Bar -==-")
Print("Size: "+VirtualTerminal::GetTerminalWidth()+"x"+VirtualTerminal::GetTerminalHeight())
;Print("Press enter key to exit...")
Debug Input()
;FreeMemory(*Buffer)
VirtualTerminal::DisableVirtualTerminalProcessing()
CloseConsole()
End 0
CompilerEndIf

7
readme.md Normal file
View File

@ -0,0 +1,7 @@
# PB-ConsoleHelpers
A set of includes that should help you develop CLI applications that need to interact with the terminal and its buffers directly.
## License
[Unlicense](LICENSE)