Updating colorama (Issue #1784)

This commit is contained in:
Miroslav Stampar 2016-03-30 15:11:34 +02:00
parent 0245ce6228
commit e83d8f6143
8 changed files with 378 additions and 157 deletions

View File

@ -12,7 +12,7 @@ This file lists bundled packages and their associated licensing terms.
Copyright (C) 2005, Zope Corporation. Copyright (C) 2005, Zope Corporation.
Copyright (C) 1998-2000, Gisle Aas. Copyright (C) 1998-2000, Gisle Aas.
* The Colorama library located under thirdparty/colorama/. * The Colorama library located under thirdparty/colorama/.
Copyright (C) 2010, Jonathan Hartley. Copyright (C) 2013, Jonathan Hartley.
* The Fcrypt library located under thirdparty/fcrypt/. * The Fcrypt library located under thirdparty/fcrypt/.
Copyright (C) 2000, 2001, 2004 Carey Evans. Copyright (C) 2000, 2001, 2004 Carey Evans.
* The Odict library located under thirdparty/odict/. * The Odict library located under thirdparty/odict/.

View File

@ -20,7 +20,7 @@ from lib.core.enums import OS
from lib.core.revision import getRevisionNumber from lib.core.revision import getRevisionNumber
# sqlmap version (<major>.<minor>.<month>.<monthly commit>) # sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.0.3.9" VERSION = "1.0.3.10"
REVISION = getRevisionNumber() REVISION = getRevisionNumber()
STABLE = VERSION.count('.') <= 2 STABLE = VERSION.count('.') <= 2
VERSION_STRING = "sqlmap/%s#%s" % (VERSION, "stable" if STABLE else "dev") VERSION_STRING = "sqlmap/%s#%s" % (VERSION, "stable" if STABLE else "dev")

View File

@ -0,0 +1,7 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
from .initialise import init, deinit, reinit, colorama_text
from .ansi import Fore, Back, Style, Cursor
from .ansitowin32 import AnsiToWin32
__version__ = '0.3.7'

View File

@ -1,21 +1,52 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
''' '''
This module generates ANSI character codes to printing colors to terminals. This module generates ANSI character codes to printing colors to terminals.
See: http://en.wikipedia.org/wiki/ANSI_escape_code See: http://en.wikipedia.org/wiki/ANSI_escape_code
''' '''
CSI = '\033[' CSI = '\033['
OSC = '\033]'
BEL = '\007'
def code_to_chars(code): def code_to_chars(code):
return CSI + str(code) + 'm' return CSI + str(code) + 'm'
def set_title(title):
return OSC + '2;' + title + BEL
def clear_screen(mode=2):
return CSI + str(mode) + 'J'
def clear_line(mode=2):
return CSI + str(mode) + 'K'
class AnsiCodes(object): class AnsiCodes(object):
def __init__(self, codes): def __init__(self):
for name in dir(codes): # the subclasses declare class attributes which are numbers.
# Upon instantiation we define instance attributes, which are the same
# as the class attributes but wrapped with the ANSI escape sequence
for name in dir(self):
if not name.startswith('_'): if not name.startswith('_'):
value = getattr(codes, name) value = getattr(self, name)
setattr(self, name, code_to_chars(value)) setattr(self, name, code_to_chars(value))
class AnsiFore:
class AnsiCursor(object):
def UP(self, n=1):
return CSI + str(n) + 'A'
def DOWN(self, n=1):
return CSI + str(n) + 'B'
def FORWARD(self, n=1):
return CSI + str(n) + 'C'
def BACK(self, n=1):
return CSI + str(n) + 'D'
def POS(self, x=1, y=1):
return CSI + str(y) + ';' + str(x) + 'H'
class AnsiFore(AnsiCodes):
BLACK = 30 BLACK = 30
RED = 31 RED = 31
GREEN = 32 GREEN = 32
@ -26,7 +57,18 @@ class AnsiFore:
WHITE = 37 WHITE = 37
RESET = 39 RESET = 39
class AnsiBack: # These are fairly well supported, but not part of the standard.
LIGHTBLACK_EX = 90
LIGHTRED_EX = 91
LIGHTGREEN_EX = 92
LIGHTYELLOW_EX = 93
LIGHTBLUE_EX = 94
LIGHTMAGENTA_EX = 95
LIGHTCYAN_EX = 96
LIGHTWHITE_EX = 97
class AnsiBack(AnsiCodes):
BLACK = 40 BLACK = 40
RED = 41 RED = 41
GREEN = 42 GREEN = 42
@ -37,13 +79,24 @@ class AnsiBack:
WHITE = 47 WHITE = 47
RESET = 49 RESET = 49
class AnsiStyle: # These are fairly well supported, but not part of the standard.
LIGHTBLACK_EX = 100
LIGHTRED_EX = 101
LIGHTGREEN_EX = 102
LIGHTYELLOW_EX = 103
LIGHTBLUE_EX = 104
LIGHTMAGENTA_EX = 105
LIGHTCYAN_EX = 106
LIGHTWHITE_EX = 107
class AnsiStyle(AnsiCodes):
BRIGHT = 1 BRIGHT = 1
DIM = 2 DIM = 2
NORMAL = 22 NORMAL = 22
RESET_ALL = 0 RESET_ALL = 0
Fore = AnsiCodes( AnsiFore ) Fore = AnsiFore()
Back = AnsiCodes( AnsiBack ) Back = AnsiBack()
Style = AnsiCodes( AnsiStyle ) Style = AnsiStyle()
Cursor = AnsiCursor()

View File

@ -1,16 +1,22 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
import re import re
import sys import sys
import os
from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
from .winterm import WinTerm, WinColor, WinStyle from .winterm import WinTerm, WinColor, WinStyle
from .win32 import windll from .win32 import windll, winapi_test
winterm = None winterm = None
if windll is not None: if windll is not None:
winterm = WinTerm() winterm = WinTerm()
def is_stream_closed(stream):
return not hasattr(stream, 'closed') or stream.closed
def is_a_tty(stream): def is_a_tty(stream):
return hasattr(stream, 'isatty') and stream.isatty() return hasattr(stream, 'isatty') and stream.isatty()
@ -40,7 +46,8 @@ class AnsiToWin32(object):
sequences from the text, and if outputting to a tty, will convert them into sequences from the text, and if outputting to a tty, will convert them into
win32 function calls. win32 function calls.
''' '''
ANSI_RE = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') ANSI_CSI_RE = re.compile('\001?\033\[((?:\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer
ANSI_OSC_RE = re.compile('\001?\033\]((?:.|;)*?)(\x07)\002?') # Operating System Command
def __init__(self, wrapped, convert=None, strip=None, autoreset=False): def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
# The wrapped stream (normally sys.stdout or sys.stderr) # The wrapped stream (normally sys.stdout or sys.stderr)
@ -52,16 +59,21 @@ class AnsiToWin32(object):
# create the proxy wrapping our output stream # create the proxy wrapping our output stream
self.stream = StreamWrapper(wrapped, self) self.stream = StreamWrapper(wrapped, self)
on_windows = sys.platform.startswith('win') on_windows = os.name == 'nt'
# We test if the WinAPI works, because even if we are on Windows
# we may be using a terminal that doesn't support the WinAPI
# (e.g. Cygwin Terminal). In this case it's up to the terminal
# to support the ANSI codes.
conversion_supported = on_windows and winapi_test()
# should we strip ANSI sequences from our output? # should we strip ANSI sequences from our output?
if strip is None: if strip is None:
strip = on_windows strip = conversion_supported or (not is_stream_closed(wrapped) and not is_a_tty(wrapped))
self.strip = strip self.strip = strip
# should we should convert ANSI sequences into win32 calls? # should we should convert ANSI sequences into win32 calls?
if convert is None: if convert is None:
convert = on_windows and is_a_tty(wrapped) convert = conversion_supported and not is_stream_closed(wrapped) and is_a_tty(wrapped)
self.convert = convert self.convert = convert
# dict of ansi codes to win32 functions and parameters # dict of ansi codes to win32 functions and parameters
@ -70,7 +82,6 @@ class AnsiToWin32(object):
# are we wrapping stderr? # are we wrapping stderr?
self.on_stderr = self.wrapped is sys.stderr self.on_stderr = self.wrapped is sys.stderr
def should_wrap(self): def should_wrap(self):
''' '''
True if this class is actually needed. If false, then the output True if this class is actually needed. If false, then the output
@ -81,7 +92,6 @@ class AnsiToWin32(object):
''' '''
return self.convert or self.strip or self.autoreset return self.convert or self.strip or self.autoreset
def get_win32_calls(self): def get_win32_calls(self):
if self.convert and winterm: if self.convert and winterm:
return { return {
@ -98,6 +108,14 @@ class AnsiToWin32(object):
AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
AnsiFore.WHITE: (winterm.fore, WinColor.GREY), AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
AnsiFore.RESET: (winterm.fore, ), AnsiFore.RESET: (winterm.fore, ),
AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True),
AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True),
AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True),
AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True),
AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True),
AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True),
AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True),
AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True),
AnsiBack.BLACK: (winterm.back, WinColor.BLACK), AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
AnsiBack.RED: (winterm.back, WinColor.RED), AnsiBack.RED: (winterm.back, WinColor.RED),
AnsiBack.GREEN: (winterm.back, WinColor.GREEN), AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
@ -107,8 +125,16 @@ class AnsiToWin32(object):
AnsiBack.CYAN: (winterm.back, WinColor.CYAN), AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
AnsiBack.WHITE: (winterm.back, WinColor.GREY), AnsiBack.WHITE: (winterm.back, WinColor.GREY),
AnsiBack.RESET: (winterm.back, ), AnsiBack.RESET: (winterm.back, ),
AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True),
AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True),
AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True),
AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True),
AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True),
AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True),
AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True),
AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True),
} }
return dict()
def write(self, text): def write(self, text):
if self.strip or self.convert: if self.strip or self.convert:
@ -123,7 +149,7 @@ class AnsiToWin32(object):
def reset_all(self): def reset_all(self):
if self.convert: if self.convert:
self.call_win32('m', (0,)) self.call_win32('m', (0,))
elif is_a_tty(self.wrapped): elif not self.strip and not is_stream_closed(self.wrapped):
self.wrapped.write(Style.RESET_ALL) self.wrapped.write(Style.RESET_ALL)
@ -134,7 +160,8 @@ class AnsiToWin32(object):
calls. calls.
''' '''
cursor = 0 cursor = 0
for match in self.ANSI_RE.finditer(text): text = self.convert_osc(text)
for match in self.ANSI_CSI_RE.finditer(text):
start, end = match.span() start, end = match.span()
self.write_plain_text(text, cursor, start) self.write_plain_text(text, cursor, start)
self.convert_ansi(*match.groups()) self.convert_ansi(*match.groups())
@ -150,21 +177,29 @@ class AnsiToWin32(object):
def convert_ansi(self, paramstring, command): def convert_ansi(self, paramstring, command):
if self.convert: if self.convert:
params = self.extract_params(paramstring) params = self.extract_params(command, paramstring)
self.call_win32(command, params) self.call_win32(command, params)
def extract_params(self, paramstring): def extract_params(self, command, paramstring):
def split(paramstring): if command in 'Hf':
for p in paramstring.split(';'): params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';'))
if p != '': while len(params) < 2:
yield int(p) # defaults:
return tuple(split(paramstring)) params = params + (1,)
else:
params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0)
if len(params) == 0:
# defaults:
if command in 'JKm':
params = (0,)
elif command in 'ABCD':
params = (1,)
return params
def call_win32(self, command, params): def call_win32(self, command, params):
if params == []:
params = [0]
if command == 'm': if command == 'm':
for param in params: for param in params:
if param in self.win32_calls: if param in self.win32_calls:
@ -173,17 +208,29 @@ class AnsiToWin32(object):
args = func_args[1:] args = func_args[1:]
kwargs = dict(on_stderr=self.on_stderr) kwargs = dict(on_stderr=self.on_stderr)
func(*args, **kwargs) func(*args, **kwargs)
elif command in ('H', 'f'): # set cursor position elif command in 'J':
func = winterm.set_cursor_position winterm.erase_screen(params[0], on_stderr=self.on_stderr)
func(params, on_stderr=self.on_stderr) elif command in 'K':
elif command in ('J'): winterm.erase_line(params[0], on_stderr=self.on_stderr)
func = winterm.erase_data elif command in 'Hf': # cursor position - absolute
func(params, on_stderr=self.on_stderr) winterm.set_cursor_position(params, on_stderr=self.on_stderr)
elif command == 'A': elif command in 'ABCD': # cursor position - relative
if params == () or params == None: n = params[0]
num_rows = 1 # A - up, B - down, C - forward, D - back
else: x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command]
num_rows = params[0] winterm.cursor_adjust(x, y, on_stderr=self.on_stderr)
func = winterm.cursor_up
func(num_rows, on_stderr=self.on_stderr)
def convert_osc(self, text):
for match in self.ANSI_OSC_RE.finditer(text):
start, end = match.span()
text = text[:start] + text[end:]
paramstring, command = match.groups()
if command in '\x07': # \x07 = BEL
params = paramstring.split(";")
# 0 - change title and icon (we will only change title)
# 1 - change icon (we don't support this)
# 2 - change title
if params[0] in '02':
winterm.set_title(params[1])
return text

View File

@ -1,19 +1,22 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
import atexit import atexit
import contextlib
import sys import sys
from .ansitowin32 import AnsiToWin32 from .ansitowin32 import AnsiToWin32
orig_stdout = sys.stdout orig_stdout = None
orig_stderr = sys.stderr orig_stderr = None
wrapped_stdout = sys.stdout wrapped_stdout = None
wrapped_stderr = sys.stderr wrapped_stderr = None
atexit_done = False atexit_done = False
def reset_all(): def reset_all():
if AnsiToWin32 is not None: # Issue #74: objects might become None at exit
AnsiToWin32(orig_stdout).reset_all() AnsiToWin32(orig_stdout).reset_all()
@ -23,8 +26,19 @@ def init(autoreset=False, convert=None, strip=None, wrap=True):
raise ValueError('wrap=False conflicts with any other arg=True') raise ValueError('wrap=False conflicts with any other arg=True')
global wrapped_stdout, wrapped_stderr global wrapped_stdout, wrapped_stderr
global orig_stdout, orig_stderr
orig_stdout = sys.stdout
orig_stderr = sys.stderr
if sys.stdout is None:
wrapped_stdout = None
else:
sys.stdout = wrapped_stdout = \ sys.stdout = wrapped_stdout = \
wrap_stream(orig_stdout, convert, strip, autoreset, wrap) wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
if sys.stderr is None:
wrapped_stderr = None
else:
sys.stderr = wrapped_stderr = \ sys.stderr = wrapped_stderr = \
wrap_stream(orig_stderr, convert, strip, autoreset, wrap) wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
@ -35,13 +49,26 @@ def init(autoreset=False, convert=None, strip=None, wrap=True):
def deinit(): def deinit():
if orig_stdout is not None:
sys.stdout = orig_stdout sys.stdout = orig_stdout
if orig_stderr is not None:
sys.stderr = orig_stderr sys.stderr = orig_stderr
@contextlib.contextmanager
def colorama_text(*args, **kwargs):
init(*args, **kwargs)
try:
yield
finally:
deinit()
def reinit(): def reinit():
if wrapped_stdout is not None:
sys.stdout = wrapped_stdout sys.stdout = wrapped_stdout
sys.stderr = wrapped_stdout if wrapped_stderr is not None:
sys.stderr = wrapped_stderr
def wrap_stream(stream, convert, strip, autoreset, wrap): def wrap_stream(stream, convert, strip, autoreset, wrap):

View File

@ -1,51 +1,30 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
# from winbase.h # from winbase.h
STDOUT = -11 STDOUT = -11
STDERR = -12 STDERR = -12
try: try:
from ctypes import windll import ctypes
except ImportError: from ctypes import LibraryLoader
windll = LibraryLoader(ctypes.WinDLL)
from ctypes import wintypes
except (AttributeError, ImportError):
windll = None windll = None
SetConsoleTextAttribute = lambda *_: None SetConsoleTextAttribute = lambda *_: None
winapi_test = lambda *_: None
else: else:
from ctypes import ( from ctypes import byref, Structure, c_char, POINTER
byref, Structure, c_char, c_short, c_uint32, c_ushort
)
handles = { COORD = wintypes._COORD
STDOUT: windll.kernel32.GetStdHandle(STDOUT),
STDERR: windll.kernel32.GetStdHandle(STDERR),
}
SHORT = c_short
WORD = c_ushort
DWORD = c_uint32
TCHAR = c_char
class COORD(Structure):
"""struct in wincon.h"""
_fields_ = [
('X', SHORT),
('Y', SHORT),
]
class SMALL_RECT(Structure):
"""struct in wincon.h."""
_fields_ = [
("Left", SHORT),
("Top", SHORT),
("Right", SHORT),
("Bottom", SHORT),
]
class CONSOLE_SCREEN_BUFFER_INFO(Structure): class CONSOLE_SCREEN_BUFFER_INFO(Structure):
"""struct in wincon.h.""" """struct in wincon.h."""
_fields_ = [ _fields_ = [
("dwSize", COORD), ("dwSize", COORD),
("dwCursorPosition", COORD), ("dwCursorPosition", COORD),
("wAttributes", WORD), ("wAttributes", wintypes.WORD),
("srWindow", SMALL_RECT), ("srWindow", wintypes.SMALL_RECT),
("dwMaximumWindowSize", COORD), ("dwMaximumWindowSize", COORD),
] ]
def __str__(self): def __str__(self):
@ -57,20 +36,83 @@ else:
, self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X
) )
_GetStdHandle = windll.kernel32.GetStdHandle
_GetStdHandle.argtypes = [
wintypes.DWORD,
]
_GetStdHandle.restype = wintypes.HANDLE
_GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
_GetConsoleScreenBufferInfo.argtypes = [
wintypes.HANDLE,
POINTER(CONSOLE_SCREEN_BUFFER_INFO),
]
_GetConsoleScreenBufferInfo.restype = wintypes.BOOL
_SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
_SetConsoleTextAttribute.argtypes = [
wintypes.HANDLE,
wintypes.WORD,
]
_SetConsoleTextAttribute.restype = wintypes.BOOL
_SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
_SetConsoleCursorPosition.argtypes = [
wintypes.HANDLE,
COORD,
]
_SetConsoleCursorPosition.restype = wintypes.BOOL
_FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA
_FillConsoleOutputCharacterA.argtypes = [
wintypes.HANDLE,
c_char,
wintypes.DWORD,
COORD,
POINTER(wintypes.DWORD),
]
_FillConsoleOutputCharacterA.restype = wintypes.BOOL
_FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute
_FillConsoleOutputAttribute.argtypes = [
wintypes.HANDLE,
wintypes.WORD,
wintypes.DWORD,
COORD,
POINTER(wintypes.DWORD),
]
_FillConsoleOutputAttribute.restype = wintypes.BOOL
_SetConsoleTitleW = windll.kernel32.SetConsoleTitleA
_SetConsoleTitleW.argtypes = [
wintypes.LPCSTR
]
_SetConsoleTitleW.restype = wintypes.BOOL
handles = {
STDOUT: _GetStdHandle(STDOUT),
STDERR: _GetStdHandle(STDERR),
}
def winapi_test():
handle = handles[STDOUT]
csbi = CONSOLE_SCREEN_BUFFER_INFO()
success = _GetConsoleScreenBufferInfo(
handle, byref(csbi))
return bool(success)
def GetConsoleScreenBufferInfo(stream_id=STDOUT): def GetConsoleScreenBufferInfo(stream_id=STDOUT):
handle = handles[stream_id] handle = handles[stream_id]
csbi = CONSOLE_SCREEN_BUFFER_INFO() csbi = CONSOLE_SCREEN_BUFFER_INFO()
success = windll.kernel32.GetConsoleScreenBufferInfo( success = _GetConsoleScreenBufferInfo(
handle, byref(csbi)) handle, byref(csbi))
return csbi return csbi
def SetConsoleTextAttribute(stream_id, attrs): def SetConsoleTextAttribute(stream_id, attrs):
handle = handles[stream_id] handle = handles[stream_id]
return windll.kernel32.SetConsoleTextAttribute(handle, attrs) return _SetConsoleTextAttribute(handle, attrs)
def SetConsoleCursorPosition(stream_id, position, adjust=True):
def SetConsoleCursorPosition(stream_id, position):
position = COORD(*position) position = COORD(*position)
# If the position is out of range, do nothing. # If the position is out of range, do nothing.
if position.Y <= 0 or position.X <= 0: if position.Y <= 0 or position.X <= 0:
@ -79,31 +121,34 @@ else:
# 1. being 0-based, while ANSI is 1-based. # 1. being 0-based, while ANSI is 1-based.
# 2. expecting (x,y), while ANSI uses (y,x). # 2. expecting (x,y), while ANSI uses (y,x).
adjusted_position = COORD(position.Y - 1, position.X - 1) adjusted_position = COORD(position.Y - 1, position.X - 1)
if adjust:
# Adjust for viewport's scroll position # Adjust for viewport's scroll position
sr = GetConsoleScreenBufferInfo(STDOUT).srWindow sr = GetConsoleScreenBufferInfo(STDOUT).srWindow
adjusted_position.Y += sr.Top adjusted_position.Y += sr.Top
adjusted_position.X += sr.Left adjusted_position.X += sr.Left
# Resume normal processing # Resume normal processing
handle = handles[stream_id] handle = handles[stream_id]
return windll.kernel32.SetConsoleCursorPosition(handle, adjusted_position) return _SetConsoleCursorPosition(handle, adjusted_position)
def FillConsoleOutputCharacter(stream_id, char, length, start): def FillConsoleOutputCharacter(stream_id, char, length, start):
handle = handles[stream_id] handle = handles[stream_id]
char = TCHAR(char) char = c_char(char.encode())
length = DWORD(length) length = wintypes.DWORD(length)
num_written = DWORD(0) num_written = wintypes.DWORD(0)
# Note that this is hard-coded for ANSI (vs wide) bytes. # Note that this is hard-coded for ANSI (vs wide) bytes.
success = windll.kernel32.FillConsoleOutputCharacterA( success = _FillConsoleOutputCharacterA(
handle, char, length, start, byref(num_written)) handle, char, length, start, byref(num_written))
return num_written.value return num_written.value
def FillConsoleOutputAttribute(stream_id, attr, length, start): def FillConsoleOutputAttribute(stream_id, attr, length, start):
''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )'''
handle = handles[stream_id] handle = handles[stream_id]
attribute = WORD(attr) attribute = wintypes.WORD(attr)
length = DWORD(length) length = wintypes.DWORD(length)
num_written = DWORD(0) num_written = wintypes.DWORD(0)
# Note that this is hard-coded for ANSI (vs wide) bytes. # Note that this is hard-coded for ANSI (vs wide) bytes.
return windll.kernel32.FillConsoleOutputAttribute( return _FillConsoleOutputAttribute(
handle, attribute, length, start, byref(num_written)) handle, attribute, length, start, byref(num_written))
def SetConsoleTitle(title):
return _SetConsoleTitleW(title)

View File

@ -1,4 +1,4 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
from . import win32 from . import win32
@ -17,7 +17,7 @@ class WinColor(object):
class WinStyle(object): class WinStyle(object):
NORMAL = 0x00 # dim text, dim background NORMAL = 0x00 # dim text, dim background
BRIGHT = 0x08 # bright text, dim background BRIGHT = 0x08 # bright text, dim background
BRIGHT_BACKGROUND = 0x80 # dim text, bright background
class WinTerm(object): class WinTerm(object):
@ -27,29 +27,44 @@ class WinTerm(object):
self._default_fore = self._fore self._default_fore = self._fore
self._default_back = self._back self._default_back = self._back
self._default_style = self._style self._default_style = self._style
# In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style.
# So that LIGHT_EX colors and BRIGHT style do not clobber each other,
# we track them separately, since LIGHT_EX is overwritten by Fore/Back
# and BRIGHT is overwritten by Style codes.
self._light = 0
def get_attrs(self): def get_attrs(self):
return self._fore + self._back * 16 + self._style return self._fore + self._back * 16 + (self._style | self._light)
def set_attrs(self, value): def set_attrs(self, value):
self._fore = value & 7 self._fore = value & 7
self._back = (value >> 4) & 7 self._back = (value >> 4) & 7
self._style = value & WinStyle.BRIGHT self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND)
def reset_all(self, on_stderr=None): def reset_all(self, on_stderr=None):
self.set_attrs(self._default) self.set_attrs(self._default)
self.set_console(attrs=self._default) self.set_console(attrs=self._default)
def fore(self, fore=None, on_stderr=False): def fore(self, fore=None, light=False, on_stderr=False):
if fore is None: if fore is None:
fore = self._default_fore fore = self._default_fore
self._fore = fore self._fore = fore
# Emulate LIGHT_EX with BRIGHT Style
if light:
self._light |= WinStyle.BRIGHT
else:
self._light &= ~WinStyle.BRIGHT
self.set_console(on_stderr=on_stderr) self.set_console(on_stderr=on_stderr)
def back(self, back=None, on_stderr=False): def back(self, back=None, light=False, on_stderr=False):
if back is None: if back is None:
back = self._default_back back = self._default_back
self._back = back self._back = back
# Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
if light:
self._light |= WinStyle.BRIGHT_BACKGROUND
else:
self._light &= ~WinStyle.BRIGHT_BACKGROUND
self.set_console(on_stderr=on_stderr) self.set_console(on_stderr=on_stderr)
def style(self, style=None, on_stderr=False): def style(self, style=None, on_stderr=False):
@ -84,37 +99,64 @@ class WinTerm(object):
handle = win32.STDERR handle = win32.STDERR
win32.SetConsoleCursorPosition(handle, position) win32.SetConsoleCursorPosition(handle, position)
def cursor_up(self, num_rows=0, on_stderr=False): def cursor_adjust(self, x, y, on_stderr=False):
if num_rows == 0:
return
handle = win32.STDOUT handle = win32.STDOUT
if on_stderr: if on_stderr:
handle = win32.STDERR handle = win32.STDERR
position = self.get_position(handle) position = self.get_position(handle)
adjusted_position = (position.Y - num_rows, position.X) adjusted_position = (position.Y + y, position.X + x)
self.set_cursor_position(adjusted_position, on_stderr) win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)
def erase_data(self, mode=0, on_stderr=False): def erase_screen(self, mode=0, on_stderr=False):
# 0 (or None) should clear from the cursor to the end of the screen. # 0 should clear from the cursor to the end of the screen.
# 1 should clear from the cursor to the beginning of the screen. # 1 should clear from the cursor to the beginning of the screen.
# 2 should clear the entire screen. (And maybe move cursor to (1,1)?) # 2 should clear the entire screen, and move cursor to (1,1)
#
# At the moment, I only support mode 2. From looking at the API, it
# should be possible to calculate a different number of bytes to clear,
# and to do so relative to the cursor position.
if mode[0] not in (2,):
return
handle = win32.STDOUT handle = win32.STDOUT
if on_stderr: if on_stderr:
handle = win32.STDERR handle = win32.STDERR
# here's where we'll home the cursor
coord_screen = win32.COORD(0,0)
csbi = win32.GetConsoleScreenBufferInfo(handle) csbi = win32.GetConsoleScreenBufferInfo(handle)
# get the number of character cells in the current buffer # get the number of character cells in the current buffer
dw_con_size = csbi.dwSize.X * csbi.dwSize.Y cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
# get number of character cells before current cursor position
cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X
if mode == 0:
from_coord = csbi.dwCursorPosition
cells_to_erase = cells_in_screen - cells_before_cursor
if mode == 1:
from_coord = win32.COORD(0, 0)
cells_to_erase = cells_before_cursor
elif mode == 2:
from_coord = win32.COORD(0, 0)
cells_to_erase = cells_in_screen
# fill the entire screen with blanks # fill the entire screen with blanks
win32.FillConsoleOutputCharacter(handle, ord(' '), dw_con_size, coord_screen) win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
# now set the buffer's attributes accordingly # now set the buffer's attributes accordingly
win32.FillConsoleOutputAttribute(handle, self.get_attrs(), dw_con_size, coord_screen ); win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
# put the cursor at (0, 0) if mode == 2:
win32.SetConsoleCursorPosition(handle, (coord_screen.X, coord_screen.Y)) # put the cursor where needed
win32.SetConsoleCursorPosition(handle, (1, 1))
def erase_line(self, mode=0, on_stderr=False):
# 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)
if mode == 0:
from_coord = csbi.dwCursorPosition
cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
if mode == 1:
from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
cells_to_erase = csbi.dwCursorPosition.X
elif mode == 2:
from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
cells_to_erase = csbi.dwSize.X
# fill the entire screen with blanks
win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
# now set the buffer's attributes accordingly
win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
def set_title(self, title):
win32.SetConsoleTitle(title)