Terminalbreite in C abrufen?

Lesezeit: 7 Minuten

Austins Benutzeravatar
Austin

Ich habe nach einer Möglichkeit gesucht, die Terminalbreite aus meinem C-Programm herauszubekommen. Was mir immer wieder einfällt, ist etwas in der Art von:

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

Aber jedes Mal, wenn ich es versuche, bekomme ich es

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

Ist dies der beste Weg, dies zu tun, oder gibt es einen besseren Weg? Wenn nicht, wie kann ich das zum Laufen bringen?

EDIT: Festcode ist

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

  • Keine der vorgeschlagenen Antworten ist mehr als halb richtig.

    – Thomas Dickey

    18. April 2019 um 8:10 Uhr

  • @ThomasDickey, wo ist dann deine Antwort?

    – Alexis Wilke

    15. Juli 2019 um 6:42 Uhr

Benutzeravatar von John T
Johannes T

Haben Sie darüber nachgedacht, zu verwenden getenv() ? Es ermöglicht Ihnen, die Umgebungsvariablen des Systems zu erhalten, die die Spalten und Zeilen der Terminals enthalten.

Wenn Sie alternativ mit Ihrer Methode sehen möchten, was der Kernel als Terminalgröße sieht (besser, wenn die Größe des Terminals geändert wird), müssen Sie TIOCGWINSZ verwenden, im Gegensatz zu Ihrer TIOCGSIZE, wie folgt:

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

und der vollständige 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
}

  • Ja, aber die Begriffsbreite ist keine Umgebungsvariable, sondern statisch für den Begriff.

    – Austin

    21. Juni 2009 um 1:42 Uhr

  • Es bietet Ihnen nicht die aktuell Terminalgröße, wenn jemand während der Ausführung des Programms die Größe des Terminals ändert.

    – C. K. Young

    21. Juni 2009 um 1:42 Uhr

  • @Debashish Hängt davon ab. ZB unterstützt Linux diese Felder überhaupt nicht.

    – Melpomen

    5. Juli 2019 um 22:07 Uhr

  • Obwohl wir das sehen können LINES und COLUMNS Variablen in der Umgebung, sie scheinen nicht exportiert zu werden. Also nicht von Ihrem C-Programm aus zugänglich.

    – Alexis Wilke

    14. Juli 2019 um 21:59 Uhr

  • Ich bin gerade über diese Antwort gestolpert, und mir fiel die Kinnlade herunter, als ich das erkannte getenv("COLUMNS") funktioniert perfekt, wenn man unter Aufsicht läuft (1). Also habe ich jetzt eine Reihe von Fallbacks, alle von der TIOCWINSZ ioctl, um getenv, wenn nicht ein tty, bis zum klassischen ANSI-Escape “move cursor to 9999,9999 and the query cursor pos. Der letzte funktioniert auf seriellen Konsolen für eingebettete Systeme 🙂

    – Troglobit

    23. Dezember 2020 um 8:54 Uhr

Dieses Beispiel ist etwas langatmig, aber ich glaube, es ist die portabelste Art, die Terminalabmessungen zu erkennen. Dies behandelt auch Größenänderungsereignisse.

Wie tim und rlbond andeuten, verwende ich ncurses. Es garantiert eine große Verbesserung der Terminalkompatibilität im Vergleich zum direkten Lesen von Umgebungsvariablen.

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

  • Aber ist es wirklich sicher, initscr und endwin von einem Signal-Handler aus aufzurufen? Sie sind zumindest nicht unter den asynchronsignalsicheren APIs in aufgeführt man 7 signal

    – Navi

    6. August 2013 um 18:16 Uhr


  • Das ist ein guter Punkt @nav, ich habe noch nie daran gedacht! Wäre es vielleicht eine bessere Lösung, den Signalhandler ein Flag setzen zu lassen und dann den Rest der Operationen in der Hauptschleife auszuführen?

    – gamen

    3. Oktober 2013 um 9:55 Uhr

  • @gamen, ja, das wäre besser 😉 – auch die Verwendung von sigaction anstelle von signal wäre auch besser.

    – Bodo Thiesen

    30. Juli 2015 um 16:07 Uhr

  • Sind also COLS und LINES globale Variablen?

    – einpoklum

    28. Februar 2016 um 12:12 Uhr

  • @AlexisWilke: Einschließlich OK und ERR. Wie “nett” von ihnen, uns zu helfen, diese Lücke in unserem Leben zu füllen 🙁

    – einpoklum

    15. Juli 2019 um 8:10 Uhr

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

Muss mit kompiliert werden -ltermcap . Es gibt viele andere nützliche Informationen, die Sie mit termcap erhalten können. Überprüfen Sie das Termcap-Handbuch mit info termcap für mehr Details.

  • Sie können es auch mit -lcurses kompilieren.

    – Kambus

    19. April 2011 um 10:12 Uhr

  • Ich weiß, dass dieser Kommentar 6 Jahre später kommt, aber bitte erklären Sie Ihre magische Zahl von 2048 …

    – einpoklum

    20. Februar 2016 um 14:35 Uhr

  • @einpoklum Das ist noch fast drei Jahre später, aber ist es nicht ziemlich klar, dass 2048 nur eine willkürliche Größe für den Puffer ist, der “wahrscheinlich groß genug sein sollte” für jede Eingabezeichenfolge, die dorthin geht?

    – Roflcopter4

    12. Dezember 2018 um 6:32 Uhr

  • Tatsächlich macht diese Antwort zu viele Annahmen, um richtig zu sein.

    – Thomas Dickey

    8. Mai 2019 um 8:26 Uhr

  • Für alle Neugierigen wird die Puffergröße von 2048 in der GNU-Termcap-Dokumentation hier erklärt: gnu.org/software/termutils/manual/termcap-1.3/html_mono/… Es gibt auch eine Menge anderer Dinge darin, die Leute, die diesen Beitrag lesen, nützlich finden könnten.

    Benutzer5739133

    11. Mai 2020 um 22:10 Uhr

Wenn Sie ncurses installiert haben und verwenden, können Sie verwenden getmaxyx() um die Abmessungen des Terminals zu finden.

Um eine vollständigere Antwort hinzuzufügen, habe ich herausgefunden, dass es für mich funktioniert, die Lösung von @ John_T mit einigen hinzugefügten Bits zu verwenden Rosetta-Code, zusammen mit einigen Fehlerbehebungen, um Abhängigkeiten herauszufinden. Es mag ein bisschen ineffizient sein, aber mit intelligenter Programmierung können Sie es zum Laufen bringen und müssen nicht ständig Ihre Terminaldatei öffnen.

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

Wenn Sie sicherstellen, dass Sie nicht alles aufrufen, aber vielleicht hin und wieder sollte es Ihnen gut gehen, es sollte sogar aktualisiert werden, wenn der Benutzer die Größe des Terminalfensters ändert (weil Sie die Datei öffnen und lesen jeder Zeit).

Wenn Sie nicht verwenden TIOCGWINSZ siehe die erste Antwort auf diesem Formular https://www.linuxquestions.org/questions/programming-9/get-width-height-of-a-terminal-window-in-c-810739/.

Ach ja, und nicht vergessen free() das result.

  • Warum würden Sie zurückkehren size_t *? Erstellen Sie einfach eine Struktur dafür und geben Sie den Strukturwert zurück. Auf diese Weise gibt es keine malloc() und nein free() beteiligt.

    – Mecki

    21. Juli 2021 um 18:13 Uhr

  • Für Personen, die diesen Code verwenden: 0_RDWR im Aufruf an open muß sein O_RDWR (O statt 0).

    – pschulz

    17. Januar um 7:57 Uhr

Benutzeravatar von Peter Mortensen
Peter Mortensen

Angenommen, Sie verwenden Linux, ich denke, Sie möchten die verwenden ncurses Bibliothek statt. Ich bin mir ziemlich sicher, dass das ttysize-Zeug, das Sie haben, nicht in stdlib ist.

  • Warum würden Sie zurückkehren size_t *? Erstellen Sie einfach eine Struktur dafür und geben Sie den Strukturwert zurück. Auf diese Weise gibt es keine malloc() und nein free() beteiligt.

    – Mecki

    21. Juli 2021 um 18:13 Uhr

  • Für Personen, die diesen Code verwenden: 0_RDWR im Aufruf an open muß sein O_RDWR (O statt 0).

    – pschulz

    17. Januar um 7:57 Uhr

Benutzeravatar von RajnishCoder
RajnishCoder

Also hier keine Antwort vorschlagen, aber:

linux-pc:~/scratch$ echo $LINES

49

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

linux-pc:~/scratch$

Ok, und ich stelle fest, dass, wenn ich die Größe des GNOME-Terminals verändere, die Variablen LINES und COLUMNS folgen.

Irgendwie scheint das GNOME-Terminal diese Umgebungsvariablen selbst zu erstellen?

  • Und natürlich wird es nicht an C-Code weitergegeben. getenv(“LINES”) gibt NULL zurück.

    – Scott Franco

    18. April 2019 um 6:47 Uhr


  • Variablen sind ein Shell-Ding, kein Terminal-Ding.

    – Melpomen

    5. Juli 2019 um 22:07 Uhr

1422090cookie-checkTerminalbreite in C abrufen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy