未加星标

More F# colors in the terminal

字体大小 | |
[系统(windows) 所属分类 系统(windows) | 发布者 店小二05 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

After releasing ColoredPrintf I started investigating color terminals, especially how some terminals can display more than the traditional 16 colors.

While on windows console colors are changed using a call to the SetConsoleTextAttribute API, on unix XTerm and other terminals follow the way the VT100 physical terminal did it with special escape sequences that are sent to the terminal but change it's behavior instead of being displayed.

It is especially interesting as such terminals are becomming more present, even in the windows world. ConEmu support a lot even if it's buggy, the venerable windows terminal canbe told to interpret themand it'll even soon gain True Color support too .

Compatibility

If you're on windows, expect bugs everywhere but if you have a mac or a linux OS you're good. In more details :

On windows ConEmu is still the best you can do, it's buggy but usable. There is a little trick to enable more than 16 colors but it's easy and need to be done only once per session, I'll explain how to do it from code inbut you can simply display one of the ConEmu sample file with cmd /c type "%ConEmuBaseDir%\Addons\AnsiColors256.ans" and it'll activate it. On macOS (Tested on sierra) the integrated terminal can display 256 colors without problems but will translate any True Color command to 16 colors, giving a very ugly result. But iTerm2 is free works ! On linux the Gnome default terminal, XTerm and Konsole works flawlessly (XTerm adapt true color codes to 256 colors but it's algorithm is pretty good). Visual Studio Code integrated terminal can do most of them and as the REPL provided by the Ionide plugin uses it we can use the F# REPL directly there. But beware the windows version of the VSCode terminal is pretty buggy at the moment and often completely break when control codes are sent (Well, resizing it is enough to break it, you don't need much :grin:) Using F# to play with escape sequences

Escape sequences are generated by printing the ESC ( 0x1B) character followed by at least another character.

The most common ones start with the "CSI" sequence ESC[ followed by parameters and ending with a character designating the command. The full list is on Wikipedia or MSDN .

Let's try that in F# :

let esc = string (char 0x1B) System.Console.WriteLine(esc + "[31;1m" + "Hello world" + esc + "[0m")
More F# colors in the terminal

It displayed Hello World in bright red as m is the graphic command taking sub-commands separated by ; , 31 is the red color and 1 signal that we want bold/bright text. And at the end 0 is the reset sub-command, without it the terminal would stay red on all following lines.

Using kprintf we can create a printf-like function and start implementing a few of the escape sequences :

let csi = esc + "[" let printsequencef f = Printf.kprintf (fun s -> System.Console.Write(esc + s)) f let printcsif f = Printf.kprintf (fun s -> System.Console.Write(csi + s)) f let selectGraphicRendition (gr: int list) = printcsif "%sm" (System.String.Join(";", gr |> Seq.map string)) let resetColor() = selectGraphicRendition [0] let setForeground i = selectGraphicRendition([30 + i]) let setBackground i = selectGraphicRendition([40 + i]) 256 colors is enough for everyone

Until then we've done nothing very interesting, after all the .Net console already support changing the console color to bright red, nothing new under the sun. But what the .Net console or Windows native API doesn't support is the 38 and 48 graphics sub-commands, extended foreground an background colors.

let setExtendedForeground i = selectGraphicRendition [38; 5; i] let setExtendedBackground i = selectGraphicRendition [48; 5; i] let setForegroundRgb r g b = selectGraphicRendition [38; 2; r; g; b] let setBackgroundRgb r g b = selectGraphicRendition [48; 2; r; g; b]

The first variant accept an index in a 256 colors table and the second specify the R, G and B components.

While all the terminal emulators I tested support both of them the colors the display will vary, especially for the R;G;B True Color mode. Some like macOS default terminal are pretty weird in the sense that it support 256 color mode but interpolate any R;G;B code to 16 colors.

Let's see what the palete looks like :

let extendedBlock i = for j in i..i+5 do setExtendedBackground j printf " " for row in 0..5 do for b in 0..5 do extendedBlock (16 + 36*b + row*6) resetColor() printf " " printfn ""

The result in VS Code (On macOS) :


More F# colors in the terminal
We need to be true to our colors

Now that we know how to draw with true colors we can draw pictures on the terminal :

open System.Drawing let showBitmap path = let bitmap = Image.FromFile(path) :?> Bitmap for y in 0..bitmap.Size.Height-1 do for x in 0..bitmap.Size.Width-1 do let px = bitmap.GetPixel(x, y) setBackgroundRgb (int px.R) (int px.G) (int px.B) printf " " resetColor() printfn ""

And we can apply it to a 40x40px version of the FSharp logo :


More F# colors in the terminal

The code for all of that can be found in a Gist .

I didn't test the windows insider release so can't tell how well microsoft True Color support in the native console works but it's the next thing I want to try.

Appendix A: ConEmu color support

ConEmu support more than the standard 16 colors but if you try it, it won't work directly, it seem that maybe for compatibility reasons the support isn't always on.


More F# colors in the terminal

To trigger it an application must first scroll the terminal far away at least once with the T command :

let scrollUp (lines: int) = printcsif "%iS" lines let scrollDown (lines: int) = printcsif "%iT" lines scrollDown 9999 setCursorPos 9999 1

After that ConEnu will display what we expect :


More F# colors in the terminal
Appendix B: Enabling escape codes in Windows default terminal

While the windows terminal support escape codes, it doesn't do it directly. They need to be enabled using SetConsoleMode API :

#nowarn "9" module DllImports = open System.Runtime.InteropServices open Microsoft.FSharp.NativeInterop let INVALID_HANDLE_VALUE = nativeint -1 let STD_OUTPUT_HANDLE = -11 let ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 [<DllImport("Kernel32")>] extern void* GetStdHandle(int nStdHandle) [<DllImport("Kernel32")>] extern bool GetConsoleMode(void* hConsoleHandle, int* lpMode) [<DllImport("Kernel32")>] extern bool SetConsoleMode(void* hConsoleHandle, int lpMode) let enableVTMode() = let handle = GetStdHandle(STD_OUTPUT_HANDLE) if handle <> INVALID_HANDLE_VALUE then let mode = NativePtr.stackalloc<int> 1 if GetConsoleMode(handle, mode) then let value = NativePtr.read mode let value = value ||| ENABLE_VIRTUAL_TERMINAL_PROCESSING SetConsoleMode(handle, value) else printfn "no get" false else printfn "no handle" false DllImports.enableVTMode() |> ignore

本文系统(windows)相关术语:三级网络技术 计算机三级网络技术 网络技术基础 计算机网络技术

主题: F#.NetWindowsVisual StudioSDNVisual StudiUTUAVTMCSI
分页:12
转载请注明
本文标题:More F# colors in the terminal
本站链接:http://www.codesec.net/view/484400.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 系统(windows) | 评论(0) | 阅读(61)