As you know I am working on ISM, cli to manage a Linux system.
I am facing some smalls problem actually.
I would like to know if it’s possible when my program is running under TTY if it’s possible to print underlined characters (but I guess it’s not possible). If it’s not, how can I detect if the software is under a TTY and print differently characters?
The other thing is about to align items.
If for example in 3 different ligns I would like to align some text at the same column, is there any function / way to do that ?
Last question, if I print a sentence longer that the lign size, how can I manage with that ?
Because ISM use sometime animated text, but if the text is longer than the lign, the text don’t erase the previous one, it will be duplicate
In a Linux terminal emulator, this can be done with simple ANSI sequences. The standard library has the Colorize module which has some ANSI stuff already in it, where you can do something like:
STDOUT << "This text is " << "underlined".colorize.underline << " text\n"
Or with puts or printf or whatever you’d like.
Under the hood, it’s essentially equivalent to something like STDOUT << "this is \e[4munderlined\e[24m text\n", but you don’t have to remember the actual escape sequences and codes.
Aligning is probably best done with printf and field widths. The documentation is here and is very close (identical?) to printf in other languages.
For a sentence longer than the line size, I wrote my own wrapping function. The repo is here if you want to use it. I don’t think there’s something like this in the standard library.
Hi guys, I am coming back to you because I have an another question about the CLI I am coding, because I almost finished to code all of the main features for my project.
Actually my CLI play sometime a text animation. But actually it’s just basically that:
print text+"\r"
But I am not happy to proceed like that, because if the terminal is resized, or if you are with a small resolution, the terminal will print duplicate text.
I would like to know how I can clear properly the text.
You might have to start tracking some state in your program since terminals aren’t going to be smart enough to know how to properly re-render text in all situations where the terminal size changes. You’ll have to track things like the current terminal size, how much you’ve printed, where the cursor is, etc…
On Unix platforms, you can get the terminal size like this (I don’t know if this got added to the stdlib since I originally wrote it, so it may already be present):
lib LibC
{% if flag?(:unix) %}
# Bare minimum for the ioctl stuff we need
TIOCGWINSZ = 0x5413u32
struct Winsize
ws_row : UInt16
ws_col : UInt16
ws_xpixel : UInt16
ws_ypixel : UInt16
end
fun ioctl(fd : LibC::Int, what : LibC::ULong, ...) : LibC::Int
{% end %}
end
{% if flag?(:unix) %}
# Gets the height and width of the terminal (in that order).
def getWinSize : Tuple(UInt16, UInt16)
thing = LibC::Winsize.new
LibC.ioctl(STDOUT.fd, LibC::TIOCGWINSZ, pointerof(thing))
{thing.ws_row, thing.ws_col}
end
{% end %}
From there, you’d move the cursor around, deleting and re-printing lines or segments of text as-needed.
The nuclear option would be to keep track of everything printed, then if the terminal is resized, completely clear it and redraw it
I have absolutely no data to back this up, but my gut tells me that a user is more likely to grow the size of the terminal, not shrink it, unless they’re in a tiling window manager.
The method I use to repaint the terminal and survive resizing is not perfect and can fail, but looks basically like this:
detect a tty with STDOUT.tty? or STDERR.tty? depending on where I’m writing, and if it’s not a tty, use Log to write updates instead of any of these things.
decide how many lines I need to use, and print that many linefeeds: print '\n' * num_lines
when printing my progress info, first send "\e[%dA" % num_lines in order to move up to where I should be
write my N lines of output, ending each line with "\e[K" to clear to end of line
in theory–i’ve not tried it–it should be possible to catch a SIGWNCH signal and use it to re-request the terminal dimensions in order to repaint appropriately. however, this can definitely still leave unexpected output if the resize causes lines to wrap that were not wrapped before. if this is an important problem, then i’d use the alternate screen ("\e[?1049h" to move to the alternate screen, "\e[?1049l" to move back when exiting the program) and instead of using the "\e[A" sequence to move up N lines, use "\e[1;1H" to always move to the start of the first line, allowing me to use the whole terminal display for my output.
I was trying that the other day and the signal didn’t want to fire, but this does seem to be a bit more ideal than constantly checking the terminal size every screen update. Maybe it was the terminal I was using (Terminology), or I could have been doing something wrong.
I plan to use this solution, because I would like to make something really cool. I will try to use that, and I will come back to you if I have any questions