1import os
2
3
4def get_terminal_size():  # pragma: no cover
5    '''Get the current size of your terminal
6
7    Multiple returns are not always a good idea, but in this case it greatly
8    simplifies the code so I believe it's justified. It's not the prettiest
9    function but that's never really possible with cross-platform code.
10
11    Returns:
12        width, height: Two integers containing width and height
13    '''
14
15    try:
16        # Default to 79 characters for IPython notebooks
17        from IPython import get_ipython
18        ipython = get_ipython()
19        from ipykernel import zmqshell
20        if isinstance(ipython, zmqshell.ZMQInteractiveShell):
21            return 79, 24
22    except Exception:  # pragma: no cover
23        pass
24
25    try:
26        # This works for Python 3, but not Pypy3. Probably the best method if
27        # it's supported so let's always try
28        import shutil
29        w, h = shutil.get_terminal_size()
30        if w and h:
31            # The off by one is needed due to progressbars in some cases, for
32            # safety we'll always substract it.
33            return w - 1, h
34    except Exception:  # pragma: no cover
35        pass
36
37    try:
38        w = int(os.environ.get('COLUMNS'))
39        h = int(os.environ.get('LINES'))
40        if w and h:
41            return w, h
42    except Exception:  # pragma: no cover
43        pass
44
45    try:
46        import blessings
47        terminal = blessings.Terminal()
48        w = terminal.width
49        h = terminal.height
50        if w and h:
51            return w, h
52    except Exception:  # pragma: no cover
53        pass
54
55    try:
56        w, h = _get_terminal_size_linux()
57        if w and h:
58            return w, h
59    except Exception:  # pragma: no cover
60        pass
61
62    try:
63        # Windows detection doesn't always work, let's try anyhow
64        w, h = _get_terminal_size_windows()
65        if w and h:
66            return w, h
67    except Exception:  # pragma: no cover
68        pass
69
70    try:
71        # needed for window's python in cygwin's xterm!
72        w, h = _get_terminal_size_tput()
73        if w and h:
74            return w, h
75    except Exception:  # pragma: no cover
76        pass
77
78    return 79, 24
79
80
81def _get_terminal_size_windows():  # pragma: no cover
82    res = None
83    try:
84        from ctypes import windll, create_string_buffer
85
86        # stdin handle is -10
87        # stdout handle is -11
88        # stderr handle is -12
89
90        h = windll.kernel32.GetStdHandle(-12)
91        csbi = create_string_buffer(22)
92        res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
93    except Exception:
94        return None
95
96    if res:
97        import struct
98        (_, _, _, _, _, left, top, right, bottom, _, _) = \
99            struct.unpack("hhhhHhhhhhh", csbi.raw)
100        w = right - left
101        h = bottom - top
102        return w, h
103    else:
104        return None
105
106
107def _get_terminal_size_tput():  # pragma: no cover
108    # get terminal width src: http://stackoverflow.com/questions/263890/
109    try:
110        import subprocess
111        proc = subprocess.Popen(
112            ['tput', 'cols'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
113            stderr=subprocess.PIPE)
114        output = proc.communicate(input=None)
115        w = int(output[0])
116        proc = subprocess.Popen(
117            ['tput', 'lines'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
118            stderr=subprocess.PIPE)
119        output = proc.communicate(input=None)
120        h = int(output[0])
121        return w, h
122    except Exception:
123        return None
124
125
126def _get_terminal_size_linux():  # pragma: no cover
127    def ioctl_GWINSZ(fd):
128        try:
129            import fcntl
130            import termios
131            import struct
132            size = struct.unpack(
133                'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
134        except Exception:
135            return None
136        return size
137
138    size = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
139
140    if not size:
141        try:
142            fd = os.open(os.ctermid(), os.O_RDONLY)
143            size = ioctl_GWINSZ(fd)
144            os.close(fd)
145        except Exception:
146            pass
147    if not size:
148        try:
149            size = os.environ['LINES'], os.environ['COLUMNS']
150        except Exception:
151            return None
152
153    return int(size[1]), int(size[0])
154