Print rectangles to terminal - c

I'm trying to write a text editor for Linux that looks like MS-DOS EDIT.
However, I'm stuck because I can't figure out how to draw the thin rectangles around the editor screen and dialog box. I know the Linux dialog command can do something similar:
How can I draw rectangles like that around the screen (preferably without curses)?

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃These are box-drawing characters. ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│They live in the U+2500-U+257F range of│
│Unicode characters. │
└───────────────────────────────────────┘
░▒▓▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜▓▒░
░▒▓▌ The shadows are block elements, ▐▓▒░
░▒▓▌ Unicode U+2580-U+259F. ▐▓▒░
░▒▓▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟▓▒░
Once upon a time, box-drawing characters and block elements and were common in CP-437. Modern terminals likely expect UTF-8. (They don't work very well in web browsers... see here if the above text looks odd.)
There are also ANSI escapes to set the background color, foreground color, and other attributes of text displayed on a terminal. I can't demonstrate it well on Stack Overflow, though.

The ncurses library is a good way to do what you want, although you say you want alternatives. You can use the Unicode box-drawing characters as wide characters. They include all the characters from MS-DOS code page 437.
Modern distributions should be set up to support UTF-8 by default, so this should work. (I recommend saving the source file as UTF-8 with a byte-order mark.)
#define _XOPEN_SOURCE 700
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
int main(void)
{
setlocale( LC_ALL, "" );
fputws( L"╒╩╤╣\n", stdout );
return EXIT_SUCCESS;
}
Without curses, you can check the environment variables LINES and COLS to get the dimensions of the terminal. The control characters to print colors and such on the Linux console are in the console_codes(4) man page (and are a variant of the VT102 control codes, which are a superset of VT100, a superset of ANSI standard terminals). If you want to invoke it from a program such as gnome_terminal, check its documentation too, but it will probably implement an extension of xterm, which is an extension of VT102, etc. One that is very useful is that the form feed character '\L' will clear the screen and let you redraw it. You could also use terminfo or termcap for a more abstract and general interface, but in practical terms, nobody uses anything other than an extension of VT100 plus ANSI color any more.
Make sure your terminal font includes the line-drawing characters you want to use! DejaVu Sans Mono is an excellent monospace font, especially for its coverage of Unicode. Also, you can check that your locale is set correctly with the locale command; the locale names you see should end in something like .utf8 or UTF-8.

What you're describing is using the box drawing characters present in various extended character sets. The characters available depend at least on the platform and terminal emulation.
Given your question is tagged with Linux, the easiest method would be to use the ncurses library. Why would you prefer not to use it and have to reinvent that wheel?
If you can expect at least VT100 emulation (reasonable) then you can use the basic line drawing, but higher levels have more characters.
It's a bit old, but have a look at the window sample code here:
NCURSES Programming HOWTO: Windows
You may also want to look into the Xterm escape characters (expands the VT100 set):
Xterm Control Sequences

You're looking for box-drawing characters. Here's a complete table.
Assuming your system has a Unicode font installed, which most modern distros do, you could print those to your terminal like this:
#include <wchar.h>
#include <locale.h>
...
setlocale(LC_ALL,"en_US.UTF-8");
wprintf(L"\u250C\u2500\u2510\n"); // ┏━┓
wprintf(L"\u2502 \u2502\n"); // │ │
wprintf(L"\u2514\u2500\u2518\n"); // └━─┘

Related

save and clear terminal window in linux [duplicate]

I'm having a hard time even googling this, because I don't know the right keywords. Some command-line apps (such as vi and less) take over the whole console screen and present an interactive interface to the user. Upon exiting such an app, the screen is returned to the state it was in before the app was launched. I want to write a program that behaves in this fashion, but again, I don't even know what this is called, so I can't find any documentation for how it's accomplished.
So, my question is threefold:
What keywords can I use to find documentation on this?
If you are so inclined, links to such documentation would be helpful.
Lastly, can I accomplish this in a scripting language like Ruby, or even bash? I have no problem with C, but the environment I work in is more amenable to interpreted languages.
As said in some comments, you are looking for ncurses. The Linux Documentation Project have a very good HOWTO on ncurses for C that I used myself to start on it
https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/
The feature you are describing is the alternate screen buffer. I think that [N]Curses will enable this by default. There are certainly curses bindings for Ruby, Python, and other scripting languages.
you can even access ncurses in bash by using the tput program. The whole ncurses library (like curses before it) works by sending escape sequences to the terminal. The xterm program emulates a vt100 terminal (and also a Tektronic terminal) and there were various combinations of characters which would move the cursor, clear the screen, draw various characters etc. These would generally start with an escape character, hence the name: escape sequence. You also sometimes see these escape sequences in people's PS1 shell variables with the \e to provide the escape character; often used to colour the prompt or set the window title.
tput refers to the terminfo database to figure out what the escape sequences are to perform the functions you've asked it to do.
see the manual page, type:
man 5 terminfo
for more details

How does ncurses output non-ascii characters?

I'd like to know how ncurses (a c library) manages to put characters like ├, despite them not (to the best of my knowledge) being part of ASCII.
I would have assumed it was just drawing them pixel by pixel, but you can copy/paste them out of the terminal (in MacOS).
ncurses puts characters such as ├ on the screen by assuming that your locale environment variables (LC_ALL and/or LC_CTYPE) match the terminal on which you are displaying. The environment variables indicate the encoding (e.g., UTF-8). There are other encodings and terminals which support those encodings, but generally speaking you'll mostly see UTF-8. If the environment and terminal cooperate, things "just work":
at startup, ncurses checks for the locale which a program has initialized, via setlocale, and determines if that uses UTF-8. It uses that information later.
when a program adds character strings, e.g., using addstr, ncurses uses the character-type information (set as a side-effect of calling setlocale), and uses standard C library functions for combining sequences of bytes which make up a multi-byte character, and converting those into wide characters. It stores those wide characters internally, and
when writing to the terminal, ncurses reverses the process, converting from wide characters to use the encoding assumed to be supported by the terminal (assuming that your locale environment matches the terminal).
However —
The character indicated ├ happens to be a special case. That is one of the graphic characters used for line-drawing, which predate Unicode and UTF-8. curses has names for these graphic characters, making it simple to refer to them, e.g., ACS_LTEE (the ├ is a left-tee):
Before UTF-8 came along to complicate things, developers came up with a scheme using a table of these graphic characters by adapting the escape sequences used for the VT100 (late 1970s) and the AT&T 4410 and 5410 terminals (apparently the early 1980s since the latter were in use by 1984) for drawing their graphic characters.
AT&T SystemV curses provided support for these graphic characters from the mid-1980s. BSD curses never did that...
Unicode (roughly 1990 and later) provided most of the same glyphs using a different encoding. There are a few omissions (the most noticeable are the scan lines above/below the one used for horizontal lines), but once UTF-8 got into use in the early 2000s, it was logical to extend ncurses to use these characters.
ncurses looks at the locale settings, but prefers using the terminal description for these graphic characters except for cases where that is known to not work — and will assume that the terminal can display the Unicode equivalents for these characters if the terminal is assumed to use UTF-8. It uses a table for this purpose (SystemV curses and its successor X/Open Curses didn't do any of this — NetBSD curses adapted the table from ncurses sometime after 2010).
Further reading:
NCURSES_NO_UTF8_ACS
Line Graphics (in curs_addch(3x))
Line Graphics (in curs_add_wch(3x))
There is more than one version of ncurses, for more than one platform, and if you really want to know, check the source. However, none of them would draw a character pixel-by-pixel; that isn’t something a library running inside a terminal emulator does.
Modern versions of the C standard library, POSIX and ncurses all support writing wide characters to the console and conversion between wide and multibyte strings. Today, wide characters are normally UTF-16 or UTF-32 and multibyte strings are normally UTF-8. You can see the documentation for <wchar.h> and ncursesw for more information.
Note that C11 does have support for UTF-8 literals, through the u8 prefix.
A program that’s concerned about portability with systems where the local multibyte encoding is something other than UTF-8 can use another library such as the C++ standard library or ICU to convert between UTF-8 and wide-character strings, then display those with curses.
You might need to #define _XOPEN_SOURCE 700, or the appropriate value for the version of the standard you are targeting, and with some versions of the libraries, also #define _XOPEN_SOURCE_EXTENDED 1, to get your system libraries to let you use functions such as addwstr().
However, many programs might simply send strings of char encoded in UTF-8 to the console and assume it can handle them. I don’t recommend this approach, but it works on most Linux systems in 2017.

How could I guarantee a terminal has Unicode/wide character support with NCURSES?

I am developing an NCURSES application for a little TUI (text user interface) exercise. Unfortunately, I do not have the option of using the ever-so-wonderful-and-faithful ASCII. My program uses a LOT of Unicode box drawing characters.
My program can already detect if the terminal is color-capable. I need to do something like:
if(!supportsUnicode()) //I prefer camel-case, it's just the way I am.
{
fprintf(stderr, "This program requires a Unicode-capable terminal.\n\r");
exit(1);
}
else
{
//Yay, we have Unicode! some random UI-related code goes here.
}
This isn't just a matter of simply including ncursesw and just setting the locale. I need to get specific terminal info and actually throw an error if it's not gonna happen. I need to, for example, throw an error when the user tries to run the program in the lovely XTerm rather than the Unicode-capable UXTerm.
As noted, you cannot detect the terminal's capabilities reliably. For that matter, you cannot detect the terminal's support for color either. In either case, your application can only detect what you have configured, which is not the same thing.
Some people have had partial success detecting Unicode support by writing a UTF-encoded character and using the cursor-position report to see where the cursor is (see for example Detect how much of Unicode my terminal supports, even through screen).
Compiling/linking with ncursesw relies upon having your locale configured properly, with some workarounds for terminals (such as PuTTY) which do not support VT100 line-graphics when in UTF-8 mode.
Further reading:
Line Graphics curs_add_wch(3x)
NCURSES_NO_UTF8_ACS ncurses(3x)
You can't. ncurses(w) uses termcap to determine what capabilities a terminal has, and that looks at the $TERM environment variable to determine what terminal is being used. There is no special value of that variable that indicates that a terminal supports Unicode; both XTerm and UXTerm set TERM=xterm. Many other terminal applications use that value of $TERM as well, including both ones that support Unicode and ones that don't. (Indeed, in many terminal emulators, it's possible to enable and disable Unicode support at runtime.)
If you want to start outputting Unicode text to the terminal, you will just have to take it on faith that the user's terminal will support that.
If all you want to do is output box drawing characters, though, you may not need Unicode at all — those characters are available as part of the VT100 graphical character set. You can output these characters in a ncurses application using the ACS_* constants (e.g, ACS_ULCORNER for ┌), or use a function like box() to draw a larger figure for you.
The nl_langinfo() function shall return a pointer to a string containing information relevant to the particular language or cultural area defined in the current locale.
#include <langinfo.h>
#include <locale.h>
#include <stdbool.h>
#include <string.h>
bool supportsUnicode()
{
/* Set a locale for the ctype and multibyte functions.
* This controls recognition of upper and lower case,
* alphabetic or non-alphabetic characters, and so on.
*/
setlocale(LC_CTYPE, "en_US.UTF-8");
return (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) ? true : false;
}
Refer to htop source code which can draw lines with/without Unicode.

Print Unicode characters in C, using ncurses

I have to draw a box in C, using ncurses;
First, I have defined some values for simplicity:
#define RB "\e(0\x6a\e(B" (ASCII 188,Right bottom, for example)
I have compiled with gcc, over Ubuntu, with -finput-charset=UTF-8 flag.
But, if I try to print with addstr or printw, I get the hexa code.
What I`m doing wrong?
ncurses defines the values ACS_HLINE, ACS_VLINE, ACS_ULCORNER, ACS_URCORNER, ACS_LLCORNER and ACS_LRCORNER. You can use those constants in addch and friends, which should result in your seeing the expected box characters. (There's lots more ACS characters; you'll find a complete list in man addch.)
ncurses needs to know what it is drawing because it needs to know exactly where the cursor is all the time. Outputting console control sequences is not a good idea; if ncurses knows how to handle the sequence, it has its own abstraction for the feature and you should use that abstraction. The ACS ("alternate character set") defines are one of those abstractions.
A few issues:
if your program writes something like "\e(0\x6a\e(B" using addstr, then ncurses (any curses implementation) will translate the individual characters to printable form as described in the addch manual page.
ncurses supports line-drawing for commonly-used pseudo-graphics using symbols (such as ACS_HLINE) which are predefined characters with the A_ALTCHARSET attribute combined. You can read about those in the Line Graphics section of the addch manual page.
the code 0x6a is ASCII j, which (given a VT100-style mapping) would be the lower left corner. The curses symbol for that is ACS_LRCORNER.
you cannot write the line-drawing characters with addstr; instead addch, addchstr are useful. There are also functions oriented to line-drawing (see box and friends).
running in Ubuntu, your locale encoding is probably UTF-8. To make your program work properly, it should initialize the locale as described in the Initialization section of the ncurses manual page. In particular:
setlocale(LC_ALL, "");
Also, your program should link against the ncursesw library (-lncursesw) to use UTF-8, rather than just ncurses (-lncurses).
when compiling on Ubuntu, to use the proper header definitions, you should define _GNU_SOURCE.
BTW, maybe I'm probably arriving somewhat late to the party but I'll give you some insight that might or not shed some light and skills for your "box drawing" needs.
As of 2020 I'm involved in a funny project on my own mixing Swift + Ncurses (under OSX for now, but thinking about mixing it with linux). Apparently it works flawlessly.
The thing is, as I'm using Swift, internally it all reduces to "importing .h and .c" files from some Darwin.ncurses library the MacOS Xcode/runtime offers.
That means (I hope) my newly acquired skills might be useful for you because apparently we're using the very same .h and .c files for our ncurses needs. (or at least they should be really similar)
Said that:
As of now, I "ignored" ACS_corner chars (I can't find them under swift/Xcode/Darwin.ncurses runtime !!!) in favour of pure UTF "corner chars", which also exist in the unicode pointspace, look:
https://en.wikipedia.org/wiki/Box-drawing_character
What does it mean? Whenever I want to use some drawing box chars around I just copy&paste pure UTF-8 chars into my strings, and I send these very strings onto addstr.
Why does it work? Because as someone also answered above, before initializing ncurses with initscr(), I just claimed "I want a proper locale support" in the form of a setlocale(LC_ALL, ""); line.
What did I achieve? Apparently pure magic. And very comfortable one, as I just copy paste box chars inside my normal strings. At least under Darwin.ncurses/OSX Mojave I'm getting, not only "bounding box chars", but also full UTF8 support.
Try the "setlocale(LC_ALL, ""); initscr();" approach and tell us if "drawing boxes" works also for you under a pure C environment just using UTF8 bounding box chars.
Greetings and happy ncursing!

More Control Over C Standard Output

I can't seem to find the right way to ask the almighty google...
In such programs as a command-line progress bar, the output buffer seems to be directly manipulated. It can't print a character to a terminal in any place it wants. How is such control over a program's output controlled in standard C? Is there a special library that I can look up?
look at curses
it`s a lib for unix/linux
If you just want a progress bar, you can just print a single 'X' for every 2% completion. This should fill 50 characters on a line.
If you want something more fancy, on Linux you can try the classic "curses" library, or if you just want a dialog box, you could try the library that the Debian install utilities use, but I forget its name.
you can do the progress bar with \r check this
for doing more advanced stuff you can use ncurses
It's not part of standard C. These things work by writing some special character sequences that are recognized by the terminal emulator which takes care of the cursor positioning and stuff.
The big gorilla here is the ncurses library, but you can do a lot of cool stuff with less learning curve. Try using \r to move to beginning of line and using simple control sequences to clear to end of line, turn bold on and off, etcetera. The tput(1) command is invaluable. For example, I wrote a simple application the does highlighted text, and to turn highlighting on and off I just called the commands tput smso and tput rmso. You can capture the results using C with popen(3); using a shell it is even easier.
You could use ANSI escape coding to control the termincal output. This is how a lot of MUD games do their output.

Resources