Get terminal size using ANSI escape sequences? - c

While researching this problem, in the comments I found someone mentioning ANSI escape codes to get the terminal size. Since I will be using ANSI escape sequences, I thought this would be a much more elegant way to get the terminal size than ioctl() or getenv().
Here is a very good post about ioctl(), and this is the comment that piqued my interest:
Just stumbled upon this answer, and my jaw dropped when I realized that getenv("COLUMNS") works perfectly when running under watch(1). So now I have a set of fallbacks, all from the TIOCWINSZ ioctl, to getenv if not a tty, down to the classic ANSI escape "move cursor to 9999,9999 and the query cursor pos. The last one works on serial consoles for embedded systems :)
Now, I did find some posts (1, 2) about ANSI, but none answer the specific question I have - how to actually do it?
Using this chart, I deduced that in order to move the cursor to the extreme right and down position, I would need the CUP (CSI n ; m H) "Cursor Position" command, which would probably translate to something like \x1b[9999;9999H. This is the easy part since it is a command.
The second part is where I have a big problem. Even if I could deduce that I need the DSR(CSI 6n) "Device Status Report" command, and that it goes \x1b[6n, how does this querying work, i.e., how can I take the data and store it in a variable, preferrably without disrupting the other data that is being displayed on the terminal?

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#define SIZE 100
int main ( void) {
char in[SIZE] = "";
int each = 0;
int ch = 0;
int rows = 0;
int cols = 0;
struct termios original, changed;
// change terminal settings
tcgetattr( STDIN_FILENO, &original);
changed = original;
changed.c_lflag &= ~( ICANON | ECHO);
changed.c_cc[VMIN] = 1;
changed.c_cc[VTIME] = 0;
tcsetattr( STDIN_FILENO, TCSANOW, &changed);
printf ( "\033[2J"); //clear screen
printf ( "\033[9999;9999H"); // cursor should move as far as it can
printf ( "\033[6n"); // ask for cursor position
while ( ( ch = getchar ()) != 'R') { // R terminates the response
if ( EOF == ch) {
break;
}
if ( isprint ( ch)) {
if ( each + 1 < SIZE) {
in[each] = ch;
each++;
in[each] = '\0';
}
}
}
printf ( "\033[1;1H"); // move to upper left corner
if ( 2 == sscanf ( in, "[%d;%d", &rows, &cols)) {
printf ( "%d rows\n", rows);
printf ( "%d cols\n", cols);
}
// restore terminal settings
tcsetattr( STDIN_FILENO, TCSANOW, &original);
return 0;
}

Related

Enabling Newline or Carriage return

I am reading some robotic code and i came across something like Newline and Carriage. What are those two things? I could not find any useful usage related the code itself.
Here is the code
// !! make sure you have enabled Newline or Carriage return
#define _mode 0 // (0) used for calibration and testing, (1) uses serial as input
void handle_serial() {
//: reads and stores the serial data
int i = 0; float buff[3] = {0, 0, 0};
String s_buff = "";
while (Serial.available()) {
char c = Serial.read();
if (c == 13 || c == 32 || c == '\n') {
buff[i] = s_buff.toFloat();
s_buff = "";
i++;
} else
s_buff += c;
}
if (_mode == 0)
commands_exe(buff[0], buff[1], buff[2]);
else if (_mode == 1)
if (state == 4) {
_direction = {buff[0], buff[1]};
turn = buff[2];
}
}
Newline and Carriage. What are those two things?
That's two control characters. Usually expressed as the escape sequence "\n" (new line) and "\r" (carriage return)
https://en.wikipedia.org/wiki/Carriage_return
https://en.wikipedia.org/wiki/Newline
Both have historic reasons. Carriage return moved the printhead back to the beginning of the line. New line moved to the next line.
This is still used in computers to move the cursor around or to make linebreaks when reading text files.
The idea of it in this code is to read bytes until newline, space or carriage return are received. then the bytes received till then is converted to a float.
But as the comments suggest this implementation is bad for many reasons. mainly it is very error prone.
For example you risk to exceeed the boudnaries of your buffer if you don't receive space, newline or carriage return in time.
It is also often used to terminate serial commands so the computer knows when he received a full command, which at the same time allows to display them nicely in a terminal. It is up to the sender to make sure the correct data is sent which in gerneral is a very bad idea.
The only thing you can learn from this snippet is how not do to do it.

Morse code sound effect using Beep() function

I would like to record speaker's output of my morse code. The code is written in C based on Beep function to play the beep sound. I am using Audacity to record the speaker's output. Here is the code :
/* Send character in morse code */
void MCSendChar(unsigned char ch){
unsigned char a = asciiToMC(ch);
unsigned char n = a & 0x07 , j;
for(j = 0; j < n; j++) {
if((0x80 & a) != 0)
Beep(500,40);
else
Beep(500,120);
a = a << 1;
// Inter Symbol spacing
Beep(0,40);
}
// Inter character space
Beep(0,120);
}
I have a confusion about setting the frequency and the wpm. Also, when I tried to decode the recorded file using an online web decoder, the results are not correct at all. Any Ideas ?
Assuming Beep() is the win32 function, from MSDN on the dwFreq parameter:
The frequency of the sound, in hertz. This parameter must be in the range 37 through 32,767 (0x25 through 0x7FFF).
So you must implement the pauses by some other means than using Beep(), for example using Sleep()

SetConsoleScreenBufferInfoEx works different from SetConsoleTextAttribute

Recently I tried to print text in C with underscore. My console doesn't support ANSI escape character so I tried using DBCS, which my console does support. To do so, I had to change the console text attributes. At the beginning I used SetConsoleTextAttribute to change it but later when I wanted to remember the color and ONLY change the underscore I started using GetConsoleScreenBufferInfoEx and SetConsoleScreenBufferInfoEx to also get the previous attributes. That's when I noticed that when I use the former, it only affects the text which I print after the call, and in the case of the latter, I also change the attributes of the previous text.
For example, I wrote 2 short codes and compiled them.
Code 1:
#include <Windows.h>
#include <stdio.h>
int main()
{
printf("Code 1:\n");
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD mode = 0;
int flag = 1;
flag &= GetConsoleMode(out, &mode);
flag &= SetConsoleMode(out, mode | ENABLE_LVB_GRID_WORLDWIDE);
//7 is the default foreground - gray
SetConsoleTextAttribute(out, 7 | COMMON_LVB_UNDERSCORE);
printf("Hello World! 1==%d", flag);
getchar();
SetConsoleTextAttribute(out, 7);
printf("Goodbye World! 1==%d", flag);
getchar();
return 0;
}
And code 2:
#include <Windows.h>
#include <stdio.h>
typedef CONSOLE_SCREEN_BUFFER_INFOEX CSBI;
int main()
{
printf("Code 2:\n");
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD mode = 0;
int flag = 1;
flag &= GetConsoleMode(out, &mode);
flag &= SetConsoleMode(out, mode | ENABLE_LVB_GRID_WORLDWIDE);
CSBI csbi = { 0 };
csbi.cbSize = sizeof(csbi);
flag &= GetConsoleScreenBufferInfoEx(out, &csbi);
csbi.wAttributes |= COMMON_LVB_UNDERSCORE;
flag &= SetConsoleScreenBufferInfoEx(out, &csbi);
printf("Hello World! 1==%d", flag);
getchar();
csbi.wAttributes &= ~COMMON_LVB_UNDERSCORE;
flag &= SetConsoleScreenBufferInfoEx(out, &csbi);
printf("Goodbye World! 1==%d", flag);
getchar();
return 0;
}
the flag is to make sure that all function return TRUE
In the first code, 'Code 1' will remain without underscore, 'Hello World!' will have underscore, and 'Goodbye World!' won't have an underscore.
In the second code, everything will have underscore until I enter a new line, and then everything will lose their underscore.
Does anyone have an idea why is it like that? I though that they will do the same about console text attributes.
Thanks, Roy
In the second code, everything will have underscore until I enter a new line, and then everything will lose their underscore.
After my test, the final effect of the two pieces of code is the same.
Does anyone have an idea why is it like that? I though that they will do the same about console text attributes.
SetConsoleTextAttribute: Sets the attributes of characters written to
the console screen buffer by the WriteFile or WriteConsole function,
or echoed by the ReadFile or ReadConsole function. This function
affects text written after the function call.
SetConsoleScreenBufferInfoEx: Sets extended information about the
specified console screen buffer.
For the comment, on the properties of the console text, SetConsoleTextAttribute and SetConsoleScreenBufferInfoEx can achieve the same effect, such as changing the color of the text or add underscore.

Is it possible to make a loading animation in a Console Application using C?

I would like to know if it is possible to make a loading animation in a Console Application that would always appear in the same line, like a flashing dot or a more complex ASCII animation.
Perhaps like this
#include <stdio.h>
#include <time.h>
#define INTERVAL (0.1 * CLOCKS_PER_SEC) // tenth second
int main(void) {
int i = 0;
clock_t target;
char spin[] = "\\|/-"; // '\' needs escape seq
printf(" ");
while(1) {
printf("\b%c", spin[i]);
fflush(stdout);
i = (i + 1) % 4;
target = clock() + (clock_t)INTERVAL;
while (clock() < target);
}
return 0;
}
The more portable way would be to use termcap/terminfo or (n)curses.
If you send ANSI escape sequences you assume the terminal to be capable of interpreting them (and if it isn't it'll result in a big mess.)
It's essentially a system that describes the capabilities of the terminal (if there's one connected at all).
In these days one tends to forget but the original tty didn't have a way to remove ink from the paper it typed the output on ...
Termcap tutorials are easy enough to find on Google. Just one in the GNU flavor here: https://www.gnu.org/software/termutils/manual/termcap-1.3/html_mono/termcap.html (old, but should still be good)
(n)curses is a library that will allow you control and build entire text based user interfaces if you want to.
Yes it is.
One line
At first if you want to make animation only at one line, you could use putchar('\b') to remove last character and putchar('\r') to return to line beginning and then rewrite it.
Example:
#include
#include
int main() {
int num;
while (1) {
for (num = 1; num <= 3; num++) {
putchar('.');
fflush(stdout);
sleep(1);
}
printf("\r \r"); // or printf("\b\b\b");
}
return 0;
}
But if you want to place it at specified line, you can clear and re-draw every frame, or use libs.
Clearing method
You can do this with system("clear") or with printf("\e[1;1H\e[2J").
After that you'll need to re-draw your frame. I don't recommend this method.
But this is really unportable.
Other libraries
You can use ncurses.h or conio.h depending on system type.
Ncurses example:
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
int main() {
int row, col;
initscr();
getmaxyx(stdscr, row, col);
char loading[] = "-\\|/";
while (1) {
for (int i = 0; i < 8; i++) {
mvaddch(row/2, col/2, loading[i%4]);
refresh();
sleep(1);
mvaddch(row/2, col/2, '\b');
}
}
endwin();
return 0;
}

How to detect key presses in a Linux C GUI program without prompting the user?

how to detect a keyboard event in C without prompting the user in linux? That is the program running should terminate by pressing any key.
can anyone please help with this?
You have to modify terminal settings using termios. See Stevens & Rago 2nd Ed 'Advanced Programming in the UNIX Environment' it explains why tcsetattr() can return successfuly without having set all terminal characteristcs, and why you see what looks to be redundant calls to tcsetattr().
This is ANSI C in UNIX:
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
int checktty(struct termios *p, int term_fd)
{
struct termios ck;
return (
tcgetattr(term_fd, &ck) == 0 &&
(p->c_lflag == ck.c_lflag) &&
(p->c_cc[VMIN] == ck.c_cc[VMIN]) &&
(p->c_cc[VTIME] == ck.c_cc[VMIN])
);
}
int
keypress(int term_fd)
{
unsigned char ch;
int retval=read(term_fd, &ch, sizeof ch);
return retval;
}
int /* TCSAFLUSH acts like fflush for stdin */
flush_term(int term_fd, struct termios *p)
{
struct termios newterm;
errno=0;
tcgetattr(term_fd, p); /* get current stty settings*/
newterm = *p;
newterm.c_lflag &= ~(ECHO | ICANON);
newterm.c_cc[VMIN] = 0;
newterm.c_cc[VTIME] = 0;
return(
tcgetattr(term_fd, p) == 0 &&
tcsetattr(term_fd, TCSAFLUSH, &newterm) == 0 &&
checktty(&newterm, term_fd) != 0
);
}
void
term_error(void)
{
fprintf(stderr, "unable to set terminal characteristics\n");
perror("");
exit(1);
}
void
wait_and_exit(void)
{
struct timespec tsp={0,500}; /* sleep 500 usec (or likely more ) */
struct termios attr;
struct termios *p=&attr;
int keepon=0;
int term_fd=fileno(stdin);
fprintf(stdout, "press any key to continue:");
fflush(stdout);
if(!flush_term(term_fd, p) )
term_error();
for(keepon=1; keepon;)
{
nanosleep(&tsp, NULL);
switch(keypress(term_fd) )
{
case 0:
default:
break;
case -1:
fprintf(stdout, "Read error %s", strerror(errno));
exit(1);
break;
case 1: /* time to quit */
keepon=0;
fprintf(stdout, "\n");
break;
}
}
if( tcsetattr(term_fd, TCSADRAIN, p) == -1 &&
tcsetattr(term_fd, TCSADRAIN, p) == -1 )
term_error();
exit(0);
}
int main()
{
wait_and_exit();
return 0; /* never reached */
}
The nanosleep call is there to prevent the code from gobbling up system resources. You could call nice() and not use nanosleep(). All this does is sit and wait for a keystroke, then exit.
If you want to do that in a graphical application, you should use some libraries to do this.
Such a simple task can be easily done with whatever library (even low level ones like Xlib).
Just choose one and look for a tutorial that shows how to handle keyboard events.
no way with ANSI C. Look at ncurses lib.
Here’s code from /usr/src/bin/stty/key.c:
f_cbreak(struct info *ip)
{
if (ip->off)
f_sane(ip);
else {
ip->t.c_iflag |= BRKINT|IXON|IMAXBEL;
ip->t.c_oflag |= OPOST;
ip->t.c_lflag |= ISIG|IEXTEN;
ip->t.c_lflag &= ~ICANON;
ip->set = 1;
}
}
At a minimum, you have to get out of ICANON mode before your select(2) syscall or your FIONREAD ioctl will work.
I have an ancient, 20-year-old perl4 program that clears CBREAK and ECHO mode this way. It is doing curses stuff without resorting to the curses library:
sub BSD_cbreak {
local($on) = shift;
local(#sb);
local($sgttyb);
# global $sbttyb_t
$sgttyb_t = &sgttyb'typedef() unless defined $sgttyb_t;
# native BSD stuff by author (tsc)
ioctl(TTY,&TIOCGETP,$sgttyb)
|| die "Can't ioctl TIOCGETP: $!";
#sb = unpack($sgttyb_t,$sgttyb);
if ($on) {
$sb[&sgttyb'sg_flags] |= &CBREAK;
$sb[&sgttyb'sg_flags] &= ~&ECHO;
} else {
$sb[&sgttyb'sg_flags] &= ~&CBREAK;
$sb[&sgttyb'sg_flags] |= &ECHO;
}
$sgttyb = pack($sgttyb_t,#sb);
ioctl(TTY,&TIOCSETN,$sgttyb)
|| die "Can't ioctl TIOCSETN: $!";
}
sub SYSV_cbreak {
# SysV code contributed by Jeff Okamoto <okamoto#hpcc25.corp.hp.com>
local($on) = shift;
local($termio,#termio);
# global termio_t ???
$termio_t = &termio'typedef() unless defined $termio_t;
ioctl(TTY,&TCGETA,$termio)
|| die "Can't ioctl TCGETA: $!";
#termio = unpack($termio_t, $termio);
if ($on) {
$termio[&termio'c_lflag] &= ~(&ECHO | &ICANON);
$termio[&termio'c_cc + &VMIN] = 1;
$termio[&termio'c_cc + &VTIME] = 1;
} else {
$termio[&termio'c_lflag] |= (&ECHO | &ICANON);
# In HP-UX, it appears that turning ECHO and ICANON back on is
# sufficient to re-enable cooked mode. Therefore I'm not bothering
# to reset VMIN and VTIME (VEOF and VEOL above). This might be a
# problem on other SysV variants.
}
$termio = pack($termio_t, #termio);
ioctl(TTY, &TCSETA, $termio)
|| die "Can't ioctl TCSETA: $!";
}
sub POSIX_cbreak {
local($on) = shift;
local(#termios, $termios, $bitmask);
# "file statics" for package cbreak:
# $savebits, $save_vtime, $save_vmin, $is_on
$termios_t = &termios'typedef() unless defined $termios_t;
$termios = pack($termios_t, ()); # for Sun SysVr4, which dies w/o this
ioctl(TTY,&$GETIOCTL,$termios)
|| die "Can't ioctl GETIOCTL ($GETIOCTL): $!";
#termios = unpack($termios_t,$termios);
$bitmask = &ICANON | &IEXTEN | &ECHO;
if ($on && $cbreak'ison == 0) {
$cbreak'ison = 1;
$cbreak'savebits = $termios[&termios'c_lflag] & $bitmask;
$termios[&termios'c_lflag] &= ~$bitmask;
$cbreak'save_vtime = $termios[&termios'c_cc + &VTIME];
$termios[&termios'c_cc + &VTIME] = 0;
$cbreak'save_vmin = $termios[&termios'c_cc + &VMIN];
$termios[&termios'c_cc + &VMIN] = 1;
} elsif ( !$on && $cbreak'ison == 1 ) {
$cbreak'ison = 0;
$termios[&termios'c_lflag] |= $cbreak'savebits;
$termios[&termios'c_cc + &VTIME] = $cbreak'save_vtime;
$termios[&termios'c_cc + &VMIN] = $cbreak'save_vmin;
} else {
return 1;
}
$termios = pack($termios_t,#termios);
ioctl(TTY,&$SETIOCTL,$termios)
|| die "Can't ioctl SETIOCTL ($SETIOCTL): $!";
}
sub DUMB_cbreak {
local($on) = shift;
if ($on) {
system("stty cbreak -echo");
} else {
system("stty -cbreak echo");
}
}
And it elsewhere says that for POSIX,
($GETIOCTL, $SETIOCTL) = (TIOCGETA, TIOCSETA);
RE-translation back into the original C is left as an exercise for the reader, because I can't remember where the 20-years-ago-me snagged it from originally. :(
Once you're out of ICANON mode on the tty, now your select(2) syscall works properly again. When select's read mask returns that that descriptor is ready, then you do a FIONREAD ioctl to discover exactly how many bytes are waiting for you on that file descriptor. Having got that, you can do a read(2) syscall for just that many bytes, preferably on an O_NONBLOCK descriptor, although by now that should no longer be necessary.
Hm, here’s a foreboding note in /usr/src/usr.bin/vi/cl/README.signal:
Run in cbreak mode. There are two problems in this area. First, the
current curses implementations (both System V and Berkeley) don't give
you clean cbreak modes. For example, the IEXTEN bit is left on, turning
on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with
the exception that flow control and signals are turned on, and curses
cbreak mode doesn't give you this.
We can either set raw mode and twiddle the tty, or cbreak mode and
twiddle the tty. I chose to use raw mode, on the grounds that raw
mode is better defined and I'm less likely to be surprised by a curses
implementation down the road. The twiddling consists of setting ISIG,
IXON/IXOFF, and disabling some of the interrupt characters (see the
comments in cl_init.c). This is all found in historic System V (SVID
3) and POSIX 1003.1-1992, so it should be fairly portable.
If you do a recursive grep for \b(TIOC[SG]ET[NP]|TC[SG]ET[SA]|tc[sg]etattr)\b on the non-kernel portions of /usr/src/, you should find stuff you can use. For example:
% grep -Pr '\b(TIOC[SG]ET[NP]|TC[SG]ET[SA]|tc[sg]etattr)\b' /usr/src/{{s,}bin,usr.{s,}bin,etc,gnu}
I would look at /usr/src/usr.bin/less/screen.c, down in the raw_mode() function. Riddled with ifdefs though it is in a quest for portability, that looks like the cleanest code for what you want to do. There’s also stuff lurking down in GNU.
OH MY, look in /usr/src/gnu/usr.bin/perl/h2pl/cbreak.pl! That must be my old code that I posted above. Interesting that it’s trickled out to every src system in the world. Scary, too, since it is twenty years out of date. Gosh, it's weird to see echoes of a younger self. Really hard to remember such particulars from 20 years ago.
I also see in /usr/src/lib/libcurses/term.h this line:
#define tcgetattr(fd, arg) ioctl(fd, TCGETA, arg)
in a bunch of ifdefs that are trying to infer termio or termios availability.
This should be enough to get you started.

Resources