Skip to content

Commit

Permalink
Use two instances of WinTerm, for stderr and stdout
Browse files Browse the repository at this point in the history
  • Loading branch information
Arnon Yaari committed Mar 25, 2019
1 parent 4832ef9 commit eccb87c
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 55 deletions.
28 changes: 16 additions & 12 deletions colorama/ansitowin32.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
from .win32 import windll, winapi_test


winterm = None
winterm_stdout = None
winterm_stderr = None
if windll is not None:
winterm = WinTerm()
winterm_stdout = WinTerm(on_stderr=False)
winterm_stderr = WinTerm(on_stderr=True)


class StreamWrapper(object):
Expand Down Expand Up @@ -97,12 +99,11 @@ def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
convert = conversion_supported and not self.stream.closed and self.stream.isatty()
self.convert = convert

self.on_stderr = self.wrapped is sys.stderr

# dict of ansi codes to win32 functions and parameters
self.win32_calls = self.get_win32_calls()

# are we wrapping stderr?
self.on_stderr = self.wrapped is sys.stderr

def should_wrap(self):
'''
True if this class is actually needed. If false, then the output
Expand All @@ -113,7 +114,11 @@ def should_wrap(self):
'''
return self.convert or self.strip or self.autoreset

def get_winterm(self):
return winterm_stderr if self.on_stderr else winterm_stdout

def get_win32_calls(self):
winterm = self.get_winterm()
if self.convert and winterm:
return {
AnsiStyle.RESET_ALL: (winterm.reset_all, ),
Expand Down Expand Up @@ -227,19 +232,18 @@ def call_win32(self, command, params):
func_args = self.win32_calls[param]
func = func_args[0]
args = func_args[1:]
kwargs = dict(on_stderr=self.on_stderr)
func(*args, **kwargs)
func(*args)
elif command in 'J':
winterm.erase_screen(params[0], on_stderr=self.on_stderr)
self.get_winterm().erase_screen(params[0])
elif command in 'K':
winterm.erase_line(params[0], on_stderr=self.on_stderr)
self.get_winterm().erase_line(params[0])
elif command in 'Hf': # cursor position - absolute
winterm.set_cursor_position(params, on_stderr=self.on_stderr)
self.get_winterm().set_cursor_position(params)
elif command in 'ABCD': # cursor position - relative
n = params[0]
# A - up, B - down, C - forward, D - back
x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command]
winterm.cursor_adjust(x, y, on_stderr=self.on_stderr)
self.get_winterm().cursor_adjust(x, y)


def convert_osc(self, text):
Expand All @@ -254,5 +258,5 @@ def convert_osc(self, text):
# 1 - change icon (we don't support this)
# 2 - change title
if params[0] in '02':
winterm.set_title(params[1])
self.get_winterm().set_title(params[1])
return text
72 changes: 29 additions & 43 deletions colorama/winterm.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ class WinStyle(object):

class WinTerm(object):

def __init__(self):
self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
def __init__(self, on_stderr=False):
self._handle = win32.STDERR if on_stderr else win32.STDOUT
self._default = win32.GetConsoleScreenBufferInfo(self._handle).wAttributes
self.set_attrs(self._default)
self._default_fore = self._fore
self._default_back = self._back
Expand All @@ -41,12 +42,12 @@ def set_attrs(self, value):
self._back = (value >> 4) & 7
self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND)

def reset_all(self, on_stderr=None):
def reset_all(self):
self.set_attrs(self._default)
self.set_console(attrs=self._default)
self._light = 0

def fore(self, fore=None, light=False, on_stderr=False):
def fore(self, fore=None, light=False):
if fore is None:
fore = self._default_fore
self._fore = fore
Expand All @@ -55,9 +56,9 @@ def fore(self, fore=None, light=False, on_stderr=False):
self._light |= WinStyle.BRIGHT
else:
self._light &= ~WinStyle.BRIGHT
self.set_console(on_stderr=on_stderr)
self.set_console()

def back(self, back=None, light=False, on_stderr=False):
def back(self, back=None, light=False):
if back is None:
back = self._default_back
self._back = back
Expand All @@ -66,56 +67,44 @@ def back(self, back=None, light=False, on_stderr=False):
self._light |= WinStyle.BRIGHT_BACKGROUND
else:
self._light &= ~WinStyle.BRIGHT_BACKGROUND
self.set_console(on_stderr=on_stderr)
self.set_console()

def style(self, style=None, on_stderr=False):
def style(self, style=None):
if style is None:
style = self._default_style
self._style = style
self.set_console(on_stderr=on_stderr)
self.set_console()

def set_console(self, attrs=None, on_stderr=False):
def set_console(self, attrs=None):
if attrs is None:
attrs = self.get_attrs()
handle = win32.STDOUT
if on_stderr:
handle = win32.STDERR
win32.SetConsoleTextAttribute(handle, attrs)
win32.SetConsoleTextAttribute(self._handle, attrs)

def get_position(self, handle):
position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
def get_position(self):
position = win32.GetConsoleScreenBufferInfo(self._handle).dwCursorPosition
# Because Windows coordinates are 0-based,
# and win32.SetConsoleCursorPosition expects 1-based.
position.X += 1
position.Y += 1
return position

def set_cursor_position(self, position=None, on_stderr=False):
def set_cursor_position(self, position=None):
if position is None:
# I'm not currently tracking the position, so there is no default.
# position = self.get_position()
return
handle = win32.STDOUT
if on_stderr:
handle = win32.STDERR
win32.SetConsoleCursorPosition(handle, position)

def cursor_adjust(self, x, y, on_stderr=False):
handle = win32.STDOUT
if on_stderr:
handle = win32.STDERR
position = self.get_position(handle)
win32.SetConsoleCursorPosition(self._handle, position)

def cursor_adjust(self, x, y):
position = self.get_position()
adjusted_position = (position.Y + y, position.X + x)
win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)
win32.SetConsoleCursorPosition(self._handle, adjusted_position, adjust=False)

def erase_screen(self, mode=0, on_stderr=False):
def erase_screen(self, mode=0,):
# 0 should clear from the cursor to the end of the screen.
# 1 should clear from the cursor to the beginning of the screen.
# 2 should clear the entire screen, and move cursor to (1,1)
handle = win32.STDOUT
if on_stderr:
handle = win32.STDERR
csbi = win32.GetConsoleScreenBufferInfo(handle)
csbi = win32.GetConsoleScreenBufferInfo(self._handle)
# get the number of character cells in the current buffer
cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
# get number of character cells before current cursor position
Expand All @@ -133,21 +122,18 @@ def erase_screen(self, mode=0, on_stderr=False):
# invalid mode
return
# fill the entire screen with blanks
win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
win32.FillConsoleOutputCharacter(self._handle, ' ', cells_to_erase, from_coord)
# now set the buffer's attributes accordingly
win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
win32.FillConsoleOutputAttribute(self._handle, self.get_attrs(), cells_to_erase, from_coord)
if mode == 2:
# put the cursor where needed
win32.SetConsoleCursorPosition(handle, (1, 1))
win32.SetConsoleCursorPosition(self._handle, (1, 1))

def erase_line(self, mode=0, on_stderr=False):
def erase_line(self, mode=0):
# 0 should clear from the cursor to the end of the line.
# 1 should clear from the cursor to the beginning of the line.
# 2 should clear the entire line.
handle = win32.STDOUT
if on_stderr:
handle = win32.STDERR
csbi = win32.GetConsoleScreenBufferInfo(handle)
csbi = win32.GetConsoleScreenBufferInfo(self._handle)
if mode == 0:
from_coord = csbi.dwCursorPosition
cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
Expand All @@ -161,9 +147,9 @@ def erase_line(self, mode=0, on_stderr=False):
# invalid mode
return
# fill the entire screen with blanks
win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
win32.FillConsoleOutputCharacter(self._handle, ' ', cells_to_erase, from_coord)
# now set the buffer's attributes accordingly
win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
win32.FillConsoleOutputAttribute(self._handle, self.get_attrs(), cells_to_erase, from_coord)

def set_title(self, title):
win32.SetConsoleTitle(title)

0 comments on commit eccb87c

Please sign in to comment.