2009-06-21 22 views
75

Ho cercato un modo per ottenere la larghezza del terminale all'interno del mio programma C. Quello che continuo a venire con è qualcosa sulla falsariga di:Ottenere la larghezza del terminale in C?

#include <sys/ioctl.h> 
#include <stdio.h> 

int main (void) 
{ 
    struct ttysize ts; 
    ioctl(0, TIOCGSIZE, &ts); 

    printf ("lines %d\n", ts.ts_lines); 
    printf ("columns %d\n", ts.ts_cols); 
} 

Ma ogni volta che provo che ricevo

[email protected]:~$ gcc test.c -o test 
test.c: In function ‘main’: 
test.c:6: error: storage size of ‘ts’ isn’t known 
test.c:7: error: ‘TIOCGSIZE’ undeclared (first use in this function) 
test.c:7: error: (Each undeclared identifier is reported only once 
test.c:7: error: for each function it appears in.) 

È questo il modo migliore per fare questo, o c'è un modo migliore? Se no, come posso farlo funzionare?

EDIT: fissa il codice è

#include <sys/ioctl.h> 
#include <stdio.h> 

int main (void) 
{ 
    struct winsize w; 
    ioctl(0, TIOCGWINSZ, &w); 

    printf ("lines %d\n", w.ws_row); 
    printf ("columns %d\n", w.ws_col); 
    return 0; 
} 

risposta

101

Hai pensato di usare getenv()? Permette di ottenere le variabili di ambiente del sistema che contengono le colonne e le linee dei terminali.

alternativa utilizzando il metodo, se si vuole vedere ciò che il kernel vede come le dimensioni del terminale (meglio nel terminale caso viene ridimensionato), si avrebbe bisogno di utilizzare TIOCGWINSZ, in contrapposizione al vostro TIOCGSIZE, in questo modo:

struct winsize w; 
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 

e il codice completo:

#include <sys/ioctl.h> 
#include <stdio.h> 
#include <unistd.h> 

int main (int argc, char **argv) 
{ 
    struct winsize w; 
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 

    printf ("lines %d\n", w.ws_row); 
    printf ("columns %d\n", w.ws_col); 
    return 0; // make sure your main returns int 
} 
+4

sì ma il termine larghezza non è una variabile ambientale, è statico al termine. – austin

+4

Non fornisce la dimensione del terminale _current_, se qualcuno ridimensiona il terminale durante l'esecuzione del programma. –

+0

sì, stava aggiungendo che :) –

0

Supponendo che si sta su Linux, penso che si desidera utilizzare la libreria ncurses invece. Sono abbastanza sicuro che le cose ttysize che hai non siano in stdlib.

+0

beh, quello che sto facendo non vale davvero la pena impostare ncurses per – austin

+0

ncurses non è nemmeno in stdlib. Entrambi sono standardizzati in POSIX, ma il modo in cui 'ioctl' è più semplice e più pulito, perché non si dispone di inizializzare maledizioni, ecc – Gandaro

2

Se si dispone di ncurses installati e lo si sta utilizzando, è possibile utilizzare getmaxyx() per trovare le dimensioni del terminale.

+1

Sì, e si fa notare che la Y viene prima e poi le variabili di ambiente X. – Daniel

-1

Ecco la funzione richiede la cosa variabile d'ambiente già suggerito:

int lines = atoi(getenv("LINES")); 
int columns = atoi(getenv("COLUMNS")); 
+8

non siete affidabile.Questi valori sono impostati dalla shell, quindi non è garantito che esistano. Inoltre, non saranno aggiornati se l'utente cambia le dimensioni del terminale. – Juliano

+0

Molte shell stabiliscono un gestore per il segnale 'SIGWINCH', in modo che possano mantenere aggiornate le variabili (ne hanno anche bisogno in modo che eseguano il corretto ritorno a capo nell'editor di input). – Barmar

+1

Potrebbero farlo, ma l'ambiente di un programma non verrà aggiornato mentre è in esecuzione. – Functino

10
#include <stdio.h> 
#include <stdlib.h> 
#include <termcap.h> 
#include <error.h> 

static char termbuf[2048]; 

int main(void) 
{ 
    char *termtype = getenv("TERM"); 

    if (tgetent(termbuf, termtype) < 0) { 
     error(EXIT_FAILURE, 0, "Could not access the termcap data base.\n"); 
    } 

    int lines = tgetnum("li"); 
    int columns = tgetnum("co"); 
    printf("lines = %d; columns = %d.\n", lines, columns); 
    return 0; 
} 

deve essere compilato con -ltermcap. Ci sono molte altre informazioni utili che puoi ottenere usando termcap. Controllare il manuale termcap utilizzando info termcap per ulteriori dettagli.

+0

Puoi compilarlo anche con -lcurses. – Kambus

+0

Non riesco a includere termcap in Ubuntu 14.04 e non riesco a trovarlo neanche nei repository. :/ –

+1

So che questo commento arriva 6 anni dopo il fatto, ma per favore spiega il tuo numero magico di 2048 ... – einpoklum

15

Questo esempio è un po 'sul lato lungo, ma credono che il modo più portatile di rilevare le dimensioni del terminale. Questo gestisce anche gli eventi di ridimensionamento.

come Tim e rlbond suggerisce, sto usando ncurses. Garantisce un notevole miglioramento della compatibilità del terminale rispetto alla lettura diretta delle variabili d'ambiente.

#include <ncurses.h> 
#include <string.h> 
#include <signal.h> 

// SIGWINCH is called when the window is resized. 
void handle_winch(int sig){ 
    signal(SIGWINCH, SIG_IGN); 

    // Reinitialize the window to update data structures. 
    endwin(); 
    initscr(); 
    refresh(); 
    clear(); 

    char tmp[128]; 
    sprintf(tmp, "%dx%d", COLS, LINES); 

    // Approximate the center 
    int x = COLS/2 - strlen(tmp)/2; 
    int y = LINES/2 - 1; 

    mvaddstr(y, x, tmp); 
    refresh(); 

    signal(SIGWINCH, handle_winch); 
} 

int main(int argc, char *argv[]){ 
    initscr(); 
    // COLS/LINES are now set 

    signal(SIGWINCH, handle_winch); 

    while(getch() != 27){ 
    /* Nada */ 
    } 

    endwin(); 

    return(0); 
} 
+2

Ma è davvero sicuro chiamare initscr e endwin da un gestore di segnale? Almeno non sono elencati tra le API sicure con segnale asincrono nel 'segnale uomo 7 ' – nav

+1

Questo è un buon punto @nav, non ho * mai * pensato a questo! Una soluzione migliore sarebbe forse avere il gestore del segnale che alza un flag e quindi eseguire il resto delle operazioni nel loop principale? – gamen

+1

@gamen, sì, sarebbe meglio;) - anche usare sigaction invece del segnale sarebbe anche meglio. –

Problemi correlati