Question

I asked a question yesterday and you guys were very helpful. Another issue popped up after that and this time it's regarding ncurses. Whenever I move the arrow (<) around, unwanted text pops up after the cursor. Image of ncurses output

The arrow right now is in the first column to the right of the p. It seems that whatever key I press shows up after the cursor. I'm running the program in Cygwin if that makes a difference.

Code:

    #include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <ncurses/ncurses.h>

#define MAXWORDS 100
#define WORDLEN 11
#define DEBUG 0   // set to 0 to disable debug output

#define ARROW '<'
#define BLANK ' '

//PROTOTYPES
int readWords(char *wl[MAXWORDS], char* filename); // reads words from the file into wl and trims the whitespace off of the end
void trimws(char* s); //modifies s to trim white space off the right side
void printWords(char* words[20][5], int wordCount, int rows, int columns);
void draw_character(int x, int y, char use);
void read_dir(int* time, int* b_down, int* b_up, int* b_left, int * b_right, int* dir_down, int* dir_up, int* dir_left, int* dir_right);
void printSentence(char* sentence[MAXWORDS], int position);

struct arrowlocation{
    int row;
    int column;
}arrowloc;

int main(int argc, char* argv[]) {
    char* wordlist[MAXWORDS];
    char* sentence[MAXWORDS];
    char space[]=" \0";
    int wordCount;
    int i=0,j=0,k=0;
    int columns=5, rows;
    int row=0,column=16; //initial position for '<'
    int prevrow, prevcolumn;
    int time, b_down, b_up, b_left, b_right, dir_down, dir_up, dir_left, dir_right;
    int position=0;
    wordCount = readWords(wordlist, argv[1]);

    struct arrowlocation arrowloc;
    arrowloc.row=0;
    arrowloc.column=0;

    //DEBUGGING
    if (DEBUG) {
        printf("Read %d words from %s \n",wordCount, argv[1]);
        for (i = 0; i< wordCount; i++) {
            printf("%s,", wordlist[i]);
        }
        printf("\n\n");
    }

    //Calculates number of rows needed
    if ((((double)wordCount)/columns)>((double)(wordCount/columns))){
        rows=(wordCount/5)+1;
    }
    else{
        rows=(wordCount/5);
    }

    char* words[rows][columns];

    //Converts the list of words to a 2D array table
    for(i=0; i<rows; i++){
        for(j=0; j<columns; j++){
            if (k==wordCount){
                break;
            }
            words[i][j]=wordlist[k];
            k++;
        }
    }

    //Starts ncurses screen
    initscr();
    refresh();


    printWords(words, wordCount, rows, columns);
    refresh();

    draw_character(row, column, ARROW);

    int c=0;
    do{
        refresh();
        c = wgetch(stdscr);

        refresh();
        switch(c){
            case 66:
                //move the arrow down one
                if((arrowloc.row)<20){
                    draw_character(row, column, BLANK);
                    row=row+1;
                    arrowloc.row=arrowloc.row+1;
                    draw_character(row, column, ARROW);

                    //mvprintw(27, 0, "arrow loc; row:%d column:%d",arrowloc.row, arrowloc.column);
                    refresh();
                }
                break;
            case 65:
                //move the arrow up one
                if((arrowloc.row)>0){
                    draw_character(row, column, BLANK);
                    row=row-1;
                    arrowloc.row=arrowloc.row-1;
                    draw_character(row, column, ARROW);

                    //mvprintw(27, 0, "arrow loc; row:%d column:%d",arrowloc.row, arrowloc.column);
                    refresh();
                }
                break;
            case 68:
                //move the arrow left one
                if((arrowloc.column)>0){
                    draw_character(row, column, BLANK);
                    column=column-15;
                    draw_character(row, column, ARROW);

                    arrowloc.column--;
                    //mvprintw(27, 0, "arrow loc; row:%d column:%d",arrowloc.row, arrowloc.column);
                }
                break;
            case 67:
                //move the arrow right one
                if((arrowloc.column)<5){
                    draw_character(row, column, BLANK);
                    column=column+15;
                    draw_character(row, column, ARROW);

                    arrowloc.column++;
                    //mvprintw(27, 0, "arrow loc; row:%d column:%d",arrowloc.row, arrowloc.column);
                }
                break;
            case 113:
                //Left button adds word to sentence without space at end
                //if (b_left==1){
                    sentence[position]=words[arrowloc.row][arrowloc.column];
                    position++;
                    printSentence(sentence, position);
                //}
                break;
            case 119:
                //Up button adds word to sentence with a space at the end
                //if (b_up==1){
                    sentence[position]=words[arrowloc.row][arrowloc.column];
                    position++;
                    sentence[position]=&space[0];
                    position++;
                    printSentence(sentence, position);
                //}
                break;
            case 101:
            //Down button acts as a backspace
            //else if (b_down==1){
                position--;
                printSentence(sentence, position);
            //}
                break;
            default:
                //mvprintw(24, 0, "Charcter pressed is = %3d , c, c);
                refresh();
                break;
        }



    } while(1);


    endwin();

}

void printSentence(char* sentence[MAXWORDS], int position){
    int i=0, x=0;
    //clrtoeol();
    for(i=0; i<position; i++){
        mvprintw(22, x, "%s", sentence[i]);
        //mvprintw(23, x, "%d", i);
        x+=strlen(sentence[i]);
    }
    refresh();

}

void printWords(char* words[20][5], int wordCount, int rows, int columns){
    int k=0, i=0, j=0;
    clear();
    for(i=0; i<rows; i++){
        for(j=0; j<columns; j++){
            if (k<wordCount){
                printw("%15s", words[i][j]);
                k++;
            }
        }
        printw("\n");
    }
    refresh();
}   

void draw_character(int x, int y, char use){
    mvaddch(x,y,use);
    refresh();
}

void read_dir(int* time, int* b_down, int* b_up, int* b_left, int * b_right, int* dir_down, int* dir_up, int* dir_left, int* dir_right) {
    int dummy;
    scanf("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d,", time, b_down, b_up, b_left, b_right, dummy, dummy, dir_down, dir_up, dir_left, dir_right, dummy, dummy);
}

void trimws(char* s) {
    int len = strlen(s) ;
    int x;
    if (len ==0) return;
    x = len-1;
    while (isspace(s[x]) && (x>=0)) {
        s[x] = '\0';
        x--;
    }
}

int readWords(char* wl[MAXWORDS], char* filename) {
    int numread =0;
    char line[WORDLEN];
    char *p;
    FILE* fp = fopen(filename,"r");
    while (!feof(fp)) {
        p  =fgets(line, WORDLEN, fp);
        if (!feof(fp) && p !=NULL) {
            trimws(line);
            wl[numread] = (char *)  malloc(strlen(line)+1);
            strcpy(wl[numread], line);
            numread++;
            }
        } 
    fclose(fp);
    return numread; 
}

If you wish to compile and try it out for yourself, you will need this text file http://pastebin.com/xk0MmkEm You might have to replace the numbers in the case structure with ones for your machine for it to work.

Running it is just ./program.exe wordslist.txt

Was it helpful?

Solution

Your terminal definition appears to be correct since <ESC>[B is the cursor-down escape sequence for CygWin and, on running the code, it does actually move the cursor.

However, the fact that those escape sequences are also showing up means that they're coming from somewhere and it turns out where they're coming from is the input side of the equation, not the output side.

When you press the down-arrow, the escape sequence is first echoed to the screen then the input is acted on, moving the cursor to the correct location before outputting the < character.

So you need to turn echo off, immediately after the initscr() call, with:

initscr();
noecho();   // This line added.
refresh();

This will prevent the input from being echoed.

Now you may rely at some point on echo being done (e.g., for regular characters) but I find it's usually better to totally disconnect input from output and do the mapping in the program itself. That way, you can make the mapping as simple or complex as you like.

In other words, if someone enters an e, make sure echo is off and output the e yourself rather than relying on echo to do it for you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top