Как получить ширину терминала в С?

Я искал способ получить ширину терминала из моей 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);
}

Но каждый раз, когда я пытаюсь это получить

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.)

Это лучший способ сделать это, или есть лучший способ? Если нет, как я могу заставить это работать?

РЕДАКТИРОВАТЬ: фиксированный код

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

9 ответов

Решение

Рассматривали ли вы использование getenv ()? Это позволяет получить системные переменные среды, которые содержат столбцы и строки терминала.

В качестве альтернативы, используя ваш метод, если вы хотите видеть то, что ядро ​​видит как размер терминала (лучше в случае изменения размера терминала), вам нужно будет использовать TIOCGWINSZ, а не ваш TIOCGSIZE, например так:

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

и полный код:

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

Этот пример немного длинен, но я считаю, что это самый портативный способ определения размеров терминала. Это также обрабатывает события изменения размера.

Как предполагают Тим ​​и Рлбонд, я использую ncurses. Это гарантирует значительное улучшение совместимости терминала по сравнению с непосредственным чтением переменных среды.

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

Необходимо скомпилировать с -ltermcap, Существует много другой полезной информации, которую вы можете получить с помощью termcap. Проверьте руководство termcap используя info termcap Больше подробностей.

Чтобы добавить более полный ответ, я обнаружил, что для меня работает решение @John_T с некоторыми битами, добавленными из кода Rosetta, а также с некоторыми устранениями неполадок, выясняющих зависимости. Это может быть немного неэффективно, но с умным программированием вы можете заставить его работать и не открывать файл терминала все время.

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

Если вы убедитесь, что не вызываете его все, но, возможно, время от времени все будет в порядке, он должен даже обновляться, когда пользователь изменяет размер окна терминала (потому что вы открываете файл и читаете его каждый раз).

Если вы не используете TIOCGWINSZсм. первый ответ в этой форме https://www.linuxquestions.org/questions/programming-9/get-width-height-of-a-terminal-window-in-c-810739/.

О, и не забудьте free() то result.

Если вы установили ncurses и используете его, вы можете использовать getmaxyx() найти размеры терминала.

Моя версия - это уничтожение подхода ioctl. Я не выделяю память и не передаю структуру обратно по значению, я считаю, что здесь нет утечек памяти.

заголовочный файл

      #include <stdlib.h>
#include <sys/ioctl.h> // ioctl, TIOCGWINSZ

struct winsize get_screen_size();

unsigned short get_screen_width();
unsigned short get_screen_height();

void test_screen_size();

Реализация, я также добавил тестовую функцию, которая заполняет терминал полем, заполненным одним символом по всему периметру.

      /**
* Implementation of nos_utils signatures
*/

#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>   // doesnt seem to be needed for this 
#include "nos_utils.h"

/**
 * @return  struct winsize   
 *  unsigned short int ws_row;
 *   unsigned short int ws_col;
 *   unsigned short int ws_xpixel;
 *   unsigned short int ws_ypixel;
 */
struct winsize get_screen_size() {
    struct winsize ws;
    int fd;

    fd = open("/dev/tty", O_RDWR);
    if (fd < 0 || ioctl(fd, TIOCGWINSZ, &ws) < 0) err(8, "/dev/tty");
    close(fd); // dont forget to close files
    return ws;
}

unsigned short get_screen_width() {
    struct winsize ws = get_screen_size();
    return ws.ws_col;
}

unsigned short get_screen_height() {
    struct winsize ws = get_screen_size();
    return ws.ws_row;
}

void test_screen_size() {
    struct winsize ws = get_screen_size();
//    unsigned short  h = ws.ws_row;
//    unsigned short  w = ws.ws_col;
    printf("The Teminal Size is\n rows: %zu  in %upx\n cols: %zu in %upx\n", ws.ws_row, ws.ws_ypixel, ws.ws_col,
           ws.ws_xpixel);
    
    unsigned short  h = get_screen_height();
    unsigned short  w = get_screen_width();
    h = h - 4; //for the 3 lines above + 1 fro new terminal line after :)
    for (unsigned short  i = 0; i < h; i++) {// for each row
        for (unsigned short  j = 0; j < w; j++) { // for each col
            //new line if we are last char
            if (j == w - 1) {
                printf(" \n");
            }//one space buffer around edge
            else if (i == 0 || i == h - 1 || j == 0) {
                printf(" ");
            } //the up arrows
            else if (i == 1) {
                printf("^");
            } else if (i == h - 2) {
                printf("v");
            } else if (j == 1) {
                printf("<");
            } else if (j == w - 2) {
                printf(">");
            } else {
                printf("#");
            }
        }//end col
    }//end row
}

int main(int argc, char** argv) {
    test_screen_size();
    return 0;
}

Предполагая, что вы работаете в Linux, я думаю, что вы хотите использовать библиотеку ncurses. Я почти уверен, что твой размер, которого ты имеешь, не в stdlib.

Так что не предлагая ответ здесь, но:

linux-pc:~/scratch$ echo $LINES

49

linux-pc:~/scratch$ printenv | grep LINES

linux-pc:~/scratch$

Хорошо, и я заметил, что если я изменю размер терминала GNOME, переменные LINES и COLUMNS будут следовать за этим.

Вроде как терминал GNOME сам создает эти переменные окружения?

Вот вызовы функции для уже предложенной переменной среды:

int lines = atoi(getenv("LINES"));
int columns = atoi(getenv("COLUMNS"));
Другие вопросы по тегам