Szukałem sposobu, aby uzyskać szerokość terminala z mojego programu C. Ciągle wymyślam coś w rodzaju:
#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);
}
Ale za każdym razem, gdy próbuję, otrzymuję
austin@:~$ 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.)
Czy to najlepszy sposób, czy jest lepszy sposób? Jeśli nie, jak mogę to uruchomić?
EDYCJA: naprawiono kod
#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;
}
Odpowiedzi:
Czy rozważałeś użycie getenv () ? Pozwala na pobranie zmiennych środowiskowych systemu, które zawierają kolumny i linie terminala.
Alternatywnie, używając twojej metody, jeśli chcesz zobaczyć, co jądro widzi jako rozmiar terminala (lepiej w przypadku zmiany rozmiaru terminala), musisz użyć TIOCGWINSZ, a nie TIOCGSIZE, na przykład:
struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
i pełny kod:
#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 }
źródło
ws_xpixel
iws_ypixel
, ale to po prostu drukuje zera!Ten przykład jest trochę obszerny, ale uważam, że jest to najbardziej przenośny sposób wykrywania wymiarów terminala. Obsługuje to również zdarzenia zmiany rozmiaru.
Jak sugerują tim i rlbond, używam ncurses. Gwarantuje dużą poprawę kompatybilności terminala w porównaniu do bezpośredniego odczytu zmiennych środowiskowych.
#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); }
źródło
man 7 signal
OK
iERR
. Jak "mili" z ich strony, że pomogli nam wypełnić tę lukę w naszym życiu :-(#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; }
Musi być skompilowany z
-ltermcap
. Istnieje wiele innych przydatnych informacji, które można uzyskać za pomocą termcap.info termcap
Więcej szczegółów znajdziesz w instrukcji obsługi termcap .źródło
Jeśli masz zainstalowany ncurses i używasz go, możesz użyć,
getmaxyx()
aby znaleźć wymiary terminala.źródło
Zakładając, że korzystasz z Linuksa, myślę, że zamiast tego chcesz użyć biblioteki ncurses . Jestem prawie pewien, że rzeczy o rozmiarze ttysize nie znajdują się w standardowym katalogu.
źródło
ioctl
sposób jest prostszy i czystszy, ponieważ nie musisz inicjalizować curses itp.Więc nie sugeruję tutaj odpowiedzi, ale:
linux-pc:~/scratch$ echo $LINES
49
linux-pc:~/scratch$ printenv | grep LINES
linux-pc:~/scratch$
Ok, i zauważam, że jeśli zmienię rozmiar terminala GNOME, zmienne LINES i COLUMNS podążą za tym.
Wygląda na to, że terminal GNOME sam tworzy te zmienne środowiskowe?
źródło
Aby dodać bardziej kompletną odpowiedź, odkryłem, że działa dla mnie, używając rozwiązania @ John_T z kilkoma bitami dodanymi z Rosetta Code , wraz z kilkoma rozwiązywaniem problemów związanych z zależnościami. Może to być trochę nieefektywne, ale dzięki inteligentnemu programowaniu możesz sprawić, że zadziała i nie będziesz otwierać pliku terminala przez cały czas.
#include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> // ioctl, TIOCGWINSZ #include <err.h> // err #include <fcntl.h> // open #include <unistd.h> // close #include <termios.h> // don't remember, but it's needed size_t* get_screen_size() { size_t* result = malloc(sizeof(size_t) * 2); if(!result) err(1, "Memory Error"); struct winsize ws; int fd; fd = open("/dev/tty", 0_RDWR); if(fd < 0 || ioctl(fd, TIOCGWINSZ, &ws) < 0) err(8, "/dev/tty"); result[0] = ws.ws_row; result[1] = ws.ws_col; close(fd); return result; }
Jeśli upewnisz się, że nie wywołujesz tego wszystkiego, ale może od czasu do czasu powinno być dobrze, powinno nawet zaktualizować się, gdy użytkownik zmieni rozmiar okna terminala (ponieważ otwierasz plik i czytasz go za każdym razem).
Jeśli nie używasz,
TIOCGWINSZ
zobacz pierwszą odpowiedź w tym formularzu https://www.linuxquestions.org/questions/programming-9/get-width-height-of-a-terminal-window-in-c-810739/ .Aha, i nie zapomnij .
free()
result
źródło
Oto wywołania funkcji dla już sugerowanej zmiennej środowiskowej:
int lines = atoi(getenv("LINES")); int columns = atoi(getenv("COLUMNS"));
źródło
SIGWINCH
sygnału, dzięki czemu mogą aktualizować zmienne (potrzebują tego również, aby odpowiednio zawijać wiersze w edytorze wejściowym).getenv()
zwraca NULL, czy nie, a dzieje się tak w moim terminalu Linux (ponieważ te zmienne nie są eksportowane). Również nawet jeśli powłoka aktualizuje te zmienne, nie zobaczysz zmienia się podczas działania programu (nie bez posiadania własnego programuSIGWINCH
obsługi).