#!/usr/bin/env python # Get the current size of the terminal window, and set stty size accordingly. # A replacement for xterm's resize program, with no X dependency. # Useful when logged in over a serial line. # Copyright 2013,2017 by Akkana Peck. Share and enjoy under the GPL v2 or later. # In the unlikely event you ever need to *set* the terminal size, check out: # https://github.com/zerorax/pyresize/blob/master/resize.py import os, sys import fcntl import struct import time import re import tty import termios import select try: import curses except: pass # python3 has get_terminal size, but it doesn't actually query # the terminal, just trusts current stty settings, which isn't # useful here. But 3 has different open() characteristics so we # have to detect version, not just features. if (sys.version_info < (3, 0)): ttyfd = open('/dev/tty', 'r+') else: # Python3 needs extra flags set, which requires a 2-step open process: fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY) ttyfd = open(fd, 'wb+', buffering=0) def get_terminal_size_curses(): stdscr = curses.initscr() rows, cols = stdscr.getmaxyx() curses.endwin() return rows, cols def get_terminal_size_noncurses(): # The non-curses version works well under xterm, but in urxvt, # the terminal echoes back the rows and columns, but in a way # that makes it see those characters as input. I haven't # found a way around that. # Save the terminal state fileno = sys.stdin.fileno() stty_sav = termios.tcgetattr(sys.stdin) fc_sav = fcntl.fcntl(fileno, fcntl.F_GETFL) # Turn off echo. stty_new = termios.tcgetattr(sys.stdin) stty_new[3] = stty_new[3] & ~termios.ECHO termios.tcsetattr(sys.stdin, termios.TCSADRAIN, stty_new) ttyfd.write(b'\033[7\033[r\033[999;999H\033[6n') ttyfd.flush() # Put stdin into cbreak mode. # Python2 can use fd here, but in Python3 we have to get sys.stdin.fileno. tty.setcbreak(sys.stdin) # Nonblocking mode. fcntl.fcntl(fileno, fcntl.F_SETFL, fc_sav | os.O_NONBLOCK) try: while True: r, w, e = select.select([ttyfd], [], []) if r: output = sys.stdin.read() break finally: # Reset the terminal back to normal cooked mode termios.tcsetattr(fileno, termios.TCSAFLUSH, stty_sav) fcntl.fcntl(fileno, fcntl.F_SETFL, fc_sav) rows, cols = list(map(int, re.findall(r'\d+', output))) return rows, cols if __name__ == '__main__': try: rows, cols = get_terminal_size_curses() except: rows, cols = get_terminal_size_noncurses() fcntl.ioctl(ttyfd, termios.TIOCSWINSZ, struct.pack("HHHH", rows, cols, 0, 0)) print("Reset the terminal to %d rows, %d cols" % (rows, cols))