C terminal genişliğini mi alıyorsunuz?


91

C programımdan terminal genişliğini elde etmenin bir yolunu arıyordum. Bulmaya devam ettiğim şey şu çizgide bir şey:

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

Ama bunu her denediğimde

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

Bunu yapmanın en iyi yolu bu mu yoksa daha iyi bir yolu var mı? Değilse, bunu nasıl çalıştırabilirim?

DÜZENLEME: sabit 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;
}

1
önerilen cevapların hiçbiri yarıdan fazla doğru değil.
Thomas Dickey

2
@ThomasDickey, cevabın nerede o zaman?
Alexis Wilke

Yanıtlar:


127

Getenv () kullanmayı düşündünüz mü ? Terminal sütun ve satırlarını içeren sistemin ortam değişkenlerini almanızı sağlar.

Alternatif olarak, yönteminizi kullanarak, çekirdeğin terminal boyutu olarak ne gördüğünü görmek istiyorsanız (terminalin yeniden boyutlandırılması durumunda daha iyidir), TIOCGSIZE yerine TIOCGWINSZ kullanmanız gerekir, şöyle:

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

ve tam 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
}

7
evet ama genişlik terimi çevresel bir değişken değildir, terim için statiktir.
austin

4
Programın yürütülmesi sırasında birisi terminali yeniden boyutlandırırsa, size mevcut terminal boyutunu sağlamaz .
Chris Jester-Young

evet, bunu ekliyordu :)
John T

piksel cinsinden boyutlar nasıl alınır? Kullandım ws_xpixelve ws_ypixelama sadece sıfırlar yazdırıyor!
Debashish

@Debashish Bağlıdır. Örneğin, Linux bu alanları hiç desteklemiyor.
melpomene

16

Bu örnek biraz uzun, ancak uçbirim boyutlarını algılamanın en taşınabilir yolu olduğuna inanıyorum. Bu aynı zamanda yeniden boyutlandırma olaylarını da işler.

Tim ve rlbond'un önerdiği gibi, ncurses kullanıyorum. Doğrudan okuma ortamı değişkenlerine kıyasla uçbirim uyumluluğunda büyük bir gelişme sağlar.

#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
Ama bir sinyal işleyiciden initscr'yi aramak ve endwin'i aramak gerçekten güvenli mi? Onlar en azından zaman uyumsuz-sinyal güvenli API'ler arasında yer konumman 7 signal
nav

1
Bu iyi bir nokta @ nav, bunu hiç düşünmemiştim! Belki daha iyi bir çözüm, sinyal işleyicinin bir bayrak kaldırması ve ardından ana döngüdeki işlemlerin geri kalanını gerçekleştirmesi olabilir mi?
gamen

1
@gamen, evet, bu daha iyi olurdu;) - sinyal yerine sigaction kullanmak da daha iyi olurdu.
Bodo Thiesen

Öyleyse COLS ve LINES global değişkenler mi?
einpoklum

1
@AlexisWilke: OKve dahil ERR. Hayatımızdaki bu boşluğu doldurmamıza ne kadar "nazik" yardım
edecekler

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

İle derlenmesi gerekiyor -ltermcap. Termcap kullanarak elde edebileceğiniz birçok yararlı bilgi var. info termcapDaha fazla ayrıntı için termcap kılavuzunu kontrol edin .


-Lcurses ile de derleyebilirsiniz.
Kambus

2
Bu yorumun olaydan 6 yıl sonra geldiğini biliyorum, ama lütfen sihirli
sayınız olan 2048'i açıklayın

1
@einpoklum Bu yaklaşık üç yıl sonra, ancak 2048'in arabellek için sadece keyfi bir boyut olduğu ve oraya giden her ne olursa olsun "muhtemelen yeterince büyük olması gereken" oldukça açık değil mi?
Roflcopter4

2
Aslında bu cevap, doğru olamayacak kadar çok varsayım yapıyor.
Thomas Dickey

1
Merak edenler için, 2048 arabellek boyutu GNU termcap belgelerinde açıklanmıştır: gnu.org/software/termutils/manual/termcap-1.3/html_mono/… Ayrıca, bu yazıyı okuyan insanların yararlı bulabileceği birçok başka şey de var. .

3

Eğer ncurses kurduysanız ve kullanıyorsanız getmaxyx(), terminalin boyutlarını bulmak için kullanabilirsiniz .


2
Evet ve önce Y'nin ardından X'in geldiğini unutmayın.
Daniel

0

Linux'ta olduğunuzu varsayarsak, bunun yerine ncurses kitaplığını kullanmak isteyeceğinizi düşünüyorum . Sahip olduğunuz ttysize öğesinin stdlib'de olmadığından oldukça eminim.


Pekala, yaptığım şey için ncurses kurmaya değmez
austin

ncurses de stdlib'de değil. Her ikisi de ioctl
POSIX'te

0

Yani burada bir cevap önermiyoruz, ama:

linux-pc:~/scratch$ echo $LINES

49

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

linux-pc:~/scratch$

Tamam, ve GNOME terminalini yeniden boyutlandırırsam LINES ve COLUMNS değişkenlerinin bunu takip ettiğini fark ettim.

GNOME terminali bu ortam değişkenlerini kendisi yaratıyor gibi görünüyor?


1
Ve kesinlikle C koduna geçmiyor. getenv ("LINES") NULL döndürür.
Scott Franco

Değişkenler bir kabuk şeydir, uç bir şey değildir.
melpomene

0

Daha eksiksiz bir cevap eklemek için, benim için işe yaradığını bulduğum şey, @ John_T'nin çözümünü Rosetta Kodundan eklenen bazı bitlerle birlikte kullanmak ve bağımlılıkları çözen bazı sorun giderme ile birlikte. Biraz verimsiz olabilir, ancak akıllı programlama ile çalışmasını sağlayabilir ve her zaman terminal dosyanızı açmayabilirsiniz.

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

Hepsini çağırmadığınızdan emin olursanız, ancak belki arada bir sorun olmazsa, kullanıcı terminal penceresini yeniden boyutlandırdığında bile güncellenmelidir (çünkü dosyayı açıp her seferinde okuyorsunuz ).

Eğer kullanmıyorsanız TIOCGWINSZbu forma birinci cevaba bakınız https://www.linuxquestions.org/questions/programming-9/get-width-height-of-a-terminal-window-in-c-810739/ .

Oh ve unutmayın .free()result


-1

Zaten önerilen çevresel değişken şey için işlev çağrıları:

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

11
Ortam değişkenleri güvenilir değildir. Bu değerler kabuk tarafından belirlenir, dolayısıyla var olmaları garanti edilmez. Ayrıca, kullanıcı terminal boyutunu değiştirirse güncel olmayacaktır.
Juliano

1
Çoğu kabuk, SIGWINCHsinyal için bir işleyici oluşturur , böylece değişkenleri güncel tutabilirler (ayrıca buna ihtiyaç duyarlar, böylece girdi düzenleyicide düzgün satır kaydırma yapabilirler).
Barmar

5
Bunu yapabilirler, ancak bir programın ortamı çalışırken güncellenmeyecektir.
Functino

Tabii ki, getenv()NULL döndürüp döndürmediğini test etmediğiniz ve benim Linux terminalimde yaptığı için (çünkü bu değişkenler dışa aktarılmadığı için) bu kodun çökme olasılığı çok yüksektir . Ayrıca kabuk bu değişkenleri güncellese bile, programınız çalışırken değişir (kendi SIGWINCHişleyiciniz olmadan olmaz ).
Alexis Wilke
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.