Andrea Martinelli
Andrea Martinelli

Reputation: 312

C Avoid cursor positioning on key press

I'm trying to implement a sort of terminal in C. I'm trying to build terminal history on up-down key pressure, but if I press those keys the cursor will go up (or down) one row. How to "control" this feature?

Edit: Let's take an example:

fgets(cmd, sizeof(cmd), stdin);

    if (cmd[0] == '\033')
        if (cmd[2] == 'A')
            printf("up-arrow pressed!\n");

This code is horrible, but it is a good starting point. It works. The problem is that if you press the up arrow key (cursor), it moves on the upper row and print "up-arrow pressed!". How to avoid the cursor up-down movement without external libraries?

Upvotes: 0

Views: 221

Answers (2)

Thomas Dickey
Thomas Dickey

Reputation: 54505

A real application would provide for exiting the program other than with a ^C. Here is an example, amending @ctx's sample to do this (and improving the handling of escape sequences), exiting when two escapes in a row are read:

#include <stdio.h>
#include <termios.h>

int main (void) {

        struct termios t, save;
        int last;
        tcgetattr(0, &t);
        save = t;
        t.c_lflag &= ~(ECHO | ICANON);
        tcsetattr(0, TCSANOW, &t);

        while (1) {
                int ch = getchar();
                if (ch == '\033') {
                    if (last == ch)
                            break;
                        do {
                            last = ch;
                            ch = getchar();
                        } while (ispunct(ch) || isdigit(ch));
                        if (isalpha(ch)) {
                            printf("Got escape sequence: ");
                            switch (ch) {
                                case 'A':
                                        printf("Cursor up\n");
                                        break;
                                case 'B':
                                        printf("Cursor down\n");
                                        break;
                                case 'D':
                                        printf("Cursor left\n");
                                        break;
                                case 'C':
                                        printf("Cursor right\n");
                                        break;
                                default:
                                    printf("%c\n", ch);
                                    break;
                                    break;
                            }
                        }
                } else if (ch > 0) {
                        printf("Got %c\n", ch);
                }
            last = ch;
        }

        tcsetattr(0, TCSANOW, &save);
        return 0;
}

The equivalent (with different messages) is simpler in curses:

#include <curses.h>

int main (void) {
    int last = 0;

    filter();
    initscr();
    noecho();
    cbreak();
    keypad(stdscr, TRUE);

    while (1) {
            int ch = getch();
            if (ch >= 0) {
                    if (ch >= KEY_MIN) {
                            printf("Got special key %s\r\n", keyname(ch));
                    } else {
                            if (ch == '\033' && last == ch)
                                    break;
                            printf("Got %s\r\n", unctrl(ch));
                    }
                    last = ch;
                    fflush(stdout);
            }
        }

        endwin();
        return 0;
}

Upvotes: 1

Ctx
Ctx

Reputation: 18420

For a starter, an example how you could initialize your terminal and some basic code:

#include <stdio.h>
#include <termios.h>

int main (void) {

        struct termios t;
        tcgetattr(0, &t);
        t.c_lflag &= ~(ECHO | ICANON);
        tcsetattr(0, TCSANOW, &t);

        while (1) {
                char ch = getchar();
                if (ch == '\x1b') {
                        do {
                                ch = getchar();
                        } while (!isalpha(ch));
                        printf("Got escape sequence: ");
                        switch (ch) {
                                case 'A':
                                        printf("Cursor up\n");
                                        break;
                                case 'B':
                                        printf("Cursor down\n");
                                        break;
                                case 'D':
                                        printf("Cursor left\n");
                                        break;
                                case 'C':
                                        printf("Cursor right\n");
                                        break;
                                default:
                                        printf("%c\n", ch);
                        }
                        // handle escape sequence
                } else {
                        printf("Got %c\n", ch);
                }
        }

        return 0;
}

Now you have to output the typed characters and move around by sending appropriate escape sequences when you receive a cursor-* or some other control sequence (instead of the debug-printfs above). You have to closely keep track on your current screen-layout for that.

As I already suggested in my comments, this a very tedious work and apart from learning (or maybe embedded systems) I do not see a benefit compared to using libreadline or a similar API.

Upvotes: 1

Related Questions