Page 1 of 1

The weak spot of EnableGraphicalConsole()

Posted: Wed Jun 04, 2025 9:16 pm
by es_91
I tried implementing Ascii Art just to have a banner for a cli app.

Unlike just prompting, a 'graphical console' should know about the frame size of a cli client area. Just like the default may be 80x25 signs, the user, for example, can change that and enlarge or narrow down the cli output.

When using graphical functions on cli, like consoleLocate, we should know the size of that screen, but how to find out? Is there any trick in PB or does it need system functions?


Here is a short example of an ascii art generated from a font (fixedSys). You will notice that the output should crucially have info about the frame size, or else the graphical effect is broken.

Code: Select all


procedure  drawBanner  (  text$  )

;{
	
	;{
		
		protected  *img
		protected  *fnt
		
		protected  txSpanX
		protected  txSpanY
		
	;}
	
	
	*img  =  createImage  (  #pb_any,  
	                         1024,  
	                         1024  )
	
	createImage  (  #null,  
	                1,  
	                1  )
	
	*fnt  =  loadFont  (  #pb_any,  
	                      "FixedSys",  
	                      9  )
	
	if  (  startDrawing  (  imageOutput  (  *img  )  )  )
		
		;{
			
			if  (  (  *fnt  )  and  (  isFont  (  *fnt  )  )  )
				
				;{
					
					if  (  fontId  (  *fnt  )  )
						
						;{
							
							drawingFont  (  fontId  (  *fnt  )  )
							
							drawText  (  0,  
							             0, 
							             text$,  
							             #white,  
							             #black  )
							
							txSpanX  =  textWidth  (  text$  )
							txSpanY  =  textHeight  (  text$  )
							
						;}
						
					endIf
					
				;}
				
			endIf
			
			stopDrawing  (  )
			
			grabImage  (  *img,  
			              #null,  
			              0,  
			              0,  
			              txSpanX,  
			              txSpanY  )
			
		;}
		
	endIf
	
	
	if  (  startDrawing  (  imageOutput  (  #null  )  )  )
		
		;{
			
			enableGraphicalConsole  (  #true  )
			
			for  y  =  1  to  imageHeight  (  #null  )  -  1
			
			;{
				
				for  x  =  1  to  imageWidth  (  #null  )  -  1
				
				;{
					
					consoleLocate  (  x,  y  )
					
					if  (  point  (  x,  y  )  =  #white  )
						
						;{
							
							print  (  "#"  )
							
						;}
						
					endIf
					
				;}
				
				next
				
			;}
			
			next
			
		;}
		
	endIf
	
;}

endProcedure


if  (  openConsole  (  )  )
	
	;{
		
		drawBanner  (  "PureBasic v6"  )
		
		input  (  )
		
		closeConsole  (  )
		
	;}
	
endIf


IF YOUR SYSTEM HAS NO 'FIXEDSYS' FONT, please change the font used.

Re: The weak spot of EnableGraphicalConsole()

Posted: Thu Jun 19, 2025 5:58 pm
by benubi
No you can't do it with the PureBasic console library yet, I wrote an incomplete include for that. I can't test it on MacOS, though. ;/

I added a little abstraction to it, so that it can be used by all OS, instead of using Inkey() and Input() there are wrappers with Console prefix i.e. ConsoleInkey() and ConsoleSpecialKey(). This allows to use the F keys, and other special keys, it returns #PB_Key_XXX constants.

I haven't fixed a few things but I imagine it could be useful already.

There's a new glitch in PB6.21 on non-windows; this causes the lib to wait for ever for a (broken) message when cursor position is queried. On Linux/Mac use PB6.20, otherwise it should work in Windows. Windows lib can't detect successfully if VT emulation is enabled (has been partially included in W10+).

Code: Select all

; =====================================================
; = UpdateConsoleMetrics.pb          (c) 2025 by benubi
; =====================================================
; Author: benubi
; Version: 0.91
; OS: All
;
; Description: Additional console commands for Terminal window size query, cursor position query and 256/true color (RGB) support, and little special keys support [portable RawKey() replacement].
;
; The code uses WinAPI on Windows side and mostly VT Escape Sequneces in the other OS's, with a bit of ioctl_().
;
;
; ConsoleInkey() and ConsoleSpecialKey() are wrappers and mappers of all special keys (F1-F24 keys, Home, Cursor left/up/right/down, page etc)
;
; If ConsoleSpecialKey() = #PB_Key_F4 : Debug "User Pressed F4-Key" : EndIf
;
; This should make developping Console API's and GUI's more comfortable, because the #PB_Key_XYZ can be ported, even hitting the Return key might look different when using Inkey():
; You might prefer to use ConsoleSpecialKey() to check for #PB_Key_Return (instead of using constants Windows=Chr(13), Linux=Chr(10))
;
; +---------+
; | History |
; +---------+
;  19/Jun/2025 To-Do list
;  14/Jan/2025 Added: ConsoleBold(), ConsoleItalic(), ConsoleUnderline(), ConsoleStrikeOut(), ConsoleBlink() and ConsoleDim()
;  13/Jan/2025 ioctl_() and getyx_() implementation to get terminal metrics on non-Windows; fixes flickering, replaces original VT-escape-sequence approach where possible
;  03/Jan/2025 (description update, comments, bugfixes)
;  28/Jun/2024 (1st working version)
;
; To-do
; -----
; - Detection on Windows 10+ if VT escape sequences are enabled (reading from registry fails for now)
; - 256 color mode  
; - direct buffer access
; - alternate buffer 
; - MacOS testing
;
Define ConsoleBufferColumns
Define ConsoleBufferRows
Define ConsoleWidth
Define ConsoleHeight
Define ConsoleMaximumColorBits
Define ConsoleX
Define ConsoleY
Define ConsoleSizeChanged
Define ConsoleHandle
Define ConsoleVtEscapeSequences = Bool(#PB_Compiler_OS <> #PB_OS_Windows)
EnableExplicit

NewList InkeyBuffer.s()

CompilerIf Not Defined(CharLen8, #PB_Procedure)
  Procedure.l xRGB(r, g, b)
    r & 255
    g & 255
    b & 255
    Protected result.l = r | (g << 8) | (b << 16)
    ProcedureReturn result
  EndProcedure
CompilerEndIf

; Same across different OS
CompilerIf Not Defined(CharLen8, #PB_Procedure)
  Procedure CharLen8(byte.a) ; check UTF8 character byte length / check leading byte
    Protected t_length
    If byte < 128
      ProcedureReturn 1
    EndIf
    While byte & $80 = $80
      byte << 1
      t_length + 1
    Wend
    ProcedureReturn t_length
  EndProcedure
CompilerEndIf

Procedure ConsoleClosestColor(RGB.l)
  Structure con_dist
    number.i
    distance.i
  EndStructure
  Dim dist.con_dist(16)
  Protected i
  Restore console_rgb
  Protected r = RGB & 255
  Protected g = (RGB >> 8) & 255
  Protected b = (RGB >> 16) & 255
  Protected r2
  Protected g2
  Protected b2
  Protected tdist
  Protected t_indx
  Protected min_dist = 7000
  
  For i = 0 To 15
    Read.b r2
    Read.b g2
    Read.b b2
    tdist = Abs(r2 - r) + Abs(g2 - g) + Abs(b2 - b)
    If tdist < min_dist
      min_dist = tdist
      t_indx   = i
    EndIf
  Next
  
  ProcedureReturn t_indx
  DataSection
    console_rgb: ; default/cross platform "average"
    Data.b 0, 0, 0
    Data.b 0, 0, 127
    Data.b 0, 127, 0
    Data.b 0, 127, 127
    Data.b 127, 0, 0
    Data.b 127, 0, 127
    Data.b 127, 127, 0
    Data.b 127, 127, 127
    Data.b 64, 64, 64
    Data.b 0, 0, 255
    Data.b 0, 255, 0
    Data.b 0, 255, 255
    Data.b 255, 0, 0
    Data.b 255, 0, 255
    Data.b 255, 255, 0
    Data.b 128, 128, 128
    
  EndDataSection
EndProcedure

Procedure ConsoleMaximumColorBits() ; Return the maximum color bits for console colors (4,8,24) for 16,256 or "true color"
  Shared ConsoleMaximumColorBits
  ProcedureReturn ConsoleMaximumColorBits
EndProcedure
Procedure ConsoleColor8(FrontColor, BackColor) ; Change console colors using 8 bit 256 colors palette
  Shared ConsoleMaximumColorBits
  If ConsoleMaximumColorBits >= 8
    EnableGraphicalConsole(0)
    Protected *A = Ascii(Chr(27) + "[38;5;" + Str(FrontColor & 255) + "m" + Chr(27) + "[48;5;" + Str(BackColor & 255) + "m")
    WriteConsoleData(*A, MemorySize(*A))
    FreeMemory(*A)
  Else
    Protected r = (FrontColor) & 255
    Protected g = (FrontColor >> 8) & 255
    Protected b = (FrontColor >> 16) & 255
    Protected fg, bg
    fg = Bool(r > 127 Or b > 127 Or g > 127) * 8
    If r
      fg + 4
    EndIf
    If g
      fg + 2
    EndIf
    If b
      fg + 1
    EndIf
    r  = (BackColor) & 255
    g  = (BackColor >> 8) & 255
    b  = (BackColor >> 16) & 255
    bg = Bool(r > 127 Or g > 127 Or b > 127) * 8
    EnableGraphicalConsole(1)
    ConsoleColor(fg, bg)
  EndIf
EndProcedure
Procedure ConsoleColor24(FrontColor, BackColor) ; Change console colors using RGB values
  Shared ConsoleMaximumColorBits
  
  Protected r = (FrontColor) & 255
  Protected g = (FrontColor >> 8) & 255
  Protected b = (FrontColor >> 16) & 255
  Protected r2 = (BackColor) & 255
  Protected g2 = (BackColor >> 8) & 255
  Protected b2 = (BackColor >> 16) & 255
  
  If ConsoleMaximumColorBits >= 24
    EnableGraphicalConsole(0)
    
    Protected *A = Ascii(Chr(27) + "[38;2;" + Str(r) + ";" + Str(g) + ";" + Str(b) + "m" + Chr(27) + "[48;2;" + Str(r2) + ";" + Str(g2) + ";" + Str(b2) + "m")
    WriteConsoleData(*A, MemorySize(*A))
    FreeMemory(*A)
  Else
    Protected fg, bg
;
;     fg = Bool(r > 127 Or b > 127 Or g > 127) * 8
;     If r
;       fg + 4
;     EndIf
;     If g
;       fg + 2
;     EndIf
;     If b
;       fg + 1
;     EndIf
;
;     bg = Bool(r > 127 Or g > 127 Or b > 127) * 8
;     If r
;       bg + 4
;     EndIf
;     If g
;       bg + 2
;     EndIf
;     If b
;       bg + 1
;     EndIf
;
    
    fg = ConsoleClosestColor(FrontColor)
    bg = ConsoleClosestColor(BackColor)
    EnableGraphicalConsole(1)
    ConsoleColor(fg, bg)
    
  EndIf
EndProcedure
Procedure ConsoleWidth() ; Returns the current console width in columns aka characters
  Shared ConsoleWidth
  ProcedureReturn ConsoleWidth
EndProcedure
Procedure ConsoleHeight() ; Returns the current console height in lines
  Shared ConsoleHeight
  ProcedureReturn ConsoleHeight
EndProcedure
Procedure ConsoleBufferColumns() ; Returns the current console buffer maximum column count (may be out of view)
  Shared ConsoleBufferColumns
 ; ConsoleBufferColumns = ConsoleWidth()
  ProcedureReturn ConsoleBufferColumns
EndProcedure
Procedure ConsoleBufferRows() ; Returns the current console buffer maximum line count (may be out of view)
  Shared ConsoleBufferRows
 ; ConsoleBufferRows = ConsoleHeight()
  ProcedureReturn ConsoleBufferRows
EndProcedure
Procedure ConsoleBold(bool) ; bold font on/off (windows=hilight foreground on/off)
  Shared ConsoleVtEscapeSequences
  Shared ConsoleHandle
  
  If ConsoleVtEscapeSequences
    EnableGraphicalConsole(0)
    If bool
      Print(Chr(27) + "[1m")
    Else
      Print(Chr(27) + "[22m")
    EndIf
    EnableGraphicalConsole(1)
  Else
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Protected info.CONSOLE_SCREEN_BUFFER_INFO, wAttributes
      GetConsoleScreenBufferInfo_(ConsoleHandle, @Info)
      If bool
        wAttributes = info\wAttributes | 8
      Else
        wAttributes = info\wAttributes & ~8
      EndIf
      SetConsoleTextAttribute_(ConsoleHandle, wAttributes)
    CompilerEndIf
  EndIf
EndProcedure
Procedure ConsoleItalic(bool) ; italic font on/off (non-Windows only)
  Shared ConsoleVtEscapeSequences
  If ConsoleVtEscapeSequences
    EnableGraphicalConsole(0)
    If bool
      Print(Chr(27) + "[3m")
    Else
      Print(Chr(27) + "[23m")
    EndIf
    EnableGraphicalConsole(1)
  EndIf
EndProcedure
Procedure ConsoleUnderline(bool) ; underline font on/off (non-Windows only)
  Shared ConsoleVtEscapeSequences
  
  If ConsoleVtEscapeSequences
    EnableGraphicalConsole(0)
    If bool
      Print(Chr(27) + "[4m")
    Else
      Print(Chr(27) + "[24m")
    EndIf
    EnableGraphicalConsole(1)
  Else
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
;       Protected Mode
;       Shared ConsoleHandle
;       GetConsoleMode_(ConsoleHandle,@Mode)
;       Mode = (Mode & ~ #COMMON_LVB_UNDERSCORE) | (Bool(bool) * #COMMON_LVB_UNDERSCORE)
;       SetConsoleTextAttributes_(ConsoleHandle, Mode)
    CompilerEndIf
  EndIf
EndProcedure
Procedure ConsoleBlink(level) ; blink 0: off 1: normal speed 2: fast (non-Windows only)
  Shared ConsoleVtEscapeSequences
  If ConsoleVtEscapeSequences
    EnableGraphicalConsole(0)
    If level = 0
      Print(Chr(27) + "[25m") ; off
    ElseIf level = 1
      Print(Chr(27) + "[5m") ; normal speed
    ElseIf level = 2
      Print(Chr(27) + "[6m") ; blink fast
    Else
      Print(Chr(27) + "[25m") ;off
    EndIf
    EnableGraphicalConsole(1)
  EndIf
EndProcedure
Procedure ConsoleStrikeOut(bool) ; strike-out font on/off (non-Windows only)
  Shared ConsoleVtEscapeSequences
  If ConsoleVtEscapeSequences
    EnableGraphicalConsole(0)
    If bool
      Print(Chr(27) + "[9m")
    Else
      Print(Chr(27) + "[29m")
    EndIf
    EnableGraphicalConsole(1)
  EndIf
EndProcedure
Procedure ConsoleDim(bool) ; dim/faint font on/off (non-Windows only)
  Shared ConsoleVtEscapeSequences
  If ConsoleVtEscapeSequences
    EnableGraphicalConsole(0)
    If bool
      Print(Chr(27) + "[2m")
    Else
      Print(Chr(27) + "[22m")
    EndIf
    EnableGraphicalConsole(1)
  EndIf
EndProcedure

;
; OS specific commands
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  
  ; Windows based
  #DISABLE_NEWLINE_AUTO_RETURN        = $0008
  #ENABLE_VIRTUAL_TERMINAL_INPUT      = $0200
  #ENABLE_PROCESSED_OUTPUT            = $0001
  #ENABLE_VIRTUAL_TERMINAL_PROCESSING = $0004
  
  #ENABLE_REGISTERY_VT_INPUT = 0
  
  Procedure GetRegLong(hKey, strValueName$, defaultValue = 0)
    Protected vres = defaultValue
    Protected type   = #REG_DWORD
    Protected cbsize = SizeOf(INTEGER)
    Protected err = RegQueryValueEx_(hKey, strValueName$, #Null, @type ,  @vres, @cbsize)
;     Debug "Err="+err
;     Debug "type="+type
;     Debug "cbsize="+cbsize
;     Debug "vres="+vres 
    If err = #ERROR_SUCCESS
      Debug "success"
      ProcedureReturn vres
    Else
      Debug "fail"
      ProcedureReturn defaultValue
    EndIf
    
  EndProcedure
  
  Procedure UpdateConsoleSize() ; Update console size values and returns 1 on resize or 0 on no changes
    Shared ConsoleWidth
    Shared ConsoleHeight
    Shared ConsoleBufferColumns
    Shared ConsoleBufferRows
    Shared ConsoleHandle
    
    Protected Changed = 0
    
    If Not ConsoleHandle ; Get console std output handle
      ConsoleHandle = GetStdHandle_( - 12)
      changed       = 1
    EndIf
    
    ; Retrieve Windows console screen buffer info
    Protected Info.CONSOLE_SCREEN_BUFFER_INFO
    GetConsoleScreenBufferInfo_(ConsoleHandle, @info)
    
    ; Check for differences and update values
    ; - Console width:
    If ConsoleWidth <> 1 + info\srWindow\right - info\srWindow\left
      ;Debug "changed width"
      ;Debug "old="+ConsoleWidth
      ConsoleWidth = 1 + info\srWindow\right - info\srWindow\left
      changed      = 1
      ;Debug "new="+ConsoleWidth
    EndIf
    ; - Console height:
    If ConsoleHeight <> 1 + info\srWindow\bottom - info\srWindow\top
      ;Debug "changed height"
      ;Debug "old="+ConsoleHeight
      ConsoleHeight = 1 + info\srWindow\bottom - info\srWindow\top
      changed       = 1
      ;Debug "new="+ConsoleHeight
    EndIf
    ; - Buffer columns:
    If ConsoleBufferColumns <> info\dwSize\x
      ;Debug "changed col"
      ;Debug "old="+ConsoleBufferColumns
      ConsoleBufferColumns = info\dwSize\x
      changed              = 1
      Debug "new=" + ConsoleBufferColumns
    EndIf
    ; - Buffer rows:
    If ConsoleBufferRows <> info\dwSize\y
      ;Debug "changed rows"
      ;Debug "old="+ConsoleBufferRows
      ConsoleBufferRows = info\dwSize\y
      changed           = 1
      ;Debug "new="+ConsoleBufferRows
    EndIf
    
    ; return update result
    ProcedureReturn changed
  EndProcedure
  Procedure ConsoleDefaultColors() ; Reset default colors (7,0)
    EnableGraphicalConsole(1)
    ConsoleColor(7, 0)
  EndProcedure
  Procedure ConsoleX() ; Returns current cursor row (x) starting with 0
    Shared ConsoleX
    Shared ConsoleHandle
    Protected Info.CONSOLE_SCREEN_BUFFER_INFO
    GetConsoleScreenBufferInfo_(ConsoleHandle, @info)
    ConsoleX = info\dwCursorPosition\x
    ProcedureReturn ConsoleX
  EndProcedure
  Procedure ConsoleY() ; Returns current cursor line (y) starting with 0
    Shared ConsoleY
    Shared ConsoleHandle
    Protected Info.CONSOLE_SCREEN_BUFFER_INFO
    GetConsoleScreenBufferInfo_(ConsoleHandle, @info)
    ConsoleY = info\dwCursorPosition\y
    ProcedureReturn ConsoleY
  EndProcedure
  
  Procedure InitConsoleMetrics() ; Init console after OpenConsole()
    Shared ConsoleMaximumColorBits
    Shared ConsoleHandle
    Shared ConsoleVtEscapeSequences
    Protected mode.l, result
    SetEnvironmentVariable("TERM", "TRUE")
    result = UpdateConsoleSize()
    
    If OSVersion() < #PB_OS_Windows_8_1
      ConsoleMaximumColorBits = 4
    Else
      ConsoleMaximumColorBits = 24
      GetConsoleMode_(ConsoleHandle, @mode)
      
      ; enable VT input mode for escape sequences
      ;Mode = Mode | #ENABLE_VIRTUAL_TERMINAL_INPUT | #DISABLE_NEWLINE_AUTO_RETURN | #ENABLE_PROCESSED_OUTPUT | #ENABLE_VIRTUAL_TERMINAL_PROCESSING ;
      Mode = Mode | #ENABLE_VIRTUAL_TERMINAL_PROCESSING ;
      SetConsoleMode_(ConsoleHandle, mode)
      GetConsoleMode_(ConsoleHandle, @mode)
      If Mode & #ENABLE_VIRTUAL_TERMINAL_INPUT = 0
    ;;  If Mode & #ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0
    ; If Mode & 7 = 0
        If GetRegLong(#HKEY_CURRENT_USER, "Console\UseDx") 
          Debug "Set VT input mode UseDx!!"
          
        Else
          ConsoleMaximumColorBits = 4
          Debug "Set VT input mode for sequence: FAILED :("
        EndIf
      Else
        ConsoleVtEscapeSequences = #True
        Debug "Set VT input mode for sequence: SUCCESS"
      EndIf
    EndIf
  EndProcedure
  
CompilerElse
  ; Linux based - untested on MacOS & AmigaOS
  #STDOUT_FILENO = 0
  #STDIN_FILENO  = 1
  #STDERR_FILENO = 2
  
  #TIOCGWINSZ = $5413 ; Thank you ChatGPT :|
  
  Structure _winsize
    ws_row.u
    ws_col.u
    ws_xpixel.u
    ws_ypixel.u
      ; struct winsize {
;     unsigned short ws_row;
;     unsigned short ws_col;
;     unsigned short ws_xpixel;   /* unused */
;     unsigned short ws_ypixel;   /* unused */
; };
  EndStructure
  
  
  DataSection
    ansi_query_cursor:
    Data.a Chr(27) + "[6n"
    reset_console_colors:
    Data.a Chr(27) + "[0m"
  EndDataSection
  
  ; internal old/backward compatibility (VT escape sequences only)
  Procedure$ _vt_queryCursorPos() ; VT escape sequences
    Shared InkeyBuffer()
    Shared ConsoleX, ConsoleY
    
    Protected Dim buff.b(16)
    Protected buff_len = 0
    Protected buff_size = 16
    Protected re$, ch, has_esc
    EnableGraphicalConsole(0)
    WriteConsoleData(?ansi_query_cursor, 4)
    Repeat
      If read_(#STDIN_FILENO, @ch, 1)
        If buff_len + 1 > buff_size
          buff_size = buff_size + 16
          ReDim Buff(buff_size)
        EndIf
        buff(buff_len) = ch
        buff_len       = buff_len + 1
      ;Debug Chr(ch)+" "+ch
      Else
        Delay(0)
      EndIf
    Until ch = 27
    Protected clen, i = 0
    If buff_len > 1
      While i < buff_len - 1
        LastElement(InkeyBuffer())
        AddElement(InkeyBuffer())
        clen          = CharLen8(buff(i))
        InkeyBuffer() = PeekS(@buff(i), clen, #PB_ByteLength | #PB_UTF8)
        i             = i + clen
      Wend
    EndIf
    
    re$ = Chr(27)
    Repeat
      If read_(#STDIN_FILENO, @ch, 1)
        re$ = re$ + Chr(ch)
        ;Debug re$
      Else
        Delay(0)
      EndIf
    Until ch = 'R'
    While CountString(re$, Chr(27)) > 1
      i = FindString(re$, Chr(27), 2)
      LastElement(InkeyBuffer())
      AddElement(InkeyBuffer())
      InkeyBuffer() = Left(re$, i - 1)
      re$           = Mid(re$, i)
    Wend
    re$      = Mid(re$, 3)
    re$      = Left(re$, Len(re$) - 1)
    ConsoleY = Val(StringField(re$, 1, ";"))
    ConsoleX = Val(StringField(re$, 2, ";"))
  EndProcedure
  Procedure _vt_queryConsoleSize() ; deprecated but potentially useful for COM port conntections/using VT sequences to query size
    Shared ConsoleX, ConsoleY
    Shared ConsoleWidth, ConsoleHeight
    Shared ConsoleBufferColumns, ConsoleBufferRows
    Shared ConsoleSizeChanged
    Protected changed = ConsoleSizeChanged
    _vt_queryCursorPos()
    ConsoleSizeChanged = 0
    
    Protected *A = Ascii(Chr(27) + "[" + Str(consoley) + ";" + Str(consolex) + "H")
    EnableGraphicalConsole(1)
    ConsoleLocate(9999, 9999)
    _vt_queryCursorPos()
    
    If ConsoleWidth <> ConsoleX
      ConsoleWidth = ConsoleX
      changed      = 1
    EndIf
    If ConsoleBufferColumns <> ConsoleX
      ConsoleBufferColumns = ConsoleX
      changed              = 1
    EndIf
    If ConsoleHeight <> ConsoleY
      ConsoleHeight = ConsoleY
      changed       = 1
    EndIf
    If ConsoleBufferRows <> ConsoleY
     ; Debug "Buff rows changed"
      ConsoleBufferRows = ConsoleY
      changed           = 1
    EndIf
    WriteConsoleData(*A, MemorySize(*A))
    FreeMemory(*A)
    
    ProcedureReturn changed
  EndProcedure
  
  ; internal new (uses API ioctl() + getyx()) where possible
  Procedure _queryCursorPos() ; more APO
    Shared ConsoleX, ConsoleY
    Protected Y, X
    CompilerIf Defined(getyx_, #PB_Procedure) ; termios (?)
      getyx_(@Y, @X)
      ConsoleX = X
      ConsoleY = Y
    CompilerElse
      _vt_queryCursorPos()
    CompilerEndIf
  EndProcedure
  Procedure _queryConsoleSize() ; faster, robust and without flickering
    Protected ws._winsize
    Shared ConsoleWidth
    Shared ConsoleHeight
    Shared ConsoleBufferColumns
    Shared ConsoleBufferRows
    
    Protected changed
    ioctl_(0, #TIOCGWINSZ, @ws);
    If ws\ws_col <> ConsoleWidth
      changed + 1
      ConsoleWidth         = ws\ws_col
      ConsoleBufferColumns = ConsoleWidth
    EndIf
    If ws\ws_row <> ConsoleHeight
      Changed + 1
      ConsoleHeight     = ws\ws_row
      ConsoleBufferRows = ConsoleHeight
    EndIf
    ProcedureReturn Changed
  EndProcedure
  
    ; Public:
  Procedure UpdateConsoleSize() ; returns 0 if sizes haven't changed
    ProcedureReturn _queryConsoleSize()
  EndProcedure
  Procedure ConsoleDefaultColors() ; reset to default colors
    EnableGraphicalConsole(0)
    WriteConsoleData(?reset_console_colors, 4)
  EndProcedure
  Procedure ConsoleX() ; Console cursor current column position (x)
    Shared ConsoleX
    _queryCursorPos()
    ProcedureReturn ConsoleX
  EndProcedure
  Procedure ConsoleY() ; Console cursor current line position (y)
    Shared ConsoleY
    _queryCursorPos()
    ProcedureReturn ConsoleY
  EndProcedure
  Procedure InitConsoleMetrics() ; Init console metrics after OpenConsole()
    Shared ConsoleMaximumColorBits
    
    Protected result = UpdateConsoleSize()
    Protected colors$ = GetEnvironmentVariable("TERM") + " " + GetEnvironmentVariable("COLORTERM")
    
    If FindString(colors$, "truecolor") Or FindString(colors$, "24bit")
      ConsoleMaximumColorBits = 24
    ElseIf FindString(colors$, "256")
      ConsoleMaximumColorBits = 8
    Else
      ConsoleMaximumColorBits = 4
    EndIf
    ProcedureReturn result
  EndProcedure
  
CompilerEndIf

Define ConsoleInkey_EscapeSequence$
Define ConsoleInkey_SpecialKey
Define ConsoleInkey_HasEscapeSequence

; ESCape sequence CSI
#ESC_SEQ = Chr(27) + "["

Procedure$ ConsoleInkey() ; Like Inkey() except it sets SpecialKey (#PB_Key_XYZ constant) in place of Window's RawKey
  Shared ConsoleInkey_HasEscapeSequence
  Shared ConsoleInkey_EscapeSequence$
  Shared ConsoleInkey_SpecialKey
  Shared InkeyBuffer()
  
  Protected k$
  If ListSize(InkeyBuffer())
    FirstElement(InkeyBuffer())
    k$ = InkeyBuffer()
    DeleteElement(InkeyBuffer())
  Else
    EnableGraphicalConsole(0)
    k$ = Inkey()
  EndIf
  
  ConsoleInkey_SpecialKey = 0
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    If k$ <> #Empty$
      Select Asc(k$)
        Case 27
          ConsoleInkey_SpecialKey = #PB_Key_Escape
        Case 10, 13
          ConsoleInkey_SpecialKey = #PB_Key_Return
        Case 8, 127
          ConsoleInkey_SpecialKey = #PB_Key_Back
        Case 128
          ConsoleInkey_SpecialKey = #PB_Key_Delete
        Default
          ConsoleInkey_SpecialKey = 0
      EndSelect
      If Not ConsoleInkey_SpecialKey
        ProcedureReturn k$
      Else
        ProcedureReturn #Empty$
      EndIf
    Else
      
      Protected rk = RawKey()
      Select rk
        Case 0
        Case 33
          ConsoleInkey_SpecialKey = #PB_Key_PageUp
        Case 34
          ConsoleInkey_SpecialKey = #PB_Key_PageDown
        Case 35
          ConsoleInkey_SpecialKey = #PB_Key_End
        Case 36
          ConsoleInkey_SpecialKey = #PB_Key_Home
        Case 37
          ConsoleInkey_SpecialKey = #PB_Key_Left
        Case 38
          ConsoleInkey_SpecialKey = #PB_Key_Up
        Case 39
          ConsoleInkey_SpecialKey = #PB_Key_Right
        Case 40
          ConsoleInkey_SpecialKey = #PB_Key_Down
        Case 45
          ConsoleInkey_SpecialKey = #PB_Key_Insert
        Case 46
          ConsoleInkey_SpecialKey = #PB_Key_Delete
        Case 112 To 121
          ConsoleInkey_SpecialKey = #PB_Key_F1 + (rk - 112)
        Default
          
          ProcedureReturn #Empty$
      EndSelect
      ProcedureReturn #Empty$
    EndIf
    
  CompilerElse
    Static last_in.q
    If k$ = Chr(27)
      ConsoleInkey_EscapeSequence$   = ""
      ConsoleInkey_HasEscapeSequence = ConsoleInkey_HasEscapeSequence + 1
      last_in                        = ElapsedMilliseconds()
    EndIf
    
    If ConsoleInkey_HasEscapeSequence And k$ <> ""
      
      
      ConsoleInkey_EscapeSequence$ + k$
      ; PrintN("Escape seq: "+Mid(ConsoleInkey_EscapeSequence$,2))
      Select ConsoleInkey_EscapeSequence$
        Case Chr(27)
          If ElapsedMilliseconds() - last_in >= 150000
            ConsoleInkey_SpecialKey = #PB_Key_Escape
          EndIf
        Case #ESC_SEQ + "A", #ESC_SEQ + "1A"
          ConsoleInkey_SpecialKey = #PB_Key_Up
        Case #ESC_SEQ + "B", #ESC_SEQ + "1B"
          ConsoleInkey_SpecialKey = #PB_Key_Down
        Case #ESC_SEQ + "C", #ESC_SEQ + "1C"
          ConsoleInkey_SpecialKey = #PB_Key_Right
        Case #ESC_SEQ + "D", #ESC_SEQ + "1D"
          ConsoleInkey_SpecialKey = #PB_Key_Left
        Case #ESC_SEQ + "1~", #ESC_SEQ + "H"
          ConsoleInkey_SpecialKey = #PB_Key_Home
        Case #ESC_SEQ + "2~"
          ConsoleInkey_SpecialKey = #PB_Key_Insert
        Case #ESC_SEQ + "3~"
          ConsoleInkey_SpecialKey = #PB_Key_Delete
        Case #ESC_SEQ + "4~", #ESC_SEQ + "F"
          ConsoleInkey_SpecialKey = #PB_Key_End
        Case #ESC_SEQ + "5~"
          ConsoleInkey_SpecialKey = #PB_Key_PageUp
        Case #ESC_SEQ + "6~"
          ConsoleInkey_SpecialKey = #PB_Key_PageDown
        Case #ESC_SEQ + "7~"
          ConsoleInkey_SpecialKey = #PB_Key_Home
        Case #ESC_SEQ + "8~"
          ConsoleInkey_SpecialKey = #PB_Key_End
        Case #ESC_SEQ + "9~"
          ConsoleInkey_SpecialKey = 0
        Case #ESC_SEQ + "10~"
          ConsoleInkey_SpecialKey = 0
        Case #ESC_SEQ + "11~"
          ConsoleInkey_SpecialKey = #PB_Key_F1
        Case #ESC_SEQ + "12~"
          ConsoleInkey_SpecialKey = #PB_Key_F2
        Case #ESC_SEQ + "13~"
          ConsoleInkey_SpecialKey = #PB_Key_F3
        Case #ESC_SEQ + "14~"
          ConsoleInkey_SpecialKey = #PB_Key_F4
        Case #ESC_SEQ + "15~"
          ConsoleInkey_SpecialKey = #PB_Key_F5
        Case #ESC_SEQ + "16~"
          ConsoleInkey_SpecialKey = 0
        Case #ESC_SEQ + "17~"
          ConsoleInkey_SpecialKey = #PB_Key_F6
        Case #ESC_SEQ + "18~"
          ConsoleInkey_SpecialKey = #PB_Key_F7
        Case #ESC_SEQ + "19~"
          ConsoleInkey_SpecialKey = #PB_Key_F8
        Case #ESC_SEQ + "20~"
          ConsoleInkey_SpecialKey = #PB_Key_F9
        Case #ESC_SEQ + "21~"
          ConsoleInkey_SpecialKey = #PB_Key_F10
        Case #ESC_SEQ + "22~"
          ConsoleInkey_SpecialKey = 0
        Case #ESC_SEQ + "23~"
          ConsoleInkey_SpecialKey = #PB_Key_F11
        Case #ESC_SEQ + "24~"
          ConsoleInkey_SpecialKey = #PB_Key_F12
        Case #ESC_SEQ + "P", #ESC_SEQ + "1P", Chr(27) + "0P", Chr(27) + "OP"
          ConsoleInkey_SpecialKey = #PB_Key_F1
        Case #ESC_SEQ + "Q", #ESC_SEQ + "1Q", Chr(27) + "0Q", Chr(27) + "OQ"
          ConsoleInkey_SpecialKey = #PB_Key_F2
        Case #ESC_SEQ + "R", #ESC_SEQ + "1R", Chr(27) + "0R", Chr(27) + "OR"
          ConsoleInkey_SpecialKey = #PB_Key_F3
        Case #ESC_SEQ + "S", #ESC_SEQ + "1S", Chr(27) + "0S", Chr(27) + "OS"
          ConsoleInkey_SpecialKey = #PB_Key_F4
          
          
        Default
          Select Asc(Right(ConsoleInkey_EscapeSequence$, 1))
            Case 'n'
              
            Case '~', 'A' To 'Z', 'a' To 'z'
              If Len(ConsoleInkey_EscapeSequence$) > 2
                ConsoleInkey_EscapeSequence$   = #Null$
                ConsoleInkey_HasEscapeSequence = 0
                ConsoleInkey_SpecialKey        = 0
              EndIf
            Case 27
              If ElapsedMilliseconds() - last_in >= 25
                ConsoleInkey_SpecialKey        = #PB_Key_Escape
                ConsoleInkey_EscapeSequence$   = #Null$
                ConsoleInkey_HasEscapeSequence = 0
              EndIf
            Default
              
          EndSelect
          
      EndSelect
      
      If ConsoleInkey_SpecialKey
        ConsoleInkey_HasEscapeSequence = 0
        ConsoleInkey_EscapeSequence$   = #Null$
      EndIf
      
      ProcedureReturn #Empty$
    Else
      Select Asc(k$)
        Case 0
          If ConsoleInkey_HasEscapeSequence
            If ElapsedMilliseconds() - last_in >= 5 And ConsoleInkey_EscapeSequence$ = Chr(27)
              ConsoleInkey_SpecialKey        = #PB_Key_Escape
              ConsoleInkey_EscapeSequence$   = #Null$
              ConsoleInkey_HasEscapeSequence = 0
            EndIf
          EndIf
          
        Case 8, 127
          ConsoleInkey_SpecialKey = #PB_Key_Back
        Case 128
          ConsoleInkey_SpecialKey = #PB_Key_Delete
        Case 10, 13
          ConsoleInkey_SpecialKey = #PB_Key_Return
        Default
          ConsoleInkey_SpecialKey = 0
      EndSelect
      If ConsoleInkey_SpecialKey = 0
        ProcedureReturn k$
      Else
        ProcedureReturn ""
      EndIf
    EndIf
  CompilerEndIf
  
EndProcedure
Procedure ConsoleSpecialKey() ; Returns the #PB_Key_XYZ constant of the last pressed keyboard key
  Shared ConsoleInkey_SpecialKey
  ProcedureReturn ConsoleInkey_SpecialKey
EndProcedure

Procedure VTSequence_To_SpecialKey(sequence$) ; translate escape sequence to #PB_Key_XXX constant
  Protected ConsoleInkey_SpecialKey
  Select sequence$
    Case Chr(27) + "^"
      ConsoleInkey_SpecialKey = #PB_Key_Escape
    Case #ESC_SEQ + "A", #ESC_SEQ + "1A"
      ConsoleInkey_SpecialKey = #PB_Key_Up
    Case #ESC_SEQ + "B", #ESC_SEQ + "1B"
      ConsoleInkey_SpecialKey = #PB_Key_Down
    Case #ESC_SEQ + "C", #ESC_SEQ + "1C"
      ConsoleInkey_SpecialKey = #PB_Key_Right
    Case #ESC_SEQ + "D", #ESC_SEQ + "1D"
      ConsoleInkey_SpecialKey = #PB_Key_Left
    Case #ESC_SEQ + "1~", #ESC_SEQ + "H"
      ConsoleInkey_SpecialKey = #PB_Key_Home
    Case #ESC_SEQ + "2~"
      ConsoleInkey_SpecialKey = #PB_Key_Insert
    Case #ESC_SEQ + "3~"
      ConsoleInkey_SpecialKey = #PB_Key_Delete
    Case #ESC_SEQ + "4~", #ESC_SEQ + "F"
      ConsoleInkey_SpecialKey = #PB_Key_End
    Case #ESC_SEQ + "5~"
      ConsoleInkey_SpecialKey = #PB_Key_PageUp
    Case #ESC_SEQ + "6~"
      ConsoleInkey_SpecialKey = #PB_Key_PageDown
    Case #ESC_SEQ + "7~"
      ConsoleInkey_SpecialKey = #PB_Key_Home
    Case #ESC_SEQ + "8~"
      ConsoleInkey_SpecialKey = #PB_Key_End
    Case #ESC_SEQ + "9~"
      ConsoleInkey_SpecialKey = 0
    Case #ESC_SEQ + "10~"
      ConsoleInkey_SpecialKey = 0
    Case #ESC_SEQ + "11~"
      ConsoleInkey_SpecialKey = #PB_Key_F1
    Case #ESC_SEQ + "12~"
      ConsoleInkey_SpecialKey = #PB_Key_F2
    Case #ESC_SEQ + "13~"
      ConsoleInkey_SpecialKey = #PB_Key_F3
    Case #ESC_SEQ + "14~"
      ConsoleInkey_SpecialKey = #PB_Key_F4
    Case #ESC_SEQ + "15~"
      ConsoleInkey_SpecialKey = #PB_Key_F5
    Case #ESC_SEQ + "16~"
      ConsoleInkey_SpecialKey = 0
    Case #ESC_SEQ + "17~"
      ConsoleInkey_SpecialKey = #PB_Key_F6
    Case #ESC_SEQ + "18~"
      ConsoleInkey_SpecialKey = #PB_Key_F7
    Case #ESC_SEQ + "19~"
      ConsoleInkey_SpecialKey = #PB_Key_F8
    Case #ESC_SEQ + "20~"
      ConsoleInkey_SpecialKey = #PB_Key_F9
    Case #ESC_SEQ + "21~"
      ConsoleInkey_SpecialKey = #PB_Key_F10
    Case #ESC_SEQ + "22~"
      ConsoleInkey_SpecialKey = 0
    Case #ESC_SEQ + "23~"
      ConsoleInkey_SpecialKey = #PB_Key_F11
    Case #ESC_SEQ + "24~"
      ConsoleInkey_SpecialKey = #PB_Key_F12
      
    Case #ESC_SEQ + "P", #ESC_SEQ + "1P", Chr(27) + "0P", Chr(27) + "OP"
      ConsoleInkey_SpecialKey = #PB_Key_F1
    Case #ESC_SEQ + "Q", #ESC_SEQ + "1Q", Chr(27) + "0Q", Chr(27) + "OQ"
      ConsoleInkey_SpecialKey = #PB_Key_F2
    Case #ESC_SEQ + "R", #ESC_SEQ + "1R", Chr(27) + "0R", Chr(27) + "OR"
      ConsoleInkey_SpecialKey = #PB_Key_F3
    Case #ESC_SEQ + "S", #ESC_SEQ + "1S", Chr(27) + "0S", Chr(27) + "OS"
      ConsoleInkey_SpecialKey = #PB_Key_F4
      
  EndSelect
  
  ProcedureReturn ConsoleInkey_SpecialKey
EndProcedure

Procedure$ SpecialKey_To_VTSequence(SpecialKey) ; translate #PB_Key_XXX to VT sequence
  Protected result$
  Select SpecialKey
    Case #PB_Key_Back
      result$ = Chr(8)
    Case #PB_Key_Return
      result$ = Chr(13)
    Case #PB_Key_Up
      result$ = #ESC_SEQ + "A"
    Case #PB_Key_Down
      result$ = #ESC_SEQ + "B"
    Case #PB_Key_Right
      result$ = #ESC_SEQ + "C"
    Case #PB_Key_Left
      result$ = #ESC_SEQ + "D"
    Case #PB_Key_Home
      result$ = #ESC_SEQ + "1~"
    Case #PB_Key_Insert
      result$ = #ESC_SEQ + "2~"
    Case #PB_Key_Delete
      result$ = #ESC_SEQ + "3~"
    Case #PB_Key_End
      result$ = #ESC_SEQ + "4~"
    Case #PB_Key_PageUp
      result$ = #ESC_SEQ + "5~"
    Case #PB_Key_PageDown
      result$ = #ESC_SEQ + "6~"
    Case #PB_Key_F1 To #PB_Key_F12
      result$ = #ESC_SEQ + Str(11 + SpecialKey - #PB_Key_F1) + "~"
  EndSelect
  ProcedureReturn result$
EndProcedure

Procedure$ SpecialKey_GetName(K) ; little helper returns "F1" when given #PB_Key_F1 etc.
  Protected result$
  Select K
    Case 0
      
    Case #PB_Key_Return
      result$ = "return"
    Case #PB_Key_Home
      result$ = "home"
    Case #PB_Key_F1 To #PB_Key_F12
      result$ = "F" + Str(1 + (K - #PB_Key_F1))
    Case #PB_Key_Back
      result$ = "backspace"
    Case #PB_Key_End
      result$ = "end"
    Case #PB_Key_PageUp
      result$ = "pageup"
    Case #PB_Key_PageDown
      result$ = "pagedown"
    Case #PB_Key_Delete
      result$ = "delete"
    Case #PB_Key_Insert
      result$ = "insert"
    Case #PB_Key_Escape
      result$ = "escape"
    Case #PB_Key_Left
      result$ = "left"
    Case #PB_Key_Up
      result$ = "up"
    Case #PB_Key_Right
      result$ = "right"
    Case #PB_Key_Down
      result$ = "down"
    Default
      result$ = "raw:" + Str(K)
  EndSelect
  ProcedureReturn result$
EndProcedure


; DEMO /TEST
CompilerIf #PB_Compiler_IsMainFile
  
  Procedure$ WaitKey(Text$, FrontColor = $FFFFFF, BackColor = $0, dephased = 0) ; Wait for a key + pulsating color fader text
    
    Protected rest
    Protected last_rest
    Protected inkey$
    Protected f.f
    Protected bf.f
    Protected fr = FrontColor & 255
    Protected fg = (FrontColor >> 8) & 255
    Protected fb = (FrontColor >> 16) & 255
    Protected br = (BackColor) & 255
    Protected bg = (BackColor >> 8) & 255
    Protected bb = (BackColor >> 16) & 255
    
    Repeat
      rest = ElapsedMilliseconds() & 1023
      If rest > 512
        rest = 1024 - rest
      EndIf
      rest = rest / 2
      
      If rest <> last_rest
        
        f = rest / 255
        If dephased
          bf = 1.0 - f
        Else
          bf = f
        EndIf
        
      ;ConsoleColor24(RGB(rest,rest,rest),RGB(255-rest,255-rest,255-rest))
        ConsoleDefaultColors()
        EnableGraphicalConsole(0)
       ; Print(#CR$ + LSet("", ConsoleWidth() - 1) + #CR$)
        Print(#CR$)
        ConsoleColor24(xRGB(fr * f, fg * f, fb * f), xRGB(br * bf, bg * bf, bb * bf))
        
        EnableGraphicalConsole(1)
        
        Print(Text$)
        ConsoleDefaultColors()
        
        last_rest = rest
      Else
        Delay(1)
      EndIf
      inkey$ = ConsoleInkey()
    Until inkey$ <> #Empty$ Or ConsoleSpecialKey()
    ConsoleDefaultColors()
    EnableGraphicalConsole(0)
    
    Print(#CR$ + LSet("", ConsoleWidth() - 1) + #CR$)
    
    ProcedureReturn inkey$
  EndProcedure
  
  OpenConsole()
  InitConsoleMetrics()
  UpdateConsoleSize()
  
  PrintN("UpdateConsoleMetrics.pb demo/test")
  PrintN("'Text decorations'")
  ConsoleBold(1):PrintN("This text is bold (non-Windows OS)"):ConsoleBold(0)
  ConsoleUnderline(1):PrintN("This text is underlined (non-Windows OS)"):ConsoleUnderline(0)
  ConsoleItalic(1):PrintN("This text is italic (non-Windows OS)"):ConsoleItalic(0)
  ConsoleStrikeOut(1):PrintN("This text is striked-out (non-Windows OS)"):ConsoleStrikeOut(0)
  ConsoleBlink(1):PrintN("This text is blinking at normal speed (non-Windows OS)"):ConsoleBlink(0)
  PrintN("This text doesn't blink")
  ConsoleBlink(2):PrintN("This text is blinking at fast speed (non-Windows OS)"):ConsoleBlink(0)
  
  Debug Str(ConsoleWidth()) + " x " + Str(ConsoleHeight())
  
  
  PrintN("Maxium color bits: " + Str(ConsoleMaximumColorBits()))
  PrintN("...")
  Print("Console X: ") : PrintN(Str(ConsoleX()))
  Print("Console Y: ") : PrintN(Str(ConsoleY()))
  PrintN("Console Width: " + Str(ConsoleWidth()))
  PrintN("Console Height: " + Str(ConsoleHeight()))
  Print("Console Buffer Columns: ") : PrintN(Str(ConsoleBufferColumns()))
  Print("Console Buffer Lines: ") : PrintN(Str(ConsoleBufferRows()))
  PrintN("")
  WaitKey("Resize your terminal window & press any key", $FFFF, $0000FF, 1)
  If UpdateConsoleSize() = 0
    ConsoleColor24(xRGB(127, 127, 127), 0)
    PrintN("(size unchanged)")
  Else
    ConsoleColor24($00FF00, 0)
    PrintN("size changed!")
  EndIf
  ConsoleDefaultColors()
  Print("Console X: ") : PrintN(Str(ConsoleX()))
  Print("Console Y: ") : PrintN(Str(ConsoleY()))
  PrintN("Console Width: " + Str(ConsoleWidth()))
  PrintN("Console Height: " + Str(ConsoleHeight()))
  Print("Console Buffer Columns: ") : PrintN(Str(ConsoleBufferColumns()))
  Print("Console Buffer Lines: ") : PrintN(Str(ConsoleBufferRows()))
  
  CompilerIf #PB_Compiler_Debugger
    WaitKey("Program end. Press any key to exit.")
  CompilerEndIf
  
  CloseConsole()
  
CompilerEndIf

CompilerIf #PB_Compiler_IsMainFile
  
  OpenConsole()
  PrintN("Hit F1-F12 keys, Home, Cursor, PageUp/Down and other special keys - #PB_Key_Escape to exit :)")
  Repeat
    Print(ConsoleInkey())
    If ConsoleSpecialKey()
      PrintN("*" + SpecialKey_GetName(ConsoleSpecialKey()) + "*")
    EndIf
    
  Until ConsoleSpecialKey() = #PB_Key_Escape
  
  CloseConsole()
  
CompilerEndIf

;
;

Re: The weak spot of EnableGraphicalConsole()

Posted: Thu Jul 24, 2025 9:28 pm
by bds107
I was just going to raise a problem about EnableGraphicalConsole(1), but I ended up here after searching this forum.

When I create an OpenConsole() program, I can use 256 ANSI colors.
I can also use ANSI control characters like background, underline, etc.
But when I enable EnableGraphicalConsole(1), ANSI control characters can't be used.
I work with Windows 11.

I'm curious about a solution.
However, the solution above, which involves switching from EnableGraphicalConsole(0) to EnableGraphicalConsole(1), can't be the solution.

Greetings!