Page 1 of 1

possibility of enabling virtual terminal processing mode for windows console process

Posted: Thu Nov 18, 2021 6:54 pm
by yal2du
Since Windows 10 build 18298 (end of 2018), it is possible to send terminal escape sequences to stdout (e.g. with print()) and have them properly interpreted as xterm terminal emulator formatting and cursor commands *if* a call to the winapi function SetConsoleMode is made to enable it.

Pretty niche but I'm wondering; is there anyone else that might be interested in this?

https://stackoverflow.com/questions/526 ... processing
https://docs.microsoft.com/en-us/window ... onsolemode
https://docs.microsoft.com/en-us/window ... -sequences
https://devblogs.microsoft.com/commandl ... -features/

Re: possibility of enabling virtual terminal processing mode for windows console process

Posted: Thu Nov 18, 2021 10:05 pm
by pgimeno
oh ANSI.SYS is back? ^^

Re: possibility of enabling virtual terminal processing mode for windows console process

Posted: Thu Nov 18, 2021 11:11 pm
by yal2du
pgimeno wrote: Thu Nov 18, 2021 10:05 pm oh ANSI.SYS is back? ^^
yesss ^.^

Here is my attempt using ffi ... [redacted see example code in next post]

Re: possibility of enabling virtual terminal processing mode for windows console process

Posted: Fri Nov 19, 2021 6:45 pm
by yal2du

Code: Select all

local osString = love.system.getOS( )
local bit      = require("bit")
local ffi      = require("ffi")

if osString == 'Windows' then

   ffi.cdef[[
      typedef uint64_t UINT_PTR; // Integer
      typedef UINT_PTR HANDLE;   // Alias
      typedef int BOOL;
      typedef unsigned long DWORD;
      typedef void *PVOID;
      typedef DWORD WINAPI_ConsoleModeFlags;
      // #define WINAPI __stdcall
      // #define _IN_
      
      HANDLE __stdcall GetStdHandle(DWORD nStdHandle);
      BOOL SetConsoleMode(HANDLE hConsoleHandle, DWORD dwMode); // (HANDLE, WINAPI_ConsoleModeFlags)
      int printf(const char *fmt, ...);
   ]]

   ffi.C.printf("hello %s!", "from ffi world")
   local handle = ffi.C.GetStdHandle(-11)
   print('handle is '..tostring(handle))
   local ENABLE_PROCESSED_OUTPUT = 0x01
   local ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x04
   local result = ffi.C.SetConsoleMode(handle,bit.bor(ENABLE_PROCESSED_OUTPUT,ENABLE_VIRTUAL_TERMINAL_PROCESSING))
   print('setconsole returned '..tostring(result))
   
else
   ffi.cdef[[
      int printf(const char *fmt, ...);
   ]]
end

local esc    = string.char(0x1B)..'['
local colors = {
   faint     = esc.."3m",     -- no support
   underline = esc.."4m",
   blink     = esc.."5m",     -- no support
   red       = esc.."31;1m",
   green     = esc.."32;1m",
   blue      = esc.."34;1m",
   white     = esc.."37;1m",
   default   = esc.."39;1m",  -- foreground
   backred   = esc.."41m",
   backgreen = esc.."42m",
   backblue  = esc.."44m",
   backwhite = esc.."47m",
   backdef   = esc.."49;1m"   -- background
}
local function printat(row, column, text, color)
   if color == nil then color = colors.default end
   if type(row) ~= 'number' then row = 1 end
   if type(column) ~= 'number' then column = 1 end
   if text == nil then text = '' end
   if type(text) ~= 'string' then text = tostring(text) end
   ffi.C.printf(esc..tostring(row)..';'..tostring(column)..'f'..color..tostring(text)..'\x1B[0m')
   return row,#text+column
end

ffi.C.printf("\n\x1B[31;1mRed\x1B[0m \x1B[32;1mGreen\x1B[0m \x1B[34;1mBlue\x1B[0m\n")
print('This is red->\27[31mred\x1B[0m\n')
local r,c = 10,30
r,c = printat(r,  c,"TEST at "..tostring(r  )..' '..tostring(c),colors.red)
r,c = printat(r+1,c,"TEST at "..tostring(r+1)..' '..tostring(c),colors.green)
r,c = printat(r+1,c,"TEST at "..tostring(r+1)..' '..tostring(c),colors.blue)
I was missing a flag: ENABLE_PROCESSED_OUTPUT. Appears to work now on Windows when run with lovec.exe (haven't tried with t.console = true in conf.lua). Below is an escape sequence reference for xterm:

https://invisible-island.net/xterm/ctlseqs/ctlseqs.html