I'm writing an ncurses application, and there's a strange issue with how special characters are printed to the screen. Here's an example:
#include <ncurses.h>
int main(int argc, char *argv[])
{
initscr();
noecho();
keypad(stdscr, TRUE);
cbreak();
curs_set(0);
addch(ACS_LARROW);
addch(' ');
addch(ACS_UARROW);
addch(' ');
addch(ACS_DARROW);
addch(' ');
addch(ACS_RARROW);
addch(' ');
refresh();
getch();
endwin();
return 0;
}
So, when I run this on a tty, the characters are correctly printed as arrows (←, ↑, ↓, →), but when I try and run this on a terminal (I've tried on gnome-terminal and LXTerminal) this is the output:
< ^ v >
Is there any reason for this difference? I thought it might be font related, but I'm really out of my territory here, and my googling didn't help.
Any suggestion on how to force lxterminal (or any other terminal) to output the same characters of the tty?
ncurses decides which character to output for ACS_LARROW and friends based on your termtype. It's likely that in a tty your termtype is set to 'linux', whereas in gnome-terminal, etc it'll most likely be set to 'xterm'. Although I'm not certain, it's quite possible that the xterm termtype doesn't support these characters.
You could try running you application as so:
env TERM=linux ./a.out
Other termtypes to try are gnome, rxvt and vt102. These will output extended ASCII characters and your terminal emulator should support them. You could also try rxvt-unicode if you have it installed, that should output the correct unicode codepoints for these special symbols.
terminal emulations are sometimes coded in 7bit ASCII, and there is no corresponding Value for the arrows (with line and wedge) in this code page, so the terminal displays what comes near (that is: only the wedge).
on tty you have all the capacities of you computer (UTF-8, color encoding, ...), so the terminal can draw the arrow.
The setting of TERM is largely irrelevant. What matters is
the locale
the choice of library
the particular terminal emulator
The usual reason for this is that the program is built-with and linked-to the ncurses library rather than ncursesw:
if you use the former (ncurses) in a locale using UTF-8, ncurses will use ASCII graphics simply because it cannot draw UTF-8. ncursesw can draw using UTF-8 characters.
using ncursesw, the program will use UTF-8 as needed (since 2002, it can use either the terminfo information, or a built-in table of Unicode values). The Linux console and screen are known special cases where the terminfo cannot describe the behavior of the terminal. For others (terminals which do not support VT100 line-drawing in UTF-8 mode), you would set NCURSES_NO_UTF8_ACS.
the terminal database does in fact have an extended capability addressing the lack of VT100 compatibility vs UTF-8 (see U8 note in the terminal database). However, most people set TERM to xterm or linux and often equate those (see note in xterm FAQ).
Related
I'm new to C and I came across this code and it was confusing me:
sprintf(banner1, "\e[37╔═╗\e[37┌─┐\e[37┌┐┌\e[37┌─┐\e[37┌─┐\e[37┌─┐\e[37┌─┐\e[37m\r\n");
sprintf(banner2, "\e[37╠═╝\e[37├─┤\e[37│││\e[37│ ┬\e[37├─┤\e[37├┤\e[37 ├─┤\e[37m\r\n");
sprintf(banner3, "\e[37╩ \e[37┴ ┴┘\e[37└┘\e[37└─┘\e[37┴ ┴\e[37└─┘\e[37┴ ┴\e[37m\r\n");
I was just confused as I don't know what do \e[37 and \r\n mean. And can I change the colors?
This looks like an attempt to use ANSI terminal color escapes and Unicode box drawing characters to write the word "PANGAEA" in a large, stylized, colorful manner. I'm guessing it's part of a retro-style BBS or MUD system, intended to be interacted with over telnet or ssh. It doesn't work, because whoever wrote it made a bunch of mistakes. Here's a corrected, self-contained program:
#include <stdio.h>
int main(void)
{
printf("\e[31m╔═╗\e[32m┌─┐ \e[33m┌┐┌\e[34m┌─┐\e[35m┌─┐\e[36m┌─┐\e[37m┌─┐\e[0m\n");
printf("\e[31m╠═╝\e[32m├─┤ \e[33m│││\e[34m│ ┬\e[35m├─┤\e[36m├┤ \e[37m├─┤\e[0m\n");
printf("\e[31m╩ \e[32m┴ ┴┘\e[33m┘└┘\e[34m└─┘\e[35m┴ ┴\e[36m└─┘\e[37m┴ ┴\e[0m\n");
return 0;
}
The mistakes were: using \r\n instead of plain \n, leaving out the m at the end of each and every escape sequence, and a number of typos in the actual letters (missing spaces and the like).
I deliberately changed sprintf(bannerN, ... to printf to make it a self-contained program instead of a fragment of a larger system, and changed the actual color codes used for each letter to make it a more interesting demo. When I run this program on my computer I get this output:
The program will only work on your computer if your terminal emulator supports both ANSI color escapes and printing UTF-8 with no special ceremony. Most Unix-style operating systems nowadays support both by default; I don't know about Windows.
I'm trying that a C console application can read (using the keyboard) special Spanish characters such as accents, 'ñ', etc in a scanf or gets and then, print it too with printf.
I have achieved to show these characters correctly (stored in a variable or, directly, from printf) thanks to the package locale.h. I show an example:
#include <stdio.h>
// Add languaje package
#include <locale.h>
int main(void)
{
char string[254];
// Set languaje to Spanish
setlocale(LC_ALL, "spanish");
// Show correctly spanish special chars
printf("¡Success!. It is shown special chars like 'ñ' or 'á'.\n\n\n");
// Gets special chars by keyboard
printf("Input spanish special chars (such 'ñ'): ");
gets(string);
printf("Your string is: %s", string);
return 0;
}
but I have not yet achieved to pick them up correctly with the functions mentioned above.
Does anyone know how to do it?
Thank you.
EDIT 1:
In testing, I observed that:
setlocale(LC_ALL, "spanish"); It shows the characters of the Spanish correctly, but it does not collect them from the keyboard.
setlocale(LC_ALL, "es_ES"); It picks up the Spanish characters correctly from the keyboard, but it does not show them well.
EDIT 2:
I have tryed too setlocale(LC_ALL, "");, setlocale(LC_ALL, "es_ES.UTF-8"); and setlocale(LC_ALL, "es_ES.ISO_8859-15"); with the same results as EDIT 1 (or catch well characters from keyboard or show them well in console, but never both at the same time).
Microsoft's C runtime library (CRT) does not support UTF-8 as the locale encoding. It only supports Windows codepages. Also, "es_ES" isn't a valid CRT locale string, so setlocale would fail, leaving you in the default C locale. Newer versions of Microsoft's CRT support Windows locale names such as "es-ES" (hyphen, not underscore). Otherwise the CRT uses the full names or the old 3-letter abbreviations, e.g. "spanish_spain", "esp_esp" or "esp_esp.1252".
But that's not the end of the story. When reading from and writing to the console using legacy text encodings instead of Unicode, there's another layer of translation in the console itself. To avoid mojibake, you have to set the console input and output codepages (i.e. SetConsoleCP and SetConsoleOutputCP) to match the locale codepage. If you're limited to Spanish or Latin-1, then it should work to set the locale to "spanish" and set the console codepages via SetConsoleCP(1252) and SetConsoleOutputCP(1252). More generally you could look up the ANSI codepage for a given locale name, set the console codepages, and save them in order to reset the console at exit. For example:
wchar_t *locale_name = L"es-ES";
if (_wsetlocale(LC_ALL, locale_name)) {
int codepage;
gPrevConsoleCP = GetConsoleCP();
if (gPrevConsoleCP) { // The process is attached to a console.
gPrevConsoleOutputCP = GetConsoleOutputCP();
if (GetLocaleInfoEx(locale_name,
LOCALE_IDEFAULTANSICODEPAGE |
LOCALE_RETURN_NUMBER,
(LPWSTR)&codepage,
sizeof(codepage) / sizeof(wchar_t))) {
if (!codepage) { // The locale doesn't have an ANSI codepage.
codepage = GetACP();
}
SetConsoleCP(codepage);
SetConsoleOutputCP(codepage);
atexit(reset_console);
}
}
}
That said, when working with the console you will be better off in general if you set stdin and stdout to use _O_U16TEXT mode and use wide-character functions such as fgetws and wprintf. Ultimately, if supported by the C runtime library, this should use the wide-character console I/O functions ReadConsoleW and WriteConsoleW. The downside of using UTF-16 wide-character mode is that it would entail a complete rewrite of your code to use wchar_t strings and wide-character functions and also would require implementing adapters for libraries that work with multibyte encoded strings (preferably UTF-8).
I am developing an ncurses application myself in C. The problem is that putty displays alternative character set characters like ACS_VLINE as letters. My locale is
LANG=en_US.UTF-8
and I have set
export NCURSES_NO_UTF8_ACS=1
I have also set putty to UTF-8 and tried different fonts. The characters display fine on the tty on the actual machine so I think the issue is with putty. I have also tried linking ncursesw instead of ncurses.
It is a combination of things. The recommended TERM for PuTTY is "putty", but due to inertia, most people use "xterm". The line-drawing support in the xterm terminal description is different from PuTTY's assumptions because xterm supports luit, which has some limitations with the way the alternate character set is managed (see Debian Bug report #254316:
ncurses-base: workaround for screen's handling of register sgr0 isn't quite right).
If you use infocmp to compare, you may see these lines which deal with the alternate character set:
acsc: '``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~', '``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~'.
enacs: NULL, '\E(B\E)0'.
rmacs: '\E(B', '^O'.
smacs: '\E(0', '^N'.
VT100s can have two character sets "designated", referred to as G0 and G1:
The "xterm" line-drawing works by changing the designation of G0 between the ASCII and line-drawing characters,
the "putty" line-drawing works by designating ASCII in G0 and line-drawing in G1 and switching between the two with the shift-in/shift-out control characters.
Although both are VT100-compatible, the xterm scheme does not work with PuTTY. Because of the tie-in with luit, the normal terminal description for xterm will not change (unless it proves possible to modify luit to solve the problem for its users), so a workaround is needed for users of PuTTY:
use a different terminal description as recommended, e.g., TERM=putty. PuTTY's settings dialog lets you set environment variables to pass to the remote machine.
This has the drawback that some systems do not have the full ncurses terminal database installed, due to "size" (it is 6.8Mb on my local machine). Also, TERM may not be on the list of allowed ssh environment variables.
you can compile your own terminfo entry with ncurses' tic, e.g.,
cat >foo <<"EOF"
xterm|my terminfo entry,
enacs=\E(B\E)0,
rmacs=^O,
smacs=^N,
use=xterm-new,
EOF
tic foo
use GNU screen. It does its own fixes, and happens to compensate for PuTTY's problems.
Further reading
SCS – Select Character Set (VT100 manual)
4.4 Character Set Selection (SCS) (VT220 manual)
The terminfo database is big—do I need all of that? (ncurses FAQ)
Putty: login, execute command/change environment variable, and do NOT close the session
I want to make some additions. I faced same issue: many ncurses-based tools like dialog, menuconfig and nconfig from Linux kenrel sources, even mc is broken when built with ncurses (although mc is built using Slang on many OSes and not affected).
Here is what happened
ncurses uses smacs record from terminfo to switch to "alternative charset" and then it uses acsc to draw boxes. It sends a which is box-drawing character in alternative charset (ACS).
This is VT100 graphics.
Some terminal emulators nowadays do not support ACS when in UTF-8 because apps have ability to send real box-drawing codepoints.
There is unofficial capability U8 (capital U!) in terminfo that tells ncurses: "Instead of ACS use real box-drawing codepoints."
I have this capability infocmp -x xterm-utf and for putty aswell, but not for xterm.
As you can read in ncurses(3) (https://invisible-island.net/ncurses/man/ncurses.3x.html), ncurses is aware of Linux console and GNU screen (and tmux, which also uses screen as TERM) and always behave like if U8 were set.
For other terminals that do not support ACS when in UTF, you can set NCURSES_NO_UTF8_ACS.
Unfortunatelly, ncurses is not aware of putty.
There is also luit that may convert ACS to Unicode points.
So, here is what we can do to run ncurses + putty in UTF-8:
Use terminal with U8#1 capability. This one is set for putty (btw, I suggest to use putty-256color instead). You can create your own entry with U8#1 and colors#256 and compile it with tic -x. Be carefull that mouse may not work on terminals that do not start with xterm (see mouseinterval(3), BUGS section). This is why I do not use putty terminal. I suggest to copy xterm-utf8, add colors#256, compile and stay with it: it works perfectly with putty, mouse and utf8.
You can set NCURSES_NO_UTF8_ACS in your profile.
You can run screen or tmux: it will set TERM to screen and fix ncurses
You can run luit: it will do all convertions for you.
Since putty 0.71 you can ask putty to support ACS drawings even in UTF-8
Is this possible?
I know it's possible in the command prompt using COLOR ##
but is it possible in C using bloodshed?
Thanks,
What operating system? What terminal do you have available? Note that this has nothing to do with C, let alone bloodshed. You output a string which the terminal may or may not choose to interpret as a color. You have to see how to do that with your terminal. The solution of course is not portable. One such example for a terminal supporting escape sequences is
printf("\\x1b[1;33mThis is yellow\\x1b[m(Back to default)\n");
You may be interested in ANSI terminal's color escape sequences
You may also want to look for libraries that do that for limited number of terminals. For example, ncurses could help you in Linux.
If you're on *nix, osx, or using cygwin msys on windows, your terminal should support the ANSI sequences Fred Larson mentions (not sure about osx). The normal windows terminal does not. But bloodshed can use cygwin, so you're in luck.
Here's an example:
#include <stdio.h>
#define BOLDMAGENTA "\033[1;35m"
#define BOLDGREEN "\033[1;32m"
int main(void) {
printf("%shello %sworld\n", BOLDMAGENTA, BOLDGREEN);
return 0;
}
Note that this leaves the terminal in bright green, but if your prompt sets colours, that will get reset.
Here's some explanation of ANSI escape codes:
http://en.wikipedia.org/wiki/ANSI_escape_code
Is it possible to color the console output in just plain ANSI C? Without an external library? Can this be done in Windows, Linux, or Mac OS X?
just plain ANSI C?
No. The C standard doesn't assume the stdout is a console or has color.
Can this be done in Windows, Linux, or Mac OS X?
Yes. See How can I print to the console in color on Mac OS X in a cross-platform manner? for Linux and Mac OS X.
For Windows, you may need to directly access the Console Functions if you want to avoid external libraries.
Yes, in Linux/ Mac it is possible using ANSI C89.
You can either manipulate the font and the color of the text.
using the following command:
printf("%c[0;00mHello, world!\n", 27); /* White color */
printf("%c[1;33mHello, world!\n", 27); /* Yellowish color */
printf("%c[1;34mHello, world!\n", 27); /* Blueish color */
Notice that the left part of the ";" (where the numbers 0, 1 are) manipulates the text font,
the right part of ";" manipulates the colors. You can experiment on your own and find out new colors.
This code compiles using "-ansi -pedantic" command with no warnings nor errors.
***** Edit *****
In Windows based systems you can achieve colorful console text/background of text using the following example:
#include <stdio.h>
#include <windows.h>
int main(void)
{
/* Point to our console */
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
int i = 0;
/* Iterate through colors */
for(; i < 255; i++)
{ /* i stands for color type: could refer to actual text color or background color of text */
SetConsoleTextAttribute(hConsole, i);
printf("Colorful text");
}
getchar();
return 0;
}
Good luck!
in Linux this can be done, if you you know the shell-specific control codes / Escape sequences.
Linux/OSX/Unix
On posix systems you can use the ANSI escape sequences.
Windows
On windows it is a bit more complicated, there are multiple solutions:
Win32 API
Using the Win32 API to set the output color before printing to the console using SetConsoleTextAttribute and friends. This is a lot more cumbersome than simply embedding ANSI escape sequences in your strings, and requires you to handle Windows as a special case.
Windows ANSI.SYS and Replacement
Older version of windows contained ANSI.SYS, but this has been removed in later versions. ANSICON is a replacement for this that you can install to get ANSI color code support in the windows command prompt: https://github.com/adoxa/ansicon
Embeddable no external dependencies solution
Here is a project that can be easily integrated into any existing project without relying on ANSI.SYS or ANSICON being installed.
It takes a string containing ANSI escape sequences and translates them to the relevant Win32 equivalent API functions: https://github.com/mattn/ansicolor-w32.c
It is true that ISO C knows nothing about the console being capable of displaying colors, however there is an ANSI norm for console capabilities management, based on escape character controls. This works transparently in Linux and Mac OS X, however it fails in Windows, in which you need to use the primitives of the Win32 API.
You can find below a very simple library that allows to clear the screen, show colors and locate the cursor in a specific coordinate, in a multiplatform way (Win32 & Unix-like systems).
It comes with plain C source files (.c and .h), doxygen documentation in Spanish (doc/), and a simple demo (main.c)
http://github.com/Baltasarq/cscrutil/