2013-04-16 16 views
7

sto cercando di recuperare le coordinate di cursore in un terminale VT100 utilizzando il seguente codice:Lettura dello stato Rapporto dispositivo ANSI sequenza di escape risposta

void getCursor(int* x, int* y) { 
    printf("\033[6n"); 
    scanf("\033[%d;%dR", x, y); 
} 

sto usando la seguente sequenza di escape ANSI:

Device Status report - ESC [6n

segnala la posizione del cursore per l'applicazione come (come se digitato sulla tastiera) ESC [n; mR, dove n è la riga e m è la colonna.

Il codice compila e la sequenza ANSI viene inviato, ma, dopo aver ricevuto esso, il terminale stampa la stringa ^[[x;yR alla stdout invece di stdin rendendo imposible per me per recuperare dal programma:

terminal window

Chiaramente, la stringa è designata per il programma, quindi, quindi devo fare qualcosa in modo non corretto. Qualcuno sa di cosa si tratta?

risposta

3

Il programma è in esecuzione ma è in attesa di un carattere EOL.

scanf è orientato alla linea in modo che sia in attesa di una nuova riga prima dell'elaborazione. Prova a eseguire il programma, quindi premi il tasto Invio.

La soluzione è utilizzare qualcos'altro che non ha bisogno di una nuova riga per leggere l'input e quindi utilizzare sscanf per analizzare i valori.

Sarà inoltre necessario rendere stdin non bloccante o non sarà possibile ottenere l'input finché il buffer non è pieno o lo stdin è chiuso. Vedere questa domanda Making stdin non-blocking

Si dovrebbe anche chiamare fflush(stdout); dopo vostro printf per assicurarsi che sia effettivamente scritta (printf è spesso la linea tamponato quindi senza un ritorno a capo non può svuotare il buffer).

+1

Non c'è un modo per far sì che il comando ANSI non scriva affatto "^ [[x; yR' al terminale? Mi piacerebbe recuperare in silenzio le coordinate senza alcun cambiamento visibile sullo schermo del terminale. Ma questo scrive sul terminale (indesiderabile quando si crea una GUI testuale) e quindi cambia le coordinate del cursore (che lo rende assolutamente inutile). – Witiko

+0

Questo è il motivo per cui curses (e ncurses) esiste quindi non devi preoccuparti di tutti questi dettagli. – Craig

+2

Certo, ma ncurses è un vero bloatware. Ecco perché sto scrivendo la mia lib di sequenze di escape ANSI leggera, che supporta semplicemente VT100. Ma sì, sembra che dovrò solo dl ncurses e provare a decodificarlo per trovare la sua soluzione a questo problema. – Witiko

3

Chiedo la posizione del cursore. Se non ho una risposta dopo 100 ms (questo è arbitrario) suppongo che la console non sia ansi.

/* This function tries to get the position of the cursor on the terminal. 
It can also be used to detect if the terminal is ANSI. 
Return 1 in case of success, 0 otherwise.*/ 

int console_try_to_get_cursor_position(int* x, int *y) 
{ 
    fd_set readset; 
    int success = 0; 
    struct timeval time; 
    struct termios term, initial_term; 

    /*We store the actual properties of the input console and set it as: 
    no buffered (~ICANON): avoid blocking 
    no echoing (~ECHO): do not display the result on the console*/ 
    tcgetattr(STDIN_FILENO, &initial_term); 
    term = initial_term; 
    term.c_lflag &=~ICANON; 
    term.c_lflag &=~ECHO; 
    tcsetattr(STDIN_FILENO, TCSANOW, &term); 

    //We request position 
    print_escape_command("6n"); 
    fflush(stdout); 

    //We wait 100ms for a terminal answer 
    FD_ZERO(&readset); 
    FD_SET(STDIN_FILENO, &readset); 
    time.tv_sec = 0; 
    time.tv_usec = 100000; 

    //If it success we try to read the cursor value 
    if (select(STDIN_FILENO + 1, &readset, NULL, NULL, &time) == 1) 
     if (scanf("\033[%d;%dR", x, y) == 2) success = 1; 

    //We set back the properties of the terminal 
    tcsetattr(STDIN_FILENO, TCSADRAIN, &initial_term); 

    return success; 
} 
0

Credo che si ottenga veramente la risposta prevista in stdin.Ma immaginare cosa succede effettivamente:

  • si invia una richiesta come sequenza di escape stdout
  • terminale riceve ed elabora una risposta corrispondente come sequenza di escape così
  • la risposta viene inviata allo stdin
  • scanf viene chiamato e stdin viene reindirizzato tramite la shell in cui viene utilizzata la libreria readline per l'input utente interattivo e modificabile
  • readline acquisisce la sequenza di escape anziché passarla al terminale
  • readline nuova formula con alcun carattere ESC per impedire l'esecuzione della sequenza di controllo, ma piuttosto rende leggibile utilizzando solo caratteri stampabili
  • la risposta inarcò raggiunge scanf ma è troppo tardi
  • la risposta inarcò è anche eco per stdout in modo che l'utente possa vedere istantaneamente ciò che ha digitato.

Per evitare questo uso un'ansa getc() (==fgetc(stdin)) invece. Se si incontra un ESC (0x1B) di scaricare i seguenti caratteri in una stringa fino a trovare il delimitatore finale della sequenza ESC (nel tuo caso 'n'). Successivamente è possibile utilizzare sscanf(esqString, formatString, ...).

Ma prima di incontrare il ciclo è necessario change with termios to raw mode (osservare l'esempio di codice seguente). Altrimenti nulla sarebbe diverso.

Problemi correlati