1 # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
3 from msvcrt
import get_osfhandle
6 raise OSError("This isn't windows!")
12 class WinColor(object):
23 class WinStyle(object):
24 NORMAL
= 0x00 # dim text, dim background
25 BRIGHT
= 0x08 # bright text, dim background
26 BRIGHT_BACKGROUND
= 0x80 # dim text, bright background
28 class WinTerm(object):
31 self
._default
= win32
.GetConsoleScreenBufferInfo(win32
.STDOUT
).wAttributes
32 self
.set_attrs(self
._default
)
33 self
._default
_fore
= self
._fore
34 self
._default
_back
= self
._back
35 self
._default
_style
= self
._style
36 # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style.
37 # So that LIGHT_EX colors and BRIGHT style do not clobber each other,
38 # we track them separately, since LIGHT_EX is overwritten by Fore/Back
39 # and BRIGHT is overwritten by Style codes.
43 return self
._fore
+ self
._back
* 16 + (self
._style | self
._light
)
45 def set_attrs(self
, value
):
46 self
._fore
= value
& 7
47 self
._back
= (value
>> 4) & 7
48 self
._style
= value
& (WinStyle
.BRIGHT | WinStyle
.BRIGHT_BACKGROUND
)
50 def reset_all(self
, on_stderr
=None):
51 self
.set_attrs(self
._default
)
52 self
.set_console(attrs
=self
._default
)
55 def fore(self
, fore
=None, light
=False, on_stderr
=False):
57 fore
= self
._default
_fore
59 # Emulate LIGHT_EX with BRIGHT Style
61 self
._light |
= WinStyle
.BRIGHT
63 self
._light
&= ~WinStyle
.BRIGHT
64 self
.set_console(on_stderr
=on_stderr
)
66 def back(self
, back
=None, light
=False, on_stderr
=False):
68 back
= self
._default
_back
70 # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
72 self
._light |
= WinStyle
.BRIGHT_BACKGROUND
74 self
._light
&= ~WinStyle
.BRIGHT_BACKGROUND
75 self
.set_console(on_stderr
=on_stderr
)
77 def style(self
, style
=None, on_stderr
=False):
79 style
= self
._default
_style
81 self
.set_console(on_stderr
=on_stderr
)
83 def set_console(self
, attrs
=None, on_stderr
=False):
85 attrs
= self
.get_attrs()
89 win32
.SetConsoleTextAttribute(handle
, attrs
)
91 def get_position(self
, handle
):
92 position
= win32
.GetConsoleScreenBufferInfo(handle
).dwCursorPosition
93 # Because Windows coordinates are 0-based,
94 # and win32.SetConsoleCursorPosition expects 1-based.
99 def set_cursor_position(self
, position
=None, on_stderr
=False):
101 # I'm not currently tracking the position, so there is no default.
102 # position = self.get_position()
104 handle
= win32
.STDOUT
106 handle
= win32
.STDERR
107 win32
.SetConsoleCursorPosition(handle
, position
)
109 def cursor_adjust(self
, x
, y
, on_stderr
=False):
110 handle
= win32
.STDOUT
112 handle
= win32
.STDERR
113 position
= self
.get_position(handle
)
114 adjusted_position
= (position
.Y
+ y
, position
.X
+ x
)
115 win32
.SetConsoleCursorPosition(handle
, adjusted_position
, adjust
=False)
117 def erase_screen(self
, mode
=0, on_stderr
=False):
118 # 0 should clear from the cursor to the end of the screen.
119 # 1 should clear from the cursor to the beginning of the screen.
120 # 2 should clear the entire screen, and move cursor to (1,1)
121 handle
= win32
.STDOUT
123 handle
= win32
.STDERR
124 csbi
= win32
.GetConsoleScreenBufferInfo(handle
)
125 # get the number of character cells in the current buffer
126 cells_in_screen
= csbi
.dwSize
.X
* csbi
.dwSize
.Y
127 # get number of character cells before current cursor position
128 cells_before_cursor
= csbi
.dwSize
.X
* csbi
.dwCursorPosition
.Y
+ csbi
.dwCursorPosition
.X
130 from_coord
= csbi
.dwCursorPosition
131 cells_to_erase
= cells_in_screen
- cells_before_cursor
133 from_coord
= win32
.COORD(0, 0)
134 cells_to_erase
= cells_before_cursor
136 from_coord
= win32
.COORD(0, 0)
137 cells_to_erase
= cells_in_screen
141 # fill the entire screen with blanks
142 win32
.FillConsoleOutputCharacter(handle
, ' ', cells_to_erase
, from_coord
)
143 # now set the buffer's attributes accordingly
144 win32
.FillConsoleOutputAttribute(handle
, self
.get_attrs(), cells_to_erase
, from_coord
)
146 # put the cursor where needed
147 win32
.SetConsoleCursorPosition(handle
, (1, 1))
149 def erase_line(self
, mode
=0, on_stderr
=False):
150 # 0 should clear from the cursor to the end of the line.
151 # 1 should clear from the cursor to the beginning of the line.
152 # 2 should clear the entire line.
153 handle
= win32
.STDOUT
155 handle
= win32
.STDERR
156 csbi
= win32
.GetConsoleScreenBufferInfo(handle
)
158 from_coord
= csbi
.dwCursorPosition
159 cells_to_erase
= csbi
.dwSize
.X
- csbi
.dwCursorPosition
.X
161 from_coord
= win32
.COORD(0, csbi
.dwCursorPosition
.Y
)
162 cells_to_erase
= csbi
.dwCursorPosition
.X
164 from_coord
= win32
.COORD(0, csbi
.dwCursorPosition
.Y
)
165 cells_to_erase
= csbi
.dwSize
.X
169 # fill the entire screen with blanks
170 win32
.FillConsoleOutputCharacter(handle
, ' ', cells_to_erase
, from_coord
)
171 # now set the buffer's attributes accordingly
172 win32
.FillConsoleOutputAttribute(handle
, self
.get_attrs(), cells_to_erase
, from_coord
)
174 def set_title(self
, title
):
175 win32
.SetConsoleTitle(title
)
178 def enable_vt_processing(fd
):
179 if win32
.windll
is None or not win32
.winapi_test():
183 handle
= get_osfhandle(fd
)
184 mode
= win32
.GetConsoleMode(handle
)
185 win32
.SetConsoleMode(
187 mode | win32
.ENABLE_VIRTUAL_TERMINAL_PROCESSING
,
190 mode
= win32
.GetConsoleMode(handle
)
191 if mode
& win32
.ENABLE_VIRTUAL_TERMINAL_PROCESSING
:
193 # Can get TypeError in testsuite where 'fd' is a Mock()
194 except (OSError, TypeError):