2015-10-08 49 views
14

我想在C程序中向前和向後移動光標。我正在循環讀整行,但我希望如果光標鍵被按下,屏幕上的光標就會改變位置,而不會阻塞循環。我試過getwch(),但它阻止了來電,直到按下它。我在尋找的是類似於bash提示符的行爲。將光標移到C程序中

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

我想要的功能使用ANSI轉義序列上面就像readline(PROMPT)readline.h

+4

不能做到這一點,沒有像ncurses庫 –

+4

聽起來像你需要['readline'](https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html),這也是使用bash。 – Olaf

+0

@Ed:我想打印一個回車* only *,然後只是輸入行的第一部分太依賴於終端的類型。 – usr2564301

回答

6

使用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; 
} 
8

一個簡單的例子:

#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; 

}

+1

我在找什麼是像readline這樣的行爲,但沒有導入readline.h。我現在正在檢查源代碼,但是如何實現readline()函數的一些幫助是值得歡迎的。 –

+1

實際上,終端通常以規範模式開始,並且您希望將其設置爲非規範模式以使其返回後退空間而不是解釋後退或等待換行符(這是上面的'tcsetattr'代碼的作用) –

+0

@ user4832408 - 規範模式是什麼意思? – inbinder

1

移動光標左右。停止輸入新行或太多字符

#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; 
} 
1

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

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