in reply to Re^3: Failing to get current TTY's rows & columns...
in thread Failing to get current TTY's rows & columns...

I think part of the answer is to do error checking correctly like the faq entry
See my answer to previous poster -- that's a red herring as well as the unpack format.

What you are missing is:

"I.e. Shouldn't I be able to use some mapping function on STDOUT to get a file descriptor that's suitable for ioctl?
ioctl says you don't need to, the faq item doesn't, and neither does Term::Size::Any
The faq item doesn't use STDOUT. It doesn't show how one would use STDOUT to determine the terminal's size. It opens '/dev/tty' -- ANOTHER file handle -- it doesn't show how to determine the size of the terminal attached to STDOUT.

Of note -- if STDOUT was redirected to a file, then trying to determine its size woudn't return valid values (presumably the ioctl would return an error).

So how would you determine the rows/columns of whatever STDOUT is pointing to (without opening another character device like "/dev/tty")?

Even if you aren't on linux, presumably there's a way to do it on your platform, no? I.e. if you have a 'tty' window, how would you determine the size of the window addressed by 'STDOUT'?

  • Comment on Re^4: Failing to get current TTY's rows & columns...

Replies are listed 'Best First'.
Re^5: Failing to get current TTY's rows & columns...
by Anonymous Monk on Apr 15, 2011 at 22:29 UTC
    See my answer to previous poster -- that's a red herring as well as the unpack format.

    Same guy, and no, the red herring is you're confusing c function ioctl with perl function ioctl

    What you are missing is:

    No, I didn't miss it. If you redirect STDIN/STDOUT/STDERR, then you need to give ioctl a handle which is connected to tty, so you open your own to /dev/tty, and it works regardless of the state of STDIN/STDOUT/STDERR

    Even if you aren't on linux, presumably there's a way to do it on your platform, no? I.e. if you have a 'tty' window, how would you determine the size of the window addressed by 'STDOUT'?

    I would use an abstraction like Term::Size::Any, I wouldn't mess around with ioctl

    But in all honesty, unless I'm writing some kind of curses program, I would never need to know the size of the terminal

      Confusing docs for system ioctl & perl ioctl -- got it.

      So my error check has a high level of bogosity! Details! ;-)

      Regardless of the bogus error check, it still tries to extract the row,col meaning that if ioctl(STDOUT...) is valid, it should display the rows & columns.

      I understand it won't work if STDOUT is redirected, but neither will opening "/dev/tty", if the program isn't running with a controlling terminal but DOES have STDOUT directed to a tty. I.e. one can always design a failure case -- but I'm more interested in why what should be a 'success' case doesn't work, namely using ioctl with 'STDOUT' when STDOUT is attached to a terminal.

      Here's a simple program that should work but doesn't. Why doesn't it (I believe I've addressed the error case you raised, as well as the unpack format)?

      #!/usr/bin/perl -w use strict; use feature ':5.10'; { no warnings "all"; $SIG{__WARN__} = sub { }; #pretend "no warnings" works require "sys/ioctl.ph"; } sub getwinsize (;$) { my $recheck=$_[0]; my $winsize=0; state ($maxcols, $maxrows); return ($maxcols,$maxrows) if $maxcols && $maxrows && !$recheck; my $err = ioctl STDOUT, &TIOCGWINSZ, $winsize; unless ($err) { print STDERR "ERROR: ioctl on STDOUT: $!\n"; $err = ioctl fileno(STDOUT), &TIOCGWINSZ, $winsize; unless ($err) { print STDERR "ERROR: ioctl on fileno(STDOUT): $!\n"; } else { printf STDERR "ioctl on fileno(STDOUT) worked\n"; } } else { printf STDERR "ioctl on STDOUT worked\n"; } my ($rows, $cols, $unused1, $unused2) = unpack "S!4", $winsize; printf STDERR "(rows=%u, cols=%u)\n", $rows//0, $cols//0; ($maxcols, $maxrows) = ($cols || -1, $rows || -1); } my ($rows,$cols) = getwinsize; print "rows=$rows, cols=$cols\n"; # vim: ts=2: sw=2
      My output:
      ERROR: ioctl on STDOUT: Bad address ERROR: ioctl on fileno(STDOUT): Bad file descriptor (rows=0, cols=0) rows=-1, cols=-1
        When run,the following code produces such wonders as
        osname = Openbsd sizeof(struct winsize) = 8 TIOCGWINSZ = 0x40087468 C (rows,cols) = (38,126) Perl (rows,cols) = (38,126) osname = Darwin sizeof(struct winsize) = 8 TIOCGWINSZ = 0x40087468 C (rows,cols) = (38,126) Perl (rows,cols) = (38,126) osname = Solaris sizeof(struct winsize) = 8 TIOCGWINSZ = 0x00005468 C (rows,cols) = (38,126) Perl (rows,cols) = (38,126) osname = Linux sizeof(struct winsize) = 8 TIOCGWINSZ = 0x00005413 C (rows,cols) = (38,126) Perl (rows,cols) = (38,126)
        Interesting how you can see their genetic relationships, eh?

        Here you go. Enjoy!

        #!/usr/bin/env perl # # magical winsz demo # # Tom Christiansen <tchrist@perl.com> # Sat Apr 16 22:34:26 MDT 2011 use 5.10.0; use Inline C; no less magic; use strict; use warnings; sub TIOCGWINSZ() { TIOCGWINSZ_constant() } use subs qw{ tty_rows tty_cols tty_xpixels tty_ypixels }; say "osname = \u$^O"; printf "sizeof(struct winsize) = %d\n", sizeof_struct_winsize(); printf "TIOCGWINSZ = %#010x\n", TIOCGWINSZ; # first in C my($rows, $cols) = winsizes(); say "C\t(rows,cols) = ($rows,$cols)"; # now in Perl my $ws = "\0" x sizeof_struct_winsize(); ioctl(STDOUT, TIOCGWINSZ, $ws) || die "ioctl: $!"; ($rows, $cols) = unpack(S4 => $ws); say "Perl\t(rows,cols) = ($rows,$cols)"; exit "0 but true" || goto exit; __END__ __C__ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <sys/ioctl.h> #include <termios.h> int sizeof_struct_winsize() { return sizeof(struct winsize); } int TIOCGWINSZ_constant() { return TIOCGWINSZ; } struct winsize mywinsize; int tty_initted = 0; void init_tty() { int ttyfd; if (tty_initted) return; if ((ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY)) == -1) croak("open /dev/tty"); if (ioctl(ttyfd, TIOCGWINSZ, &mywinsize) == -1) croak("ioctl TIOCGWINSZ"); if (close(ttyfd) == -1) croak("close ttyfd"); /* tty_initted = 1; */ } void winsizes() { init_tty(); Inline_Stack_Vars; Inline_Stack_Reset; #define PUSH(VALUE) Inline_Stack_Push(sv_2mortal(newSViv(VALUE))) PUSH(mywinsize.ws_row); PUSH(mywinsize.ws_col); PUSH(mywinsize.ws_xpixel); PUSH(mywinsize.ws_ypixel); #undef PUSH Inline_Stack_Done; } int tty_rows() { init_tty(); return mywinsize.ws_row; } int tty_cols() { init_tty(); return mywinsize.ws_col; } int tty_xpixels() { init_tty(); return mywinsize.ws_xpixel; } int tty_ypixels() { init_tty(); return mywinsize.ws_ypixel; }
        There, was it good for you? :)