170 else: |
172 else: |
171 start = ''.join(_effect_str(effect) |
173 start = ''.join(_effect_str(effect) |
172 for effect in ['none'] + effects.split()) |
174 for effect in ['none'] + effects.split()) |
173 stop = _effect_str('none') |
175 stop = _effect_str('none') |
174 return ''.join([start, text, stop]) |
176 return ''.join([start, text, stop]) |
|
177 |
|
178 w32effects = None |
|
179 if pycompat.osname == 'nt': |
|
180 import ctypes |
|
181 import re |
|
182 |
|
183 _kernel32 = ctypes.windll.kernel32 |
|
184 |
|
185 _WORD = ctypes.c_ushort |
|
186 |
|
187 _INVALID_HANDLE_VALUE = -1 |
|
188 |
|
189 class _COORD(ctypes.Structure): |
|
190 _fields_ = [('X', ctypes.c_short), |
|
191 ('Y', ctypes.c_short)] |
|
192 |
|
193 class _SMALL_RECT(ctypes.Structure): |
|
194 _fields_ = [('Left', ctypes.c_short), |
|
195 ('Top', ctypes.c_short), |
|
196 ('Right', ctypes.c_short), |
|
197 ('Bottom', ctypes.c_short)] |
|
198 |
|
199 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): |
|
200 _fields_ = [('dwSize', _COORD), |
|
201 ('dwCursorPosition', _COORD), |
|
202 ('wAttributes', _WORD), |
|
203 ('srWindow', _SMALL_RECT), |
|
204 ('dwMaximumWindowSize', _COORD)] |
|
205 |
|
206 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11 |
|
207 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12 |
|
208 |
|
209 _FOREGROUND_BLUE = 0x0001 |
|
210 _FOREGROUND_GREEN = 0x0002 |
|
211 _FOREGROUND_RED = 0x0004 |
|
212 _FOREGROUND_INTENSITY = 0x0008 |
|
213 |
|
214 _BACKGROUND_BLUE = 0x0010 |
|
215 _BACKGROUND_GREEN = 0x0020 |
|
216 _BACKGROUND_RED = 0x0040 |
|
217 _BACKGROUND_INTENSITY = 0x0080 |
|
218 |
|
219 _COMMON_LVB_REVERSE_VIDEO = 0x4000 |
|
220 _COMMON_LVB_UNDERSCORE = 0x8000 |
|
221 |
|
222 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx |
|
223 w32effects = { |
|
224 'none': -1, |
|
225 'black': 0, |
|
226 'red': _FOREGROUND_RED, |
|
227 'green': _FOREGROUND_GREEN, |
|
228 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN, |
|
229 'blue': _FOREGROUND_BLUE, |
|
230 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED, |
|
231 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN, |
|
232 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE, |
|
233 'bold': _FOREGROUND_INTENSITY, |
|
234 'black_background': 0x100, # unused value > 0x0f |
|
235 'red_background': _BACKGROUND_RED, |
|
236 'green_background': _BACKGROUND_GREEN, |
|
237 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN, |
|
238 'blue_background': _BACKGROUND_BLUE, |
|
239 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED, |
|
240 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN, |
|
241 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN | |
|
242 _BACKGROUND_BLUE), |
|
243 'bold_background': _BACKGROUND_INTENSITY, |
|
244 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only |
|
245 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only |
|
246 } |
|
247 |
|
248 passthrough = set([_FOREGROUND_INTENSITY, |
|
249 _BACKGROUND_INTENSITY, |
|
250 _COMMON_LVB_UNDERSCORE, |
|
251 _COMMON_LVB_REVERSE_VIDEO]) |
|
252 |
|
253 stdout = _kernel32.GetStdHandle( |
|
254 _STD_OUTPUT_HANDLE) # don't close the handle returned |
|
255 if stdout is None or stdout == _INVALID_HANDLE_VALUE: |
|
256 w32effects = None |
|
257 else: |
|
258 csbi = _CONSOLE_SCREEN_BUFFER_INFO() |
|
259 if not _kernel32.GetConsoleScreenBufferInfo( |
|
260 stdout, ctypes.byref(csbi)): |
|
261 # stdout may not support GetConsoleScreenBufferInfo() |
|
262 # when called from subprocess or redirected |
|
263 w32effects = None |
|
264 else: |
|
265 origattr = csbi.wAttributes |
|
266 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)', |
|
267 re.MULTILINE | re.DOTALL) |
|
268 |
|
269 def win32print(text, orig, **opts): |
|
270 label = opts.get('label', '') |
|
271 attr = origattr |
|
272 |
|
273 def mapcolor(val, attr): |
|
274 if val == -1: |
|
275 return origattr |
|
276 elif val in passthrough: |
|
277 return attr | val |
|
278 elif val > 0x0f: |
|
279 return (val & 0x70) | (attr & 0x8f) |
|
280 else: |
|
281 return (val & 0x07) | (attr & 0xf8) |
|
282 |
|
283 # determine console attributes based on labels |
|
284 for l in label.split(): |
|
285 style = _styles.get(l, '') |
|
286 for effect in style.split(): |
|
287 try: |
|
288 attr = mapcolor(w32effects[effect], attr) |
|
289 except KeyError: |
|
290 # w32effects could not have certain attributes so we skip |
|
291 # them if not found |
|
292 pass |
|
293 # hack to ensure regexp finds data |
|
294 if not text.startswith('\033['): |
|
295 text = '\033[m' + text |
|
296 |
|
297 # Look for ANSI-like codes embedded in text |
|
298 m = re.match(ansire, text) |
|
299 |
|
300 try: |
|
301 while m: |
|
302 for sattr in m.group(1).split(';'): |
|
303 if sattr: |
|
304 attr = mapcolor(int(sattr), attr) |
|
305 _kernel32.SetConsoleTextAttribute(stdout, attr) |
|
306 orig(m.group(2), **opts) |
|
307 m = re.match(ansire, m.group(3)) |
|
308 finally: |
|
309 # Explicitly reset original attributes |
|
310 _kernel32.SetConsoleTextAttribute(stdout, origattr) |