131 lines
3.1 KiB
Python
131 lines
3.1 KiB
Python
# dockerpty: tty.py
|
|
#
|
|
# Copyright 2014 Chris Corbyn <chris@w3style.co.uk>
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from __future__ import absolute_import
|
|
|
|
import os
|
|
import termios
|
|
import tty
|
|
import fcntl
|
|
import struct
|
|
|
|
|
|
def size(fd):
|
|
"""
|
|
Return a tuple (rows,cols) representing the size of the TTY `fd`.
|
|
|
|
The provided file descriptor should be the stdout stream of the TTY.
|
|
|
|
If the TTY size cannot be determined, returns None.
|
|
"""
|
|
|
|
if not os.isatty(fd.fileno()):
|
|
return None
|
|
|
|
try:
|
|
dims = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 'hhhh'))
|
|
except:
|
|
try:
|
|
dims = (os.environ['LINES'], os.environ['COLUMNS'])
|
|
except:
|
|
return None
|
|
|
|
return dims
|
|
|
|
|
|
class Terminal(object):
|
|
"""
|
|
Terminal provides wrapper functionality to temporarily make the tty raw.
|
|
|
|
This is useful when streaming data from a pseudo-terminal into the tty.
|
|
|
|
Example:
|
|
|
|
with Terminal(sys.stdin, raw=True):
|
|
do_things_in_raw_mode()
|
|
"""
|
|
|
|
def __init__(self, fd, raw=True):
|
|
"""
|
|
Initialize a terminal for the tty with stdin attached to `fd`.
|
|
|
|
Initializing the Terminal has no immediate side effects. The `start()`
|
|
method must be invoked, or `with raw_terminal:` used before the
|
|
terminal is affected.
|
|
"""
|
|
|
|
self.fd = fd
|
|
self.raw = raw
|
|
self.original_attributes = None
|
|
|
|
|
|
def __enter__(self):
|
|
"""
|
|
Invoked when a `with` block is first entered.
|
|
"""
|
|
|
|
self.start()
|
|
return self
|
|
|
|
|
|
def __exit__(self, *_):
|
|
"""
|
|
Invoked when a `with` block is finished.
|
|
"""
|
|
|
|
self.stop()
|
|
|
|
|
|
def israw(self):
|
|
"""
|
|
Returns True if the TTY should operate in raw mode.
|
|
"""
|
|
|
|
return self.raw
|
|
|
|
|
|
def start(self):
|
|
"""
|
|
Saves the current terminal attributes and makes the tty raw.
|
|
|
|
This method returns None immediately.
|
|
"""
|
|
|
|
if os.isatty(self.fd.fileno()) and self.israw():
|
|
self.original_attributes = termios.tcgetattr(self.fd)
|
|
tty.setraw(self.fd)
|
|
|
|
|
|
def stop(self):
|
|
"""
|
|
Restores the terminal attributes back to before setting raw mode.
|
|
|
|
If the raw terminal was not started, does nothing.
|
|
"""
|
|
|
|
if self.original_attributes is not None:
|
|
termios.tcsetattr(
|
|
self.fd,
|
|
termios.TCSADRAIN,
|
|
self.original_attributes,
|
|
)
|
|
|
|
def __repr__(self):
|
|
return "{cls}({fd}, raw={raw})".format(
|
|
cls=type(self).__name__,
|
|
fd=self.fd,
|
|
raw=self.raw)
|