在C中获取终端宽度?

发布于 2024-07-25 12:26:36 字数 943 浏览 4 评论 0 原文

我一直在寻找一种从 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;
}

I've been looking for a way to get the terminal width from within my C program. What I keep coming up with is something along the lines of:

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

But everytime I try that I get

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

Is this the best way to do this, or is there a better way? If not how can I get this to work?

EDIT: fixed code is

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

ζ澈沫 2024-08-01 12:26:36

您是否考虑过使用 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
}

Have you considered using getenv() ? It allows you to get the system's environment variables which contain the terminals columns and lines.

Alternatively using your method, if you want to see what the kernel sees as the terminal size (better in case terminal is resized), you would need to use TIOCGWINSZ, as opposed to your TIOCGSIZE, like so:

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

and the full code:

#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
}
怎会甘心 2024-08-01 12:26:36

这个例子有点长,但我相信这是检测终端尺寸的最便携的方法。 这也处理调整大小事件。

正如 tim 和 rlbond 所建议的,我正在使用 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);
}

This example is a bit on the lengthy side, but I believe it's the most portable way of detecting the terminal dimensions. This also handles resize events.

As tim and rlbond suggests, I'm using ncurses. It guarantees a great improvement in terminal compatability as compared to reading environment variables directly.

#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);
}
暗藏城府 2024-08-01 12:26:36
#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 可以获得许多其他有用的信息。 使用 info termcap 查看 termcap 手册以获取更多详细信息。

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

Needs to be compiled with -ltermcap . There is a lot of other useful information you can get using termcap. Check the termcap manual using info termcap for more details.

雪若未夕 2024-08-01 12:26:36

为了添加更完整的答案,我发现对我有用的是使用 @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

struct WinSize {
  size_t rows;
  size_t cols;
};

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");

  struct WinSize size;
  size.rows = ws.ws_row;
  size.cols = ws.ws_col;

  close(fd);

  return size;
}

如果您确保不要全部调用它,但也许每隔一段时间就应该没问题,它甚至应该在用户调整终端窗口大小时更新(因为您正在打开文件并读取它每个 时间)。

如果您没有使用 TIOCGWINSZ,请参阅此表单上的第一个答案 https://www.linuxquestions.org/questions/programming-9/get-width-height-of-a-terminal-window- in-c-810739/

哦,别忘了free() 结果

To add a more complete answer, what I've found to work for me is to use @John_T's solution with some bits added in from Rosetta Code, along with some troubleshooting figuring out dependencies. It might be a bit inefficient, but with smart programming you can make it work and not be opening your terminal file all the time.

#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

struct WinSize {
  size_t rows;
  size_t cols;
};

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");

  struct WinSize size;
  size.rows = ws.ws_row;
  size.cols = ws.ws_col;

  close(fd);

  return size;
}

If you make sure not to call it all but maybe every once in a while you should be fine, it should even update when the user resizes the terminal window (because you're opening the file and reading it every time).

If you aren't using TIOCGWINSZ see the first answer on this form https://www.linuxquestions.org/questions/programming-9/get-width-height-of-a-terminal-window-in-c-810739/.

Oh, and don't forget to free() the result.

此岸叶落 2024-08-01 12:26:36

如果您已安装并正在使用 ncurses,则可以使用 getmaxyx() 来查找终端的尺寸。

If you have ncurses installed and are using it, you can use getmaxyx() to find the dimensions of the terminal.

凉城凉梦凉人心 2024-08-01 12:26:36

我的版本是 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;
}

运行测试

My Version Is An extermination of the ioctl approach I am not allocating memory and passing the struct back by value I believe so no memory leaks here

header file

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

The implementation, I have also added a test function that fills in the terminal with a box padded by one char all the way around

/**
* 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;
}

running test

一念一轮回 2024-08-01 12:26:36

假设您使用的是 Linux,我认为您想要使用 ncurses
反而。 我很确定你拥有的 ttysize 东西不在 stdlib 中。

Assuming you are on Linux, I think you want to use the ncurses library
instead. I am pretty sure the ttysize stuff you have is not in stdlib.

瞎闹 2024-08-01 12:26:36

以下是已建议的环境变量的函数调用:

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

Here are the function calls for the already suggested environmental variable thing:

int lines = atoi(getenv("LINES"));
int columns = atoi(getenv("COLUMNS"));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文