cterminaltermcap

How to get the cursor position in a C program using termcap, without writing a character?


I would like to know how to get the cursor position (x, y) in my program, without writing anything on the screen neither tracking it all the time.

I found out a way to get its position with this function (I don't check the return of read, write, etc here to write a smaller code on this subject but I do it in my program):

void get_cursor_position(int *col, int *rows)
{
    int a = 0;
    int i = 0;
    char buf[4];

    write(1, "\033[6n", 4); // string asking for the cursor position
    read(1, buf, 4);

    while (buf[i])
    {
        if (buf[i] >= 48 && buf[i] <= 57)
        {
            if (a == 0)
                *rows = atoi(&buf[i]) - 1;
            else
                *col = atoi(&buf[i]) - 1;
            a++;
        }
        i++;
    }
}

This function gives me the exact cursor position (*rows = y, *col = x), but it writes on the screen.

How can I get the cursor position without writing anything on the screen?
(If the cursor is on one of the printed characters, it will overwrite it.)
Should echo be toggled before and after sending the escape sequence?

This is a school project, so I only can use termcap, I can't use ncurses functions, the only allowed functions are tputs, tgoto, tgetstr, tgetnum, tgetflag.


Solution

  • There are several problems:

    Further reading:

    In canonical mode input processing, terminal input is processed in units of lines. A line is delimited by a newline character (NL), an end-of-file character (EOF), or an end-of-line (EOL) character. See Special Characters for more information on EOF and EOL. This means that a read request will not return until an entire line has been typed or a signal has been received. Also, no matter how many bytes are requested in the read() call, at most one line will be returned. It is not, however, necessary to read a whole line at once; any number of bytes, even one, may be requested in a read() without losing information.

        CSI Ps n  Device Status Report (DSR).
                    Ps = 5  -> Status Report.
                  Result ("OK") is CSI 0 n
                    Ps = 6  -> Report Cursor Position (CPR) [row;column].
                  Result is CSI r ; c R
    

    That is, your program should be prepared to read Escape[ followed by two decimal integers (with no fixed limit on their length), and two other characters ; and R.

    By the way, termcap by itself will do little for your solution. While ncurses has some relevant capabilities defined in the terminal database:

    #       u9      terminal enquire string (equiv. to ANSI/ECMA-48 DA)
    #       u8      terminal answerback description
    #       u7      cursor position request (equiv. to VT100/ANSI/ECMA-48 DSR 6)
    #       u6      cursor position report (equiv. to ANSI/ECMA-48 CPR)
    

    few programs use those, and in any case you would find it difficult to use the cursor position report in a termcap application.