1
# terminalsize.py - find size of terminal
2
# Copyright (C) 2008 Canonical, Ltd.
4
# This program is free software: you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation, version 3 of the License.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
# Inspired by code by Chuck Blake, at
17
# http://pdos.csail.mit.edu/~cblake/cls/cls.py, but rewritten in an
18
# attempt to clarify things. This code is slightly tricky, so I thought
19
# the extra clarity would be worth it.
27
def get_terminal_size(fd=1):
28
"""Return size of terminal attached to the standard output.
30
Use ioctl(2) to query a terminal for its size, given a file
31
descriptor attached to the terminal. Return (None, None) if this
32
fails, otherwise a tuple (columns, rows).
34
(The optional 'fd' argument can be set to whatever file
35
descriptor you want to use. This is useful for unit tests.)
40
# Do the ioctl call. termios.TIOCGWINSZ is the code to query
41
# terminal size (see tty_ioctl(4), at least on Linux). We need
42
# to give it a string of suitable size to use as the input
43
# buffer for ioctl. Ioctl modifies the buffer and returns the
44
# modified buffer as its return value.
46
# The manual page specifies a struct winsize to be used, which
47
# consists of four unsigned shorts. We use struct.calcsize to
48
# compute the size of that.
50
# Note that Blake's original code assumes only the first two
51
# shorts in the struct are used, and that two shorts fit into
52
# four bytes, which is probably true for all the relevant
53
# platforms, but is cramped enough that it makes me feel icky.
54
# Thus, I assume less. This will still break if the contents
55
# of the struct change, but since that would change the system
56
# call API, that's unlikely.
58
buflen = struct.calcsize('hhhh')
59
buf = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * buflen)
61
# ioctl returns a binary buffer that represents the struct
62
# at the C level. We unpack it with struct.unpack.
64
tuple = struct.unpack('hhhh', buf)
66
# If anything went wrong, we give up and claim we don't know.
69
return tuple[0], tuple[1]