簡體   English   中英

將cursor移動到C程序中

[英]Move the cursor in a C program

我想在 C 程序中前后移動 cursor。 我正在循環讀取整行,但我希望如果按下 cursor 鍵,屏幕上的 cursor 會更改為 position,而不會阻塞循環。 我嘗試getwch()但它會阻止調用者,直到按下輸入為止。 我正在尋找的是類似於 bash 提示的行為。 我正在閱讀與此類似的代碼:

while (TRUE) {
   printf("%s", PROMPT);
   fgets(input, 1024, stdin);
   do_something(input);
}

我正在嘗試讓上面的 function 像readline.h庫中的readline(PROMPT)一樣工作

使用termios和控制台代碼(VT100兼容 - 不可移植):

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

#define cursorforward(x) printf("\033[%dC", (x))
#define cursorbackward(x) printf("\033[%dD", (x))

#define KEY_ESCAPE  0x001b
#define KEY_ENTER   0x000a
#define KEY_UP      0x0105
#define KEY_DOWN    0x0106
#define KEY_LEFT    0x0107
#define KEY_RIGHT   0x0108

static struct termios term, oterm;

static int getch(void);
static int kbhit(void);
static int kbesc(void);
static int kbget(void);

static int getch(void)
{
    int c = 0;

    tcgetattr(0, &oterm);
    memcpy(&term, &oterm, sizeof(term));
    term.c_lflag &= ~(ICANON | ECHO);
    term.c_cc[VMIN] = 1;
    term.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &term);
    c = getchar();
    tcsetattr(0, TCSANOW, &oterm);
    return c;
}

static int kbhit(void)
{
    int c = 0;

    tcgetattr(0, &oterm);
    memcpy(&term, &oterm, sizeof(term));
    term.c_lflag &= ~(ICANON | ECHO);
    term.c_cc[VMIN] = 0;
    term.c_cc[VTIME] = 1;
    tcsetattr(0, TCSANOW, &term);
    c = getchar();
    tcsetattr(0, TCSANOW, &oterm);
    if (c != -1) ungetc(c, stdin);
    return ((c != -1) ? 1 : 0);
}

static int kbesc(void)
{
    int c;

    if (!kbhit()) return KEY_ESCAPE;
    c = getch();
    if (c == '[') {
        switch (getch()) {
            case 'A':
                c = KEY_UP;
                break;
            case 'B':
                c = KEY_DOWN;
                break;
            case 'C':
                c = KEY_LEFT;
                break;
            case 'D':
                c = KEY_RIGHT;
                break;
            default:
                c = 0;
                break;
        }
    } else {
        c = 0;
    }
    if (c == 0) while (kbhit()) getch();
    return c;
}

static int kbget(void)
{
    int c;

    c = getch();
    return (c == KEY_ESCAPE) ? kbesc() : c;
}

int main(void)
{
    int c;

    while (1) {
        c = kbget();
        if (c == KEY_ENTER || c == KEY_ESCAPE || c == KEY_UP || c == KEY_DOWN) {
            break;
        } else
        if (c == KEY_RIGHT) {
            cursorbackward(1);
        } else
        if (c == KEY_LEFT) {
            cursorforward(1);
        } else {
            putchar(c);
        }
    }
    printf("\n");
    return 0;
}

使用ANSI轉義序列的簡單示例:

#include <stdio.h>


int main()
{
    char *string = "this is a string";
    char input[1024] = { 0 };
    printf("%s", string);
    /* move the cursor back 5 spaces */
    printf("\033[D");
    printf("\033[D");
    printf("\033[D");
    printf("\033[D");
    printf("\033[D");
    fgets(input, 1024, stdin);
    return 0;
}

為了做非常有用的終端需要使用termios.h和/或curses.h / ncurses.h進入規范模式。 這樣,可以立即捕獲並響應退格鍵代碼,並相應地將緩沖區繪制到屏幕。 以下是如何使用tcsetattr()將終端設置為規范模式的示例:

struct termios info;
tcgetattr(0, &info);
info.c_lflag &= ~ICANON;
info.c_cc[VMIN] = 1;
info.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &info);

另一種選擇可能是使用readline()editline()庫。 要使用readline庫,請為編譯器指定-lreadline。 可以使用以下代碼片段進行編譯

cc -lreadline some.c -o some


#include <stdio.h>

#include <readline/readline.h>
#include <readline/history.h>

int main()
{
    char *inpt;
    int i = 0;

    while ( i < 10 )
    {
        inpt = readline("Enter text: ");
        add_history(inpt);
        printf("%s", inpt);
        printf("\n");
        ++i;
    }
    return 0;

}

向右和向左移動光標。 停止換行符或太多字符

#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/ioctl.h>

#define ESC          27
#define INSERT       50
#define DELETE       51
#define PGUP         53
#define PGDN         54
#define ARROWRIGHT   67
#define ARROWLEFT    68
#define END          70
#define HOME         72
#define OTHER        79
#define BRACKETLEFT  91
#define TILDE       126
#define BACKSPACE   127

#define SIZE         30

static const int STDIN = 0;

int kbhit(void)
{
    int bytesWaiting;

    ioctl(STDIN, FIONREAD, &bytesWaiting);
    return bytesWaiting;
}

int main ( ) {
    char input[SIZE] = {'\0'};
    int insert = 0;
    int each = 0;
    int end = 0;
    int to = 0;
    int ch = 0;
    int row = 0;
    int col = 0;
    struct termios oldattr, newattr;

    //set terminal
    tcgetattr( STDIN, &oldattr );
    newattr = oldattr;
    newattr.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN, TCSANOW, &newattr );
    setbuf(stdin, NULL);

    printf ( "\033[2J");//clear screen
    printf ( "\033[25;1H");//move cursor to row 25 col 1
    printf ( "OVW");
    printf ( "\033[9;1H");//move cursor to row 9 col 1
    printf ( "enter your text ");//prompt
    //printf ( "%s", input);
    printf ( "\033[9;17H");//move cursor to row 9 col 17
    col = 17;
    row = 9;
    while ( ( ch = getchar ()) != '\n') {
        if ( isprint( ch)) {
            if ( insert && each < end && end < SIZE-3) {
                //expand
                end++;
                for ( to = end; to >= each; to--) {
                    input[to + 1] = input[to];
                }
                printf ( "\033[9;17H");//move cursor to row 9 col 12
                printf ( "\033[K");//erase to end of line
                printf ( "%s", input);
            }
            printf ( "\033[%d;%dH", row, col);
            printf ( "%c", ch);
            input[each] = ch;
            each++;
            if ( each > end) {
                end = each;
            }
            col++;
            if ( each == end) {
                input[each] = '\0';
            }
            if ( each >= SIZE-1) {
                break;
            }
            continue;
        }

        if ( ch == BACKSPACE) {
            if ( each) {
                each--;
                col--;
                //contract
                for ( to = each; to <= end; to++) {
                    input[to] = input[to + 1];
                }
                end--;
                printf ( "\033[9;17H");//move cursor to row 1 col 7
                printf ( "\033[K");//erase to end of line
                printf ( "%s", input);
                printf ( "\033[%d;%dH", row, col);
            }
        }
        if ( ch == ESC) {
            if ( !kbhit ( )) {
                continue;
            }
            ch = getchar ( );
            if ( ch == OTHER) {
                ch = getchar ( );
                if ( ch == HOME) {
                    col -= each;
                    each = 0;
                    printf ( "\033[%d;%dH", row, col);
                    ch = getchar ( );
                }
                if ( ch == END) {
                    col += end - each;
                    each = end;
                    printf ( "\033[%d;%dH", row, col);
                    ch = getchar ( );
                }
            }
            if ( ch == BRACKETLEFT) {
                ch = getchar ( );
                if ( ch == INSERT) {
                    ch = getchar ( );
                    if ( ch == TILDE) {
                        insert = !insert;
                        printf ( "\033[25;1H");//move cursor to row 25 col 1
                        if ( insert) {
                            printf ( "INS");
                        }
                        else {
                            printf ( "OVW");
                        }
                        printf ( "\033[%d;%dH", row, col);
                    }
                }
                if ( ch == DELETE) {
                    ch = getchar ( );
                    if ( ch == TILDE) {
                        //contract
                        for ( to = each; to <= end; to++) {
                            input[to] = input[to + 1];
                        }
                        end--;
                        printf ( "\033[9;17H");//move cursor to row 10 col 1
                        printf ( "\033[K");//erase to end of line
                        printf ( "%s", input);
                        printf ( "\033[%d;%dH", row, col);
                    }
                }
                if ( ch == ARROWRIGHT) {
                    if ( each < end) {
                        printf ( "\033[C");//cursor right
                        each++;
                        col++;
                    }
                }
                if ( ch == ARROWLEFT) {
                    if ( each) {
                        printf ( "\033[D");//cursor left
                        each--;
                        col--;
                    }
                }
            }
            else {
                ungetc ( ch, stdin);
            }
        }
    }
    printf ( "\n\ninput was [%s]\n", input);
    printf ( "\n\nbye\n");

    //restore terminal
    tcsetattr( STDIN, TCSANOW, &oldattr );

    return 0;
}

ANSI轉義序列允許您隨意在屏幕上移動光標。 這對於在shell下運行的程序生成的全屏用戶界面非常有用,但也可以在提示中使用。 這不適用於不接受保存和恢復光標位置代碼的終端仿真器。 有關移動光標的轉義序列的更多信息,請參閱光標移動

如果您需要更便攜的解決方案,請使用curses ,它是一個終端控制庫,用於管理應用程序在類Unix系統的字符單元終端上的顯示。 執行系統上的curses庫根據終端類型發送正確的控制字符。 ncurses中使用int wmove(WINDOW* win, int y, int x)將光標移動到新位置。 有關如何在ncurses下編程的更多信息,請參閱NCurses-programing-howto

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM