mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Windows console output: Handle consoles with non black and white default colors correctly
This commit is contained in:
parent
8211335683
commit
a4ddfc4a58
@ -12,6 +12,17 @@ from itertools import izip
|
||||
|
||||
from calibre.constants import iswindows
|
||||
|
||||
if iswindows:
|
||||
import ctypes.wintypes
|
||||
class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('dwSize', ctypes.wintypes._COORD),
|
||||
('dwCursorPosition', ctypes.wintypes._COORD),
|
||||
('wAttributes', ctypes.wintypes.WORD),
|
||||
('srWindow', ctypes.wintypes._SMALL_RECT),
|
||||
('dwMaximumWindowSize', ctypes.wintypes._COORD)
|
||||
]
|
||||
|
||||
def fmt(code):
|
||||
return ('\033[%dm'%code).encode('ascii')
|
||||
|
||||
@ -111,9 +122,16 @@ class Detect(object):
|
||||
if f(self.file_handle, byref(mode)):
|
||||
# Stream is a console
|
||||
self.set_console = windll.kernel32.SetConsoleTextAttribute
|
||||
self.write_console = WinDLL('kernel32', use_last_error=True).WriteConsoleW
|
||||
kernel32 = WinDLL('kernel32', use_last_error=True)
|
||||
self.write_console = kernel32.WriteConsoleW
|
||||
self.write_console.argtypes = [wintypes.HANDLE, wintypes.c_wchar_p, wintypes.DWORD, POINTER(wintypes.DWORD), wintypes.LPVOID]
|
||||
self.write_console.restype = wintypes.BOOL
|
||||
kernel32.GetConsoleScreenBufferInfo.argtypes = [wintypes.HANDLE, ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO)]
|
||||
kernel32.GetConsoleScreenBufferInfo.restype = wintypes.BOOL
|
||||
csbi = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
self.default_console_text_attributes = WCOLORS['white']
|
||||
if kernel32.GetConsoleScreenBufferInfo(self.file_handle, byref(csbi)):
|
||||
self.default_console_text_attributes = csbi.wAttributes
|
||||
self.is_console = True
|
||||
except:
|
||||
pass
|
||||
@ -131,7 +149,7 @@ class Detect(object):
|
||||
if not self.write_console(self.file_handle, wt, len(t), byref(written), None):
|
||||
# Older versions of windows can fail to write large strings
|
||||
# to console with WriteConsoleW (seen it happen on Win XP)
|
||||
import ctypes, winerror
|
||||
import winerror
|
||||
err = ctypes.get_last_error()
|
||||
if err == winerror.ERROR_NOT_ENOUGH_MEMORY and chunk >= 128:
|
||||
# Retry with a smaller chunk size (give up if chunk < 128)
|
||||
@ -159,6 +177,8 @@ class ColoredStream(Detect):
|
||||
self.fg, self.bg, self.bold = fg, bg, bold
|
||||
if self.set_console is not None:
|
||||
self.wval = to_flag(self.fg, self.bg, bold)
|
||||
if not self.bg:
|
||||
self.wval |= self.default_console_text_attributes & 0xF0
|
||||
|
||||
def __enter__(self):
|
||||
if not self.isatty:
|
||||
@ -183,7 +203,7 @@ class ColoredStream(Detect):
|
||||
if self.isansi:
|
||||
self.stream.write(RESET)
|
||||
elif self.set_console is not None:
|
||||
self.set_console(self.file_handle, WCOLORS['white'])
|
||||
self.set_console(self.file_handle, self.default_console_text_attributes)
|
||||
|
||||
class ANSIStream(Detect):
|
||||
|
||||
@ -192,6 +212,7 @@ class ANSIStream(Detect):
|
||||
def __init__(self, stream=None):
|
||||
super(ANSIStream, self).__init__(stream)
|
||||
self.encoding = getattr(self.stream, 'encoding', 'utf-8') or 'utf-8'
|
||||
self.last_state = (None, None, False)
|
||||
|
||||
def write(self, text):
|
||||
if isinstance(text, type(u'')):
|
||||
@ -217,7 +238,6 @@ class ANSIStream(Detect):
|
||||
sequences from the text, and optionally converting them into win32
|
||||
calls.
|
||||
'''
|
||||
self.last_state = (None, None, False)
|
||||
cursor = 0
|
||||
for match in self.ANSI_RE.finditer(text):
|
||||
start, end = match.span()
|
||||
@ -225,6 +245,7 @@ class ANSIStream(Detect):
|
||||
self.convert_ansi(*match.groups())
|
||||
cursor = end
|
||||
self.write_plain_text(text, cursor, len(text))
|
||||
self.set_console(self.file_handle, self.default_console_text_attributes)
|
||||
self.stream.flush()
|
||||
|
||||
def write_plain_text(self, text, start, end):
|
||||
@ -263,14 +284,16 @@ class ANSIStream(Detect):
|
||||
elif param == 1:
|
||||
bold = True
|
||||
elif param == 0:
|
||||
fg = 'white'
|
||||
bg, bold = None, False
|
||||
fg, bg, bold = None, None, False
|
||||
|
||||
self.last_state = (fg, bg, bold)
|
||||
if fg or bg or bold:
|
||||
self.set_console(self.file_handle, to_flag(fg, bg, bold))
|
||||
val = to_flag(fg, bg, bold)
|
||||
if not bg:
|
||||
val |= self.default_console_text_attributes & 0xF0
|
||||
self.set_console(self.file_handle, val)
|
||||
else:
|
||||
self.set_console(self.file_handle, WCOLORS['white'])
|
||||
self.set_console(self.file_handle, self.default_console_text_attributes)
|
||||
|
||||
def windows_terminfo():
|
||||
from ctypes import Structure, byref
|
||||
|
Loading…
x
Reference in New Issue
Block a user