Unexpected behaviour using escape codes in C - c

I am currently trying to create a 'screen' in the console so that whenever the screen is refreshed it is cleared and then re-drawn; an example would be a program like Vim.
So far, I have used escape codes to clear the screen and then reposition the cursor after drawing some row markers. However, when this process is looped, the row markers get drawn below the previous ones, rather than in the same position again.
So if I draw 24 row markers each on a newline, then refresh the screen, I end up with 48 row marks each on a new line, rather than just the row markers in the same position again.
I have provided the example code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void drawRows()
{
for(int i = 0; i < 24; i++) write(STDOUT_FILENO, "~\r\n", 3);
}
void refreshScreen()
{
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
drawRows();
write(STDOUT_FILENO, "\x1b[H", 3);
}
int main()
{
while(1) {
refreshScreen();
getchar();
}
return 0;
}
You can run it, enter some text and press enter. I think this should redraw the ~s in the same position but they just get get drawn below.
What am I doing wrong?

but they just get get drawn below
I guess the behavior you are describing is related that your process is "stuck" on getchar(), so the loop is not executing, because the input is line buffered. You have to set the terminal to raw mode first.
A 10 second google search resulted in https://viewsourcecode.org/snaptoken/kilo/index.html this link, which looks like a good introduction.

Related

Capturing Mouse in Virtual Terminal with ANSI Escape

I've started to learn about ANSI escape sequences online through the magic of Google. It is neat being able to position the cursor \e[row;colH on the screen and set the colors of the outputs (ie: \e[31m).
Next I would like try and see how the mouse able to be captured in a virtual terminal. I realize this code is not portable, and I know I can use ncurses or some other curses library, but the goal here is to learn how it works, not write production code with it.
I have tried \e[?1003h and it starts to fill the screen with mouse events. (Pretty cool!) However, how do I capture these in a C or C++ program?
I saw an example of what I would like to do in PHP: https://stackoverflow.com/a/58390575/1770034
However, when I try to port the code over to something in C it just locks up in the while loop. (Tested with GDB to find that out.)
#include <stdio.h> //bring in printf and fread
int main()
{
system("stty -echo"); //make the terminal not output mouse events
system("stty -icanon"); //put stdin in raw mode
printf("\e[?1003h\e[?1015h\e[?1006h"); //start getting mouse events
char* buffer[255];
while(fread(buffer, 16, 1, stdin)) // <-- suppose to read in the mouse events
{
printf("here"); //Did you actually work?!
}
printf("\e[?1000l"); //Turn off mouse events
system("stty echo"); //Turn echoing of the display back on
return 0; //end the program in a successful state
}
I have also tried scanf and it just locks up until I hit enter, and I'm not convinced it is seeing the mouse events.
Edit
I now have some working code that spits out the mouse events.
Here is the updated code from applying the accepted answer to this question:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
system("stty -echo"); //don't show mouse events on screen
system("stty -icanon");
fprintf(stderr, "\e[?1003h\e[?1015h\e[?1006h"); //use stderr since not buffered turn on mouse event capture
char buffer[16] = " ";
char previousBuffer[16] = " ";
//Make standard in not be blocking
int flags = fcntl(stdin->_fileno, F_GETFL, 0);
fcntl(stdin->_fileno, F_SETFL, flags | O_NONBLOCK);
for (int hunSeconds = 0; hunSeconds < 500; hunSeconds++) //Run for 50 seconds
{
read(fileno(stdin), buffer, 16); //read mouse input
if (strcmp(buffer, previousBuffer) != 0) //only show event if it is different
{
fprintf(stderr, "%s", buffer);
strncpy(previousBuffer, buffer, 16);
}
usleep(100); // sleep for .1 seconds
}
printf("\e[?1000l"); //turn off mouse events
system("stty echo"); //turn on screen echo again
return 0;
}
Two problems:
printf is (using stdout) buffered, so there's no guarantee that the escape sequences got to the terminal before attempting to read.
stdin isn't necessarily the terminal (though it might be). Again, fread is buffered (and you may not get the result as promptly as you wish).
Since stderr is not buffered, it would help to send the escape sequences with that stream. Rather than using fread, it can help to use read, e.g.,
read(fileno(stdin), buffer, 16)

Using 'CLS' command in C causes screen blink

I am trying to clear my console each time I am going to printf something in it (Windows environment with GCC compiler). I am using CygWin and the only way I could manage to do it was with system("cmd /c cls");. That works fine but it causes the screen to blink for a fraction of a second which is obviously annoying.
Is there any alternative way of clearing console screen?
First thing I'd do is stop using cmd to do it. CygWin, assuming you're running somewhere within the shell and not a Windows console, has a "native" option in that you can use either of:
clear
tput clear
to clear the screen, without invoking the external cmd interpreter.
So, from within a program running in CygWin, you can clear the screen with a simple:
system("clear");
Of course, if you don't want to run any external executables, you can achieve the same end with curses. By way of example, the following program clears the screen for you (make sure you include -lcurses at the end of the compilation command):
#include <curses.h>
int main (void) {
WINDOW *w = initscr();
clear(); refresh(); sleep(2);
endwin();
return 0;
}
Don't get hung up on the fact that it's restored on exit, you wouldn't be using this program as a screen clearing stand-alone thing. Instead, the statements would be incorporated into your own program, between the initscr() and endwin() calls, something like:
#include <curses.h>
int main (void) {
char buf[2],
*msg = "Now is the time for all good men to come to lunch.";
WINDOW *w = initscr();
buf[1] = '\0';
clear();
refresh();
while (*msg != '\0') {
buf[0] = *msg++; addstr(buf);
if ((buf[0] == ' ') || (buf[0] == '.')) {
refresh();
sleep(1);
}
}
endwin();
return 0;
}
This program clears the screen using curses then outputs a message in word-sized chunks.
this web page:
http://man7.org/linux/man-pages/man4/console_codes.4.html
contains the common ESC sequences for handling the terminal screen/cursor position, etc
this part of the linked info is probably what you want to implement.
These escape sequences can be placed at the beginning of the buffer that you are using to output your data/text
Of special interest is ESC [ 2 j: which erases the whole screen
J ED Erase display (default: from cursor to end of display).
ESC [ 1 J: erase from start to cursor.
ESC [ 2 J: erase whole display.
ESC [ 3 J: erase whole display including scroll-back
buffer (since Linux 3.0).

Using more lines than the window has with ncurses

I have recently been introduced to ncurses for asynchronous keyboard key listening, and getting on well with it. One issue i'm facing is that you can only have text on the visible screen, no scrollbars. I was wondering if its possible to keep using ncurses as it is so lovely, but have the program still keep the scrollbars rather than getting to the last line and staying there.
scroll(). You have to set scrollok(win, TRUE) first. Actually if you just want to spew data like a normal terminal you only need to set scrollok() by itself.
#include <ncurses.h>
int main(void)
{
int i = 0;
initscr();
scrollok(stdscr,TRUE);
while(1)
{
printw("%d - lots and lots of lines flowing down the terminal\n", i);
++i;
refresh();
}
endwin();
return 0;
}

how to print to console graphically in C on unix

So I'm familiar with printf and the like, but I need to update a single line on the screen without having multiple lines scrolling. I have found libraries to do this in windows (conio.h) but not in unix. I need to be able to run this in cygwin, but any unix examples would be welcome.
I found the following stackoverflow article , but I don't think it quite closes the question for my needs.
Thanks for your help
It depends on whether you're trying to do a text-mode full-screen application (in which case ncurses is probably what you want) or just want to update a single line in-place (e.g., you want to update an "X percent done" indicator from 1 to 100, with all the output appearing on the same line so when X percent done is printed, it prints "over" the previous X-1 percent done indicator). In the latter case, you can write code that's relatively portable, and considerably simpler as well. For example, something like this:
#include <windows.h> // Used only for "Sleep" in our simulated work load
#include <stdio.h>
void do_work() {
// Simulated work load. Just waste some time:
Sleep(100);
}
int main() {
for (int i=0; i<100; i++) {
char buffer[82];
sprintf(buffer, "%d percent done", i+1);
printf("\r%-79s", buffer);
do_work();
}
return 0;
}
You want Ncurses for this. It's a library which allows you to edit any character on the screen.

GNU Readline: how to clear the input line?

I use GNU Readline in the "select" fashion, by registering a callback function like so:
rl_callback_handler_install("", on_readline_input);
And then hooking up rl_callback_read_char as the callback for my select() loop for STDIN_FILENO. That's all pretty standard stuff, and works fine.
Now, my program asynchronously prints messages to the screen, sometimes interleaved with input from the user. A "clean" session would look like this:
user input
SERVER OUTPUT
SERVER OUTPUT
user input
SERVER OUTPUT
But what if the user is midway through a line when the server response arrives? Then it gets ugly:
user input
SERVER OUTPUT
user inSERVER OUTPUT
put
SERVER OUTPUT
I fixed this simply by printing a newline before the server output if the user had typed anything (this is easy to tell by checking rl_line_buffer), and then doing rl_forced_update_display() after printing the server output. Now it looks like this:
user input
SERVER OUTPUT
user in
SERVER OUTPUT
user input
SERVER OUTPUT
This is better, but still not perfect. The problem comes when the user typed an entire line but didn't yet press Enter--then it looks like this:
user input
SERVER OUTPUT
user input
SERVER OUTPUT
user input
SERVER OUTPUT
This is bad because it appears to the user that they typed three commands (three responses for three inputs is just as possible as three responses for two inputs, which is what actually happened).
A nasty hack (which works) is to do this:
user input
SERVER OUTPUT
user input - INCOMPLETE
SERVER OUTPUT
user input
SERVER OUTPUT
I figured I could improve this by printing backspace ('\b') characters instead of " - INCOMPLETE", but that doesn't seem to do anything at all on my terminal (gnome-terminal on Ubuntu Hardy). printf("ABC\b"); just prints ABC, for whatever reason.
So how can I erase the incomplete input line? Either by printing backspaces somehow (I can figure out how many to print--it's strlen(rl_line_buffer)), or by using some Readline facility I don't yet know about?
After quite a lot of hacking I was able to get this mechanism. I hope other people will find it useful. It does not even use select(), but I hope you will get the point.
#include <readline/readline.h>
#include <readline/history.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
const char const* prompt = "PROMPT> ";
void printlog(int c) {
char* saved_line;
int saved_point;
saved_point = rl_point;
saved_line = rl_copy_text(0, rl_end);
rl_set_prompt("");
rl_replace_line("", 0);
rl_redisplay();
printf("Message: %d\n", c);
rl_set_prompt(prompt);
rl_replace_line(saved_line, 0);
rl_point = saved_point;
rl_redisplay();
free(saved_line);
}
void handle_line(char* ch) {
printf("%s\n", ch);
add_history(ch);
}
int main() {
int c = 1;
printf("Start.\n");
rl_callback_handler_install(prompt, handle_line);
while (1) {
if (((++c) % 5) == 0) {
printlog(c);
}
usleep(10);
rl_callback_read_char();
}
rl_callback_handler_remove();
}
With spaces? Try to print "\b \b" for each character you want to "delete" rather than a single '\b'.
Edit
How it works
Suppose you have written "Hello, world!" to the display device and you want to replace "world!" with "Jim."
Hello, world!
^ /* active position */ /* now write "\b \b" */
/* '\b' moves the active position back;
// ' ' writes a space (erases the '!')
// and another '\b' to go back again */
Hello, world
^ /* active position */ /* now write "\b \b" again */
Hello, worl
^ /* active position */ /* now write "\b \b" 4 times ... */
Hello,
^ /* active position */ /* now write "Jim." */
Hello, Jim.
^ /* active position */
Portability
I'm not sure, but the Standard specifically describes the behaviour of '\b' and '\r' as has been described in answers to your question.
Section 5.2.2 Character display semantics
> 1 The active position is that location on a display device where the next character output by
> the fputc function would appear. The intent of writing a printing character (as defined
> by the isprint function) to a display device is to display a graphic representation of
> that character at the active position and then advance the active position to the next
> position on the current line. The direction of writing is locale-specific. If the active
> position is at the final position of a line (if there is one), the behavior of the display devic e
> is unspecified.
>
> 2 Alphabetic escape sequences representing nongraphic characters in the execution
> character set are intended to produce actions on display devices as follows:
> \a (alert) Produces an audible or visible alert without changing the active position.
> \b (backspace) Moves the active position to the previous position on the current line. If
> the active position is at the initial position of a line, the behavior of the display
> device is unspecified.
> \f ( form feed) Moves the active position to the initial position at the start of the next
> logical page.
> \n (new line) Moves the active position to the initial position of the next line.
> \r (carriage return) Moves the active position to the initial position of the current line.
> \t (horizontal tab) Moves the active position to the next horizontal tabulation position
> on the current line. If the active position is at or past the last defined horizontal
> tabulation position, the behavior of the display device is unspecified.
> \v (vertical tab) Moves the active position to the initial position of the next vertical
> tabulation position. If the active position is at or past the last defined vertical
> tabulation position, the behavior of the display device is unspecified.
>
> 3 Each of these escape sequences shall produce a unique implementation-defined value
> which can be stored in a single char object. The external representations in a text file
> need not be identical to the internal representations, and are outside the scope of this
> International Standard.
One thing you can do is to use \r to jump to the beginning of the line for the server output. Then you can use field width specifiers to right pad the output to the rest of the line. This will, in effect, overwrite whatever the user had already entered.
fprintf (stdout, "\r%-20s\n", "SERVER OUTPUT");
You may want to fflush(stdout) to ensure that the buffers are in a consistent state before you do that.
I tried to separate server output and user input with ncurses windows. Server output is simulated with a thread. The program run until You enter a line beginning with 'q'.
#include <unistd.h>
#include <curses.h>
#include <pthread.h>
WINDOW *top, *bottom;
int win_update( WINDOW *win, void *data ){
wprintw(win,"%s", (char*)data ); wrefresh(win);
return 0;
}
void *top_thread( void *data ){
char buff[1024];
int i=0;
while(1){
snprintf(buff, 1024, "SERVER OUTPUT: %i\n", i++ );
use_window( top, win_update, (void*)buff );
sleep(1);
}
return NULL;
}
int main(){
initscr();
int maxy, maxx;
getmaxyx( stdscr, maxy, maxx );
top = newwin(maxy-1,maxx,0,0);
wsetscrreg(top,0,maxy-1); idlok(top,1); scrollok(top,1);
pthread_t top_tid;
pthread_create(&top_tid, NULL, top_thread, NULL);
bottom = newwin(1,maxx,maxy-1,0);
char buff[1024], input[maxx];
do{
werase(bottom); wmove(bottom,0,0);
wprintw(bottom,"input> " ); wrefresh(bottom);
wgetnstr(bottom,input,sizeof(input));
snprintf(buff, 1024, "user input: '%s'\n", input );
use_window( top, win_update, (void*)buff );
}while( input[0] != 'q' );
endwin();
}
Do any of these functions help?
rl_reset_line_state()
rl_clear_message()
rl_delete_text()
rl_kill_text()
Also, can you mediate the server output - have the server output controlled so that it only appears when and where you want it to, rather than just sprawling over what the user is typing? For example, if your application is running in curses mode, could you have a split window with a line or two at the bottom in one sub-window reserved for user input and the rest of the output (server output and accepted user input) in a second sub-window above it?
This also seems to work:
rl_clear_visible_line();
printf(...);
rl_reset_line_state();
rl_redisplay();

Resources