Author Topic: linux, termio strange behaviors, with ncurses  (Read 3857 times)

0 Members and 1 Guest are viewing this topic.

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
linux, termio strange behaviors, with ncurses
« on: August 16, 2015, 08:36:53 pm »
I have learnt that curses is a terminal control library for Unix-like systems, enabling the construction of Text User Interface (TUI) applications, I think I have also understood why it is called like an expressed wish that some form of adversity or misfortune will befall or attach to some other entity (not to me)  :-//

I have the following code, as you can see there are two parts: termio_funny1 and ncurses_demo1

termio_funny1 is a blocking request for a string from the console (standard input), but … it modify a few attributes (e.g. no echo)

running this code
1) without termio_funny1, ncurses_demo1 is working as expected
2) with termio_funny1 ncurses_demo1 is working as expected
3) without ncurses_demo1 termio_funny1 is working as expected
4) with ncurses_demo1 termio_funny1 is NOT working as expected: the console has a strange behavior


I wonder what is wrong with 4), I get a console in where printf seem to be ~ executed out of order  |O
I need to understand what is wrong and to provide a fix

any idea ?


Code: [Select]
int main
(
    int argc,
    char* argv[]
)
{
    ncurses_demo1();
    termio_funny1();
    return 0;
}

Code: [Select]
void termio_funny1()
{
    boolean_t        ans;
    char_t           host_linea[40];
    uint32_t         host_linea_size;
    boolean_t        is_echo;
    char_t           host_prompt[] = "#";

    is_echo         = True;
    host_linea_size = sizeof(host_linea);

    ans = console_linea_get(host_linea, host_linea_size, is_echo, host_prompt);
}

Code: [Select]
private void termios_get_curr
(
p_termios_t p_termios
)
{
    sint32_t         res;
    res = tcgetattr(STDIN_FILENO, p_termios);
}

private void termios_set_curr
(
p_termios_t p_termios
)
{
    sint32_t         res;
    res = tcsetattr(STDIN_FILENO, TCSANOW, p_termios);
}

private void termios_set_work
(
p_termios_t p_termios
)
{
    sint32_t         res;

    res = tcgetattr(STDIN_FILENO, p_termios);
    p_termios->c_lflag    &= ~ICANON;
    p_termios->c_lflag    &= ~ECHO;
    p_termios->c_lflag    &= ~ECHOE;
    p_termios->c_lflag    &= ~ECHOK;
    p_termios->c_lflag    &= ~ECHONL;
    p_termios->c_lflag    &= ~ECHOPRT;
    p_termios->c_lflag    &= ~ECHOKE;
    p_termios->c_lflag    &= ~ISIG;
    p_termios->c_lflag    &= ~ICRNL;
    p_termios->c_cc[VMIN]  = 1;
    p_termios->c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, p_termios);
}

Code: [Select]
boolean_t console_linea_get
(
    uint8_t host_linea[],
    uint32_t host_linea_size,
    boolean_t is_echo,
    uint8_t host_prompt[]
)
{
    boolean_t    ans;
    char_t       ch;
    termios_t    prev_opts;
    p_termios_t  p_prev_opts;
    termios_t    work_opts;
    p_termios_t  p_work_opts;
    sint32_t     arrow;
    sint32_t     i;   
   
    p_prev_opts = get_address(prev_opts);
    p_work_opts = get_address(work_opts);
    termios_get_curr(p_prev_opts);
    termios_set_work(p_work_opts);
   
    printf("%s ", host_prompt);
       
    arrow = 0;
    i     = 0;
    while (i < host_linea_size)
    {
        ch = getchar();
        switch (ch)
        {
              ...
}
    termios_set_curr(p_prev_opts);

    host_linea[i] = '\0';
    return ans;
}

Code: [Select]
void ncurses_demo1()
{       
        int ch;

        initscr();                      /* Start curses mode            */
        raw();                          /* Line buffering disabled      */
        keypad(stdscr, TRUE);           /* We get F1, F2 etc..          */
        noecho();                       /* Don't echo() while we do getch */

        printw("Type any character to see it in bold\n");
        ch = getch();                   /* If raw() hadn't been called
                                         * we have to press enter before it
                                         * gets to the program          */
        if(ch == KEY_F(1))              /* Without keypad enabled this will */
                printw("F1 Key pressed");/*  not get to us either       */
                                        /* Without noecho() some ugly escape
                                         * charachters might have been printed
                                         * on screen                    */
        else
        {       printw("The pressed key is ");
                attron(A_BOLD);
                printw("%c", ch);
                attroff(A_BOLD);
        }
        refresh();                      /* Print it on to the real screen */
        getch();                        /* Wait for user input */
        endwin();                       /* End curses mode                */
}
« Last Edit: August 16, 2015, 08:46:07 pm by legacy »
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: linux, termio strange behaviors, with ncurses
« Reply #1 on: August 16, 2015, 09:34:57 pm »
the FORK does two copies before ncurses gets called, so this ugly solutions works. It really seems to me … ncurses modifies something in the termI/O, the problem is … I do not understand/know what.


Code: [Select]
int main
(
    int argc,
    char* argv[]
)
{
    int pid;
    int status;

    pid = fork();

    if (pid == 0)
    {
        /*
         * child
         */
        ncurses_demo2();
    }
    else
    {
        /*
         * mother
         */
        while (-1 == waitpid(pid, &status, 0))
        {
             /*  wait the child to finish */;
        }
        termio_funny1();
    }

    return 0;
}
 

Offline kc8apf

  • Regular Contributor
  • *
  • Posts: 103
  • Country: us
Re: linux, termio strange behaviors, with ncurses
« Reply #2 on: August 16, 2015, 09:54:37 pm »
The code as provided wouldn't compile.  I cleaned it up with my best guesses and thus was working with the following:
Code: [Select]
#include <curses.h>
#include <stdint.h>
#include <termios.h>
#include <unistd.h>

void ncurses_demo1()
{       
        int ch;

        initscr();                      /* Start curses mode            */
        raw();                          /* Line buffering disabled      */
        keypad(stdscr, TRUE);           /* We get F1, F2 etc..          */
        noecho();                       /* Don't echo() while we do getch */

        printw("Type any character to see it in bold\n");
        ch = getch();                   /* If raw() hadn't been called
                                         * we have to press enter before it
                                         * gets to the program          */
        if(ch == KEY_F(1))              /* Without keypad enabled this will */
                printw("F1 Key pressed");/*  not get to us either       */
                                        /* Without noecho() some ugly escape
                                         * charachters might have been printed
                                         * on screen                    */
        else
        {       printw("The pressed key is ");
                attron(A_BOLD);
                printw("%c", ch);
                attroff(A_BOLD);
        }
        refresh();                      /* Print it on to the real screen */
        getch();                        /* Wait for user input */
        endwin();                       /* End curses mode                */
}


void termios_get_curr(struct termios* p_termios)
{
    int32_t         res;
    res = tcgetattr(STDIN_FILENO, p_termios);
}

void termios_set_curr(struct termios* p_termios)
{
    int32_t         res;
    res = tcsetattr(STDIN_FILENO, TCSANOW, p_termios);
}

void termios_set_work(struct termios* p_termios)
{
    int32_t         res;

    res = tcgetattr(STDIN_FILENO, p_termios);
    p_termios->c_lflag    &= ~ICANON;
    p_termios->c_lflag    &= ~ECHO;
    p_termios->c_lflag    &= ~ECHOE;
    p_termios->c_lflag    &= ~ECHOK;
    p_termios->c_lflag    &= ~ECHONL;
    p_termios->c_lflag    &= ~ECHOPRT;
    p_termios->c_lflag    &= ~ECHOKE;
    p_termios->c_lflag    &= ~ISIG;
    p_termios->c_lflag    &= ~ICRNL;
    p_termios->c_cc[VMIN]  = 1;
    p_termios->c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, p_termios);
}

bool console_linea_get(
uint8_t host_linea[], uint32_t host_linea_size,
    bool is_echo, uint8_t host_prompt[])
{
    bool    ans;
    char       ch;
    struct termios    prev_opts;
    struct termios*  p_prev_opts;
    struct termios    work_opts;
    struct termios*  p_work_opts;
    int32_t     arrow;
    int32_t     i;   
   
    p_prev_opts = &prev_opts;
    p_work_opts = &work_opts;
    termios_get_curr(p_prev_opts);
    termios_set_work(p_work_opts);
   
    printf("%s ", host_prompt);
    fflush(stdout);
       
    arrow = 0;
    i     = 0;
    while (i < host_linea_size - 1)
    {
        ch = getchar();
i++;
        switch (ch)
        {}
}
    termios_set_curr(p_prev_opts);

    host_linea[i] = '\0';
    return ans;
}

void termio_funny1()
{
    bool        ans;
    char           host_linea[40];
    uint32_t         host_linea_size;
    bool        is_echo;
    char           host_prompt[] = "#";

    is_echo         = true;
    host_linea_size = sizeof(host_linea);

    ans = console_linea_get(host_linea, host_linea_size, is_echo, host_prompt);
}

int main(int argc, char* argv[])
{
    ncurses_demo1();
    termio_funny1();
    return 0;
}

I'm not entirely clear on your question but it looks like you expected the prompt to print after the screen cleared (endwin() is called) but before the getch() loop in console_linea_get().  printf(...) is essentially a call to fprintf(stdout, ...).  Normally for all files, stdout included, block buffering is enabled.  fprintf() will write to the buffer which will be flushed once enough characters have been written.  Adding a call to fflush(stdout) after the printf forces it to be written before you start reading input.  You can also disable the buffer on stdout all together with a call to setbuf(stdout, NULL).
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: linux, termio strange behaviors, with ncurses
« Reply #3 on: August 16, 2015, 10:44:49 pm »
Quote
I'm not entirely clear on your question but it looks like you expected the prompt to print after the screen cleared (endwin() is called) but before the getch() loop in console_linea_get().

exactly, the code above is extracted from an other project in where I have implemented a shell which launch functions on demand.
Everything was fine until I introduced a text-editor I have developed with ncurses

Strange behaviors happened, so I wanted to isolate the cause, and I created the little demo above.
because the output of printf does not appear as expected

Quote
printf(...) is essentially a call to fprintf(stdout, ...).  Normally for all files, stdout included, block buffering is enabled.  fprintf() will write to the buffer which will be flushed once enough characters have been written.  Adding a call to fflush(stdout) after the printf forces it to be written before you start reading input.  You can also disable the buffer on stdout all together with a call to setbuf(stdout, NULL).

OK, good explanation. I am putting your tricks on code :D
« Last Edit: August 17, 2015, 10:24:15 am by legacy »
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: linux, termio strange behaviors, with ncurses
« Reply #4 on: August 16, 2015, 11:57:48 pm »

"code view", ncurses (demo mode, just as proof of concept, it's not completed yet)

Code: [Select]
tokener_init: done

# help
help
new parser
[help] kind3 1:1 token_StrictAlphaNum, type20
command (2)
cmd_help()
cmd_list, 50 items
exit................ - byebye
?................... - cmd list
help................ - show specific cmd help
ver................. - version
my.................. - proof, provided by shell_init
idevice............. - swi tap set
iclass.............. - swi tap set
ireg................ - swi tap set
get................. - swi tap reg get
put................. - swi tap reg put
id.................. - swi tap identify
ping................ - swi tap ping
resync.............. - swi tap resync
open................ - swi tap open
close............... - swi tap close
con................. - swi tap connect
tapS................ - swi tap server
scan................ - swi tap client scan
grid................ - swi tap grip
regs................ - swi tap reg show
cv.................. - code view
ans=(1)
#

this is the output of my shell, as you can see I can launch commands, an interesting one is "cv", which launches a "code view", it's a text-editor modified to be code-viever for a debug processor. It runs as built-in function (it lives on the stack) and it has its driver to access the uart and the lan.

If i launch "cv" inside the shell … it will work as expected, the problem happens when it ends and the control returns to the shell … in this case … all the printf experiment a strange behavior, in short you stop to see what you type, then you see a shot with everything shown
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: linux, termio strange behaviors, with ncurses
« Reply #5 on: August 16, 2015, 11:57:58 pm »
Solved, it was the printf buffer, the following code compile on linux (with a classic environment), and works as expected
I have just add a few lines to get_linea's switch, just to obtain a string from the console

thank you  :-+

Code: [Select]
#include <curses.h>
//#include "types.h" /* my env */
#include <stdint.h>
#include <termios.h>
#include <unistd.h>

typedef struct termios   termios_t;
typedef termios_t*       p_termios_t;

void ncurses_demo1()
{
    int ch;

    initscr();                          /* Start curses mode            */
    raw();                              /* Line buffering disabled      */
    keypad(stdscr, TRUE);               /* We get F1, F2 etc..          */
    noecho();                           /* Don't echo() while we do getch */

    printw("Type any character to see it in bold\n");
    ch = getch();                       /* If raw() hadn't been called
                                         * we have to press enter before it
                                         * gets to the program          */
    if (ch == KEY_F(1))                 /* Without keypad enabled this will */
    {
        printw("F1 Key pressed");       /*  not get to us either       */
                                        /* Without noecho() some ugly escape
                                         * charachters might have been printed
                                         * on screen                    */
}
    else
    {
        printw("The pressed key is ");
        attron(A_BOLD);
        printw("%c", ch);
        attroff(A_BOLD);
    }
    refresh();                          /* Print it on to the real screen */
    getch();                            /* Wait for user input */
    endwin();                           /* End curses mode                */
}


void termios_get_curr
(
p_termios_t p_termios
)
{
    //sint32_t res;
    int32_t res;
    res = tcgetattr(STDIN_FILENO, p_termios);
}

void termios_set_curr
(
p_termios_t p_termios
)
{
    //sint32_t res;
    int32_t res;
    res = tcsetattr(STDIN_FILENO, TCSANOW, p_termios);
}

void termios_set_work
(
p_termios_t p_termios
)
{
    //sint32_t res;
    int32_t res;

    res = tcgetattr(STDIN_FILENO, p_termios);
    p_termios->c_lflag    &= ~ICANON;
    p_termios->c_lflag    &= ~ECHO;
    p_termios->c_lflag    &= ~ECHOE;
    p_termios->c_lflag    &= ~ECHOK;
    p_termios->c_lflag    &= ~ECHONL;
    p_termios->c_lflag    &= ~ECHOPRT;
    p_termios->c_lflag    &= ~ECHOKE;
    p_termios->c_lflag    &= ~ISIG;
    p_termios->c_lflag    &= ~ICRNL;
    p_termios->c_cc[VMIN]  = 1;
    p_termios->c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, p_termios);
}

bool console_linea_get
(
    uint8_t host_linea[],
    uint32_t host_linea_size,
    bool is_echo,
    uint8_t host_prompt[]
)
{
    bool           ans;
    char           ch;
    termios_t      prev_opts;
    p_termios_t    p_prev_opts;
    termios_t      work_opts;
    p_termios_t    p_work_opts;
    //sint32_t       arrow;
    //sint32_t       i;
    int32_t       arrow;
    int32_t       i;

    p_prev_opts = &prev_opts;
    p_work_opts = &work_opts;
    termios_get_curr(p_prev_opts);
    termios_set_work(p_work_opts);

    printf("%s ", host_prompt);
    fflush(stdout);

    ans = 0;

    arrow = 0;
    i     = 0;
    while (i < host_linea_size - 1)
    {
        ch = getchar();
        switch (ch)
        {
        case '\n':
            printf("%c", ch);
            fflush(stdout);
            host_linea[i] = '\0';
            ans = 1;
            return ans;
            break;
        default:
            printf("%c", ch);
            fflush(stdout);
            host_linea[i] = ch;
            i++;
            break;
        }
    }
    termios_set_curr(p_prev_opts);

    host_linea[i] = '\0';
    return ans;
}

void termio_funny1()
{
    bool     ans;
    char     host_linea[40];
    uint32_t host_linea_size;
    bool     is_echo;
    char     host_prompt[] = "#";

    is_echo         = true;
    host_linea_size = sizeof(host_linea);

    ans = console_linea_get(host_linea, host_linea_size, is_echo, host_prompt);
}

int main(int argc, char* argv[])
{
    ncurses_demo1();
    termio_funny1();
    return 0;
}
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: linux, termio strange behaviors, with ncurses
« Reply #6 on: August 16, 2015, 11:59:51 pm »
when possible and reasonable, I have also replaced printf with

Code: [Select]
    fputs(string, stdout);

Code: [Select]
    putchar(ch);
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf