Cで端子幅を取得していますか?


89

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

1
提案された答えはどれも、半分以上正解ではありません。
Thomas Dickey

2
@ThomasDickey、あなたの答えはどこですか?
Alexis Wilke

回答:


126

getenv()の使用を検討しましたか?端末の列と行を含むシステムの環境変数を取得できます。

別の方法として、カーネルがターミナルサイズとして認識しているものを確認したい場合(ターミナルのサイズが変更されている場合はより良い)、次のように、TIOCGSIZEではなくTIOCGWINSZを使用する必要があります。

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
}

7
そうですが、用語の幅は環境変数ではなく、用語に対して静的です。
オースティン

4
プログラムの実行中に誰かが端末のサイズを変更した場合、現在の端末サイズは提供されません。
クリスジェスター-ヤング

ええ、それを追加していました:)
John T

ピクセルでサイズを取得する方法?私は使用ws_xpixelしましたがws_ypixel、ゼロが表示されるだけです!
デバシッシュ

@Debashishに依存します。たとえば、Linuxはこれらのフィールドをまったくサポートしていません。
メルポメン

16

この例は少し長めですが、端末の寸法を検出する最もポータブルな方法だと思います。これは、サイズ変更イベントも処理します。

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

3
しかし、シグナルハンドラーからinitscrとendwinを呼び出しても本当に安全ですか?これらは、少なくともasync-signal-safe APIのリストに含まれていませんman 7 signal
nav

1
それは良いポイント@navだ、私はしまし決してそのことを考えていません!より良い解決策は、シグナルハンドラーにフラグを立てさせ、メインループで残りの操作を実行させることでしょうか?
gamen 2013年

1
@gamen、はい、それはより良いでしょう;)-シグナルの代わりにsigactionを使用することも良いでしょう。
Bodo Thiesen 2015

COLSとLINESはグローバル変数ですか?
einpoklum 2016

1
@AlexisWilke:OKおよびを含みERRます。私たちの生活のギャップを埋めるためにそれらの「種類」がどのように役立つか:-(
einpoklum

12
#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のマニュアルを使用して確認してください。


-lcursesでもコンパイルできます。
カンバス2011

2
このコメントは事実の6年後のことですが、2048のマジックナンバーについて説明してください...
einpoklum

1
@einpoklumこれは、ほぼ3年後ですが、2048が、入力文字列がそこに入るときに「おそらく十分に大きいはず」のバッファの任意のサイズであることはかなり明確ではありませんか?
Roflcopter4

2
実際には、この答えはあまりにも多くの仮定をして正しいとは言えません。
トーマスディッキー

1
2048バッファーサイズについては、GNU termcapのドキュメント(gnu.org/software/termutils/manual/termcap-1.3/html_mono/…)で説明されています。この投稿を読んでいる人には、他にもたくさんの情報が役立つかもしれません。 。

3

ncursesがインストールされていてそれを使用getmaxyx()している場合は、を使用して端末の寸法を見つけることができます。


2
Yは、最初にしてX.来ることをはい、と注意を行う
ダニエル

0

Linuxを使用していると想定すると、代わりにncursesライブラリを使用したいと思います。私はあなたが持っているttysizeのものはstdlibにないと確信しています。


まあ、私がやっていることは実際にncursesを設定する価値はありません
オースティン

ncursesもstdlibにはありません。どちらも、POSIXで標準化されていますが、ioctlあなたは呪いなどを、初期化する必要がないための方法は、簡単かつクリーンである
Gandaro

0

だからここで答えを示唆するのではなく、

linux-pc:~/scratch$ echo $LINES

49

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

linux-pc:~/scratch$

わかりました。GNOME端末のサイズを変更すると、LINES変数とCOLUMNS変数がそれに続くことに気づきました。

ちょっとGNOME端末自体がこれらの環境変数を作成しているようですか?


1
そして確かに、それはCコードに渡されません。getenv( "LINES")はNULLを返します。
Scott Franco

変数はシェルのものであり、最終的なものではありません。
メルポメン

0

より完全な答えを追加するために、私にとってうまくいくことがわかったのは、@ John_TのソリューションとRosetta Codeから追加されたいくつかのビットを使用して、依存関係を理解するためのトラブルシューティングを行うことです。少し非効率的かもしれませんが、スマートプログラミングを使用すると、それを機能させ、常にターミナルファイルを開かないようにすることができます。

#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


-1

以下は、すでに提案されている環境変数の関数呼び出しです。

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

11
環境変数は信頼できません。これらの値はシェルによって設定されるため、存在することが保証されていません。また、ユーザーが端末のサイズを変更しても、最新の状態にはなりません。
ジュリアーノ

1
多くのシェルはSIGWINCHシグナルのハンドラーを確立するため、変数を最新の状態に保つことができます(入力エディターで適切な行の折り返しを行うために必要です)。
Barmar 14

5
彼らはそれをうまくやるかもしれませんが、プログラムの実行中にプログラムの環境は更新されません。
Functino、2015

もちろん、コードがクラッシュする可能性は非常に高いです。なぜなら、getenv()NULLを返すかどうかをテストせず、私のLinuxターミナルでテストを行うからです(これらの変数はエクスポートされないためです)。また、シェルがそれらの変数を更新しても、プログラムの実行中に変更します(独自のSIGWINCHハンドラーがない場合は不可)。
Alexis Wilke
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.