#include } int main(){ termios termattr_bak; termios termattr; /* swi" /> #include } int main(){ termios termattr_bak; termios termattr; /* swi" /> #include } int main(){ termios termattr_bak; termios termattr; /* swi"/>

artifacts appearing in VT100 raw mode

84 Views Asked by At

considering this code:

#include<cstdio>
extern"C"{
#include<termios.h>
#include<unistd.h>
}
int main(){
    termios termattr_bak;
    termios termattr;
/*  switch to raw mode*/
    tcgetattr(STDIN_FILENO,&termattr_bak);
    termattr.c_iflag |= IGNBRK;
    termattr.c_iflag &= ~unsigned(INLCR|ICRNL|IXON|IXOFF);
    termattr.c_lflag &= ~unsigned(ICANON|ECHO|ECHOK|ECHOE|ECHONL|ISIG|IEXTEN);
    termattr.c_cc[VMIN] = 1;
    termattr.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO,TCSAFLUSH,&termattr);
/*  */
    printf("test\r\n");
    printf("test\r\n");
    printf("test\r\n");
    fflush(stdout);
/*  restore terminal attributes*/
    tcsetattr(STDIN_FILENO,TCSAFLUSH,&termattr_bak);
    return 0;
}

the program compiles with g++ main.cc and works just as expected around 80% of the time with this trace:

test
test
test

but sometimes, randomly, the trace is like this:

test

test

test

or this:

test
    
    test
        
        test
        
        

or even those above with capital letters (!?) like

TEST
TEST
TEST

and so on.

I just run the same program again and again in the same terminal with randomly the different behaviors above. It is the same under both gnome-terminal and xterm.

I tried to fflush and/or tcflush and/or tcdrain at different locations with no success.

What is happening here and how do I prevent this ?

EDIT:

It seems that gcc -Og main.cc produces a different code that works correctly. So it looks to me like something is beeing broken by the optimisations, which means I am doing something wrong with the code which only shows up when optimized (but What ?), or less likely there is a bug with GCC

EDIT2:

As SergeBallesta mentionned, I was not initializing the termios structure before use, which was leading the random problems above. This one works :

#include<cstdio>
extern"C"{
#include<termios.h>
#include<unistd.h>
}
int main(){
    termios termattr_bak;
    termios termattr{};//<<<<<<<<<<<<<<< zero-init the struct
/*  switch to raw mode*/
    tcgetattr(STDIN_FILENO,&termattr_bak);
    termattr.c_iflag |= IGNBRK;
    termattr.c_iflag &= ~unsigned(INLCR|ICRNL|IXON|IXOFF);
    termattr.c_lflag &= ~unsigned(ICANON|ECHO|ECHOK|ECHOE|ECHONL|ISIG|IEXTEN);
    termattr.c_cc[VMIN] = 1;
    termattr.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO,TCSAFLUSH,&termattr);
/*  */
    printf("test\r\n");
    printf("test\r\n");
    printf("test\r\n");
    fflush(stdout);
/*  restore terminal attributes*/
    tcsetattr(STDIN_FILENO,TCSAFLUSH,&termattr_bak);
    return 0;
}
1

There are 1 best solutions below

0
Dúthomhas On

You should not be zero-initializing the termios structure. Get the current settings, make a copy, then adjust it for raw mode using cfmakeraw().

#define _DEFAULT_SOURCE
#include <termios.h>
#include <unistd.h>

struct termios initial_settings;
struct termios raw_settings;

void finalize()
{
  printf( "\033[?1049l" );
  tcsetattr( STDIN_FILENO, &initial_settings );
}

bool initialize()
{
  if (tcgetattr( STDIN_FILENO, &initial_settings ) < 0) 
    return false;

  raw_settings = initial_settings;
  cfmakeraw( &raw_settings );

  // adjust raw_settings if needed here          //
  // for example, for non-blocking, polling I/O: //
  // raw_settings.c_cc[VMIN] = 0;
  // raw_settings.c_cc[VTIME] = 0;

  if (tcsetattr( STDIN_FILENO, TCSANOW, &raw_settings ) < 0) 
    return false;

  // more initializations here, like: //
  printf( "\033[?1049h" );  // alternate screen buffer mode
  ...

  // Make sure to undo all the changes at program exit
  atexit( finalize );
}