##// END OF EJS Templates
color: clarify name of an argument of 'win32print'...
Pierre-Yves David -
r31088:75c4aafe default
parent child Browse files
Show More
@@ -1,332 +1,332 b''
1 # utility for color output for Mercurial commands
1 # utility for color output for Mercurial commands
2 #
2 #
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com> and other
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com> and other
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from .i18n import _
10 from .i18n import _
11
11
12 from . import pycompat
12 from . import pycompat
13
13
14 try:
14 try:
15 import curses
15 import curses
16 # Mapping from effect name to terminfo attribute name (or raw code) or
16 # Mapping from effect name to terminfo attribute name (or raw code) or
17 # color number. This will also force-load the curses module.
17 # color number. This will also force-load the curses module.
18 _terminfo_params = {'none': (True, 'sgr0', ''),
18 _terminfo_params = {'none': (True, 'sgr0', ''),
19 'standout': (True, 'smso', ''),
19 'standout': (True, 'smso', ''),
20 'underline': (True, 'smul', ''),
20 'underline': (True, 'smul', ''),
21 'reverse': (True, 'rev', ''),
21 'reverse': (True, 'rev', ''),
22 'inverse': (True, 'rev', ''),
22 'inverse': (True, 'rev', ''),
23 'blink': (True, 'blink', ''),
23 'blink': (True, 'blink', ''),
24 'dim': (True, 'dim', ''),
24 'dim': (True, 'dim', ''),
25 'bold': (True, 'bold', ''),
25 'bold': (True, 'bold', ''),
26 'invisible': (True, 'invis', ''),
26 'invisible': (True, 'invis', ''),
27 'italic': (True, 'sitm', ''),
27 'italic': (True, 'sitm', ''),
28 'black': (False, curses.COLOR_BLACK, ''),
28 'black': (False, curses.COLOR_BLACK, ''),
29 'red': (False, curses.COLOR_RED, ''),
29 'red': (False, curses.COLOR_RED, ''),
30 'green': (False, curses.COLOR_GREEN, ''),
30 'green': (False, curses.COLOR_GREEN, ''),
31 'yellow': (False, curses.COLOR_YELLOW, ''),
31 'yellow': (False, curses.COLOR_YELLOW, ''),
32 'blue': (False, curses.COLOR_BLUE, ''),
32 'blue': (False, curses.COLOR_BLUE, ''),
33 'magenta': (False, curses.COLOR_MAGENTA, ''),
33 'magenta': (False, curses.COLOR_MAGENTA, ''),
34 'cyan': (False, curses.COLOR_CYAN, ''),
34 'cyan': (False, curses.COLOR_CYAN, ''),
35 'white': (False, curses.COLOR_WHITE, '')}
35 'white': (False, curses.COLOR_WHITE, '')}
36 except ImportError:
36 except ImportError:
37 curses = None
37 curses = None
38 _terminfo_params = {}
38 _terminfo_params = {}
39
39
40 # start and stop parameters for effects
40 # start and stop parameters for effects
41 _effects = {'none': 0,
41 _effects = {'none': 0,
42 'black': 30,
42 'black': 30,
43 'red': 31,
43 'red': 31,
44 'green': 32,
44 'green': 32,
45 'yellow': 33,
45 'yellow': 33,
46 'blue': 34,
46 'blue': 34,
47 'magenta': 35,
47 'magenta': 35,
48 'cyan': 36,
48 'cyan': 36,
49 'white': 37,
49 'white': 37,
50 'bold': 1,
50 'bold': 1,
51 'italic': 3,
51 'italic': 3,
52 'underline': 4,
52 'underline': 4,
53 'inverse': 7,
53 'inverse': 7,
54 'dim': 2,
54 'dim': 2,
55 'black_background': 40,
55 'black_background': 40,
56 'red_background': 41,
56 'red_background': 41,
57 'green_background': 42,
57 'green_background': 42,
58 'yellow_background': 43,
58 'yellow_background': 43,
59 'blue_background': 44,
59 'blue_background': 44,
60 'purple_background': 45,
60 'purple_background': 45,
61 'cyan_background': 46,
61 'cyan_background': 46,
62 'white_background': 47}
62 'white_background': 47}
63
63
64 _styles = {'grep.match': 'red bold',
64 _styles = {'grep.match': 'red bold',
65 'grep.linenumber': 'green',
65 'grep.linenumber': 'green',
66 'grep.rev': 'green',
66 'grep.rev': 'green',
67 'grep.change': 'green',
67 'grep.change': 'green',
68 'grep.sep': 'cyan',
68 'grep.sep': 'cyan',
69 'grep.filename': 'magenta',
69 'grep.filename': 'magenta',
70 'grep.user': 'magenta',
70 'grep.user': 'magenta',
71 'grep.date': 'magenta',
71 'grep.date': 'magenta',
72 'bookmarks.active': 'green',
72 'bookmarks.active': 'green',
73 'branches.active': 'none',
73 'branches.active': 'none',
74 'branches.closed': 'black bold',
74 'branches.closed': 'black bold',
75 'branches.current': 'green',
75 'branches.current': 'green',
76 'branches.inactive': 'none',
76 'branches.inactive': 'none',
77 'diff.changed': 'white',
77 'diff.changed': 'white',
78 'diff.deleted': 'red',
78 'diff.deleted': 'red',
79 'diff.diffline': 'bold',
79 'diff.diffline': 'bold',
80 'diff.extended': 'cyan bold',
80 'diff.extended': 'cyan bold',
81 'diff.file_a': 'red bold',
81 'diff.file_a': 'red bold',
82 'diff.file_b': 'green bold',
82 'diff.file_b': 'green bold',
83 'diff.hunk': 'magenta',
83 'diff.hunk': 'magenta',
84 'diff.inserted': 'green',
84 'diff.inserted': 'green',
85 'diff.tab': '',
85 'diff.tab': '',
86 'diff.trailingwhitespace': 'bold red_background',
86 'diff.trailingwhitespace': 'bold red_background',
87 'changeset.public' : '',
87 'changeset.public' : '',
88 'changeset.draft' : '',
88 'changeset.draft' : '',
89 'changeset.secret' : '',
89 'changeset.secret' : '',
90 'diffstat.deleted': 'red',
90 'diffstat.deleted': 'red',
91 'diffstat.inserted': 'green',
91 'diffstat.inserted': 'green',
92 'histedit.remaining': 'red bold',
92 'histedit.remaining': 'red bold',
93 'ui.prompt': 'yellow',
93 'ui.prompt': 'yellow',
94 'log.changeset': 'yellow',
94 'log.changeset': 'yellow',
95 'patchbomb.finalsummary': '',
95 'patchbomb.finalsummary': '',
96 'patchbomb.from': 'magenta',
96 'patchbomb.from': 'magenta',
97 'patchbomb.to': 'cyan',
97 'patchbomb.to': 'cyan',
98 'patchbomb.subject': 'green',
98 'patchbomb.subject': 'green',
99 'patchbomb.diffstats': '',
99 'patchbomb.diffstats': '',
100 'rebase.rebased': 'blue',
100 'rebase.rebased': 'blue',
101 'rebase.remaining': 'red bold',
101 'rebase.remaining': 'red bold',
102 'resolve.resolved': 'green bold',
102 'resolve.resolved': 'green bold',
103 'resolve.unresolved': 'red bold',
103 'resolve.unresolved': 'red bold',
104 'shelve.age': 'cyan',
104 'shelve.age': 'cyan',
105 'shelve.newest': 'green bold',
105 'shelve.newest': 'green bold',
106 'shelve.name': 'blue bold',
106 'shelve.name': 'blue bold',
107 'status.added': 'green bold',
107 'status.added': 'green bold',
108 'status.clean': 'none',
108 'status.clean': 'none',
109 'status.copied': 'none',
109 'status.copied': 'none',
110 'status.deleted': 'cyan bold underline',
110 'status.deleted': 'cyan bold underline',
111 'status.ignored': 'black bold',
111 'status.ignored': 'black bold',
112 'status.modified': 'blue bold',
112 'status.modified': 'blue bold',
113 'status.removed': 'red bold',
113 'status.removed': 'red bold',
114 'status.unknown': 'magenta bold underline',
114 'status.unknown': 'magenta bold underline',
115 'tags.normal': 'green',
115 'tags.normal': 'green',
116 'tags.local': 'black bold'}
116 'tags.local': 'black bold'}
117
117
118 def loadcolortable(ui, extname, colortable):
118 def loadcolortable(ui, extname, colortable):
119 _styles.update(colortable)
119 _styles.update(colortable)
120
120
121 def configstyles(ui):
121 def configstyles(ui):
122 for status, cfgeffects in ui.configitems('color'):
122 for status, cfgeffects in ui.configitems('color'):
123 if '.' not in status or status.startswith(('color.', 'terminfo.')):
123 if '.' not in status or status.startswith(('color.', 'terminfo.')):
124 continue
124 continue
125 cfgeffects = ui.configlist('color', status)
125 cfgeffects = ui.configlist('color', status)
126 if cfgeffects:
126 if cfgeffects:
127 good = []
127 good = []
128 for e in cfgeffects:
128 for e in cfgeffects:
129 if valideffect(e):
129 if valideffect(e):
130 good.append(e)
130 good.append(e)
131 else:
131 else:
132 ui.warn(_("ignoring unknown color/effect %r "
132 ui.warn(_("ignoring unknown color/effect %r "
133 "(configured in color.%s)\n")
133 "(configured in color.%s)\n")
134 % (e, status))
134 % (e, status))
135 _styles[status] = ' '.join(good)
135 _styles[status] = ' '.join(good)
136
136
137 def valideffect(effect):
137 def valideffect(effect):
138 'Determine if the effect is valid or not.'
138 'Determine if the effect is valid or not.'
139 return ((not _terminfo_params and effect in _effects)
139 return ((not _terminfo_params and effect in _effects)
140 or (effect in _terminfo_params
140 or (effect in _terminfo_params
141 or effect[:-11] in _terminfo_params))
141 or effect[:-11] in _terminfo_params))
142
142
143 def _effect_str(effect):
143 def _effect_str(effect):
144 '''Helper function for render_effects().'''
144 '''Helper function for render_effects().'''
145
145
146 bg = False
146 bg = False
147 if effect.endswith('_background'):
147 if effect.endswith('_background'):
148 bg = True
148 bg = True
149 effect = effect[:-11]
149 effect = effect[:-11]
150 try:
150 try:
151 attr, val, termcode = _terminfo_params[effect]
151 attr, val, termcode = _terminfo_params[effect]
152 except KeyError:
152 except KeyError:
153 return ''
153 return ''
154 if attr:
154 if attr:
155 if termcode:
155 if termcode:
156 return termcode
156 return termcode
157 else:
157 else:
158 return curses.tigetstr(val)
158 return curses.tigetstr(val)
159 elif bg:
159 elif bg:
160 return curses.tparm(curses.tigetstr('setab'), val)
160 return curses.tparm(curses.tigetstr('setab'), val)
161 else:
161 else:
162 return curses.tparm(curses.tigetstr('setaf'), val)
162 return curses.tparm(curses.tigetstr('setaf'), val)
163
163
164 def _render_effects(text, effects):
164 def _render_effects(text, effects):
165 'Wrap text in commands to turn on each effect.'
165 'Wrap text in commands to turn on each effect.'
166 if not text:
166 if not text:
167 return text
167 return text
168 if _terminfo_params:
168 if _terminfo_params:
169 start = ''.join(_effect_str(effect)
169 start = ''.join(_effect_str(effect)
170 for effect in ['none'] + effects.split())
170 for effect in ['none'] + effects.split())
171 stop = _effect_str('none')
171 stop = _effect_str('none')
172 else:
172 else:
173 start = [str(_effects[e]) for e in ['none'] + effects.split()]
173 start = [str(_effects[e]) for e in ['none'] + effects.split()]
174 start = '\033[' + ';'.join(start) + 'm'
174 start = '\033[' + ';'.join(start) + 'm'
175 stop = '\033[' + str(_effects['none']) + 'm'
175 stop = '\033[' + str(_effects['none']) + 'm'
176 return ''.join([start, text, stop])
176 return ''.join([start, text, stop])
177
177
178 def colorlabel(ui, msg, label):
178 def colorlabel(ui, msg, label):
179 """add color control code according to the mode"""
179 """add color control code according to the mode"""
180 if ui._colormode == 'debug':
180 if ui._colormode == 'debug':
181 if label and msg:
181 if label and msg:
182 if msg[-1] == '\n':
182 if msg[-1] == '\n':
183 msg = "[%s|%s]\n" % (label, msg[:-1])
183 msg = "[%s|%s]\n" % (label, msg[:-1])
184 else:
184 else:
185 msg = "[%s|%s]" % (label, msg)
185 msg = "[%s|%s]" % (label, msg)
186 elif ui._colormode is not None:
186 elif ui._colormode is not None:
187 effects = []
187 effects = []
188 for l in label.split():
188 for l in label.split():
189 s = _styles.get(l, '')
189 s = _styles.get(l, '')
190 if s:
190 if s:
191 effects.append(s)
191 effects.append(s)
192 elif valideffect(l):
192 elif valideffect(l):
193 effects.append(l)
193 effects.append(l)
194 effects = ' '.join(effects)
194 effects = ' '.join(effects)
195 if effects:
195 if effects:
196 msg = '\n'.join([_render_effects(line, effects)
196 msg = '\n'.join([_render_effects(line, effects)
197 for line in msg.split('\n')])
197 for line in msg.split('\n')])
198 return msg
198 return msg
199
199
200 w32effects = None
200 w32effects = None
201 if pycompat.osname == 'nt':
201 if pycompat.osname == 'nt':
202 import ctypes
202 import ctypes
203 import re
203 import re
204
204
205 _kernel32 = ctypes.windll.kernel32
205 _kernel32 = ctypes.windll.kernel32
206
206
207 _WORD = ctypes.c_ushort
207 _WORD = ctypes.c_ushort
208
208
209 _INVALID_HANDLE_VALUE = -1
209 _INVALID_HANDLE_VALUE = -1
210
210
211 class _COORD(ctypes.Structure):
211 class _COORD(ctypes.Structure):
212 _fields_ = [('X', ctypes.c_short),
212 _fields_ = [('X', ctypes.c_short),
213 ('Y', ctypes.c_short)]
213 ('Y', ctypes.c_short)]
214
214
215 class _SMALL_RECT(ctypes.Structure):
215 class _SMALL_RECT(ctypes.Structure):
216 _fields_ = [('Left', ctypes.c_short),
216 _fields_ = [('Left', ctypes.c_short),
217 ('Top', ctypes.c_short),
217 ('Top', ctypes.c_short),
218 ('Right', ctypes.c_short),
218 ('Right', ctypes.c_short),
219 ('Bottom', ctypes.c_short)]
219 ('Bottom', ctypes.c_short)]
220
220
221 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
221 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
222 _fields_ = [('dwSize', _COORD),
222 _fields_ = [('dwSize', _COORD),
223 ('dwCursorPosition', _COORD),
223 ('dwCursorPosition', _COORD),
224 ('wAttributes', _WORD),
224 ('wAttributes', _WORD),
225 ('srWindow', _SMALL_RECT),
225 ('srWindow', _SMALL_RECT),
226 ('dwMaximumWindowSize', _COORD)]
226 ('dwMaximumWindowSize', _COORD)]
227
227
228 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
228 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
229 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
229 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
230
230
231 _FOREGROUND_BLUE = 0x0001
231 _FOREGROUND_BLUE = 0x0001
232 _FOREGROUND_GREEN = 0x0002
232 _FOREGROUND_GREEN = 0x0002
233 _FOREGROUND_RED = 0x0004
233 _FOREGROUND_RED = 0x0004
234 _FOREGROUND_INTENSITY = 0x0008
234 _FOREGROUND_INTENSITY = 0x0008
235
235
236 _BACKGROUND_BLUE = 0x0010
236 _BACKGROUND_BLUE = 0x0010
237 _BACKGROUND_GREEN = 0x0020
237 _BACKGROUND_GREEN = 0x0020
238 _BACKGROUND_RED = 0x0040
238 _BACKGROUND_RED = 0x0040
239 _BACKGROUND_INTENSITY = 0x0080
239 _BACKGROUND_INTENSITY = 0x0080
240
240
241 _COMMON_LVB_REVERSE_VIDEO = 0x4000
241 _COMMON_LVB_REVERSE_VIDEO = 0x4000
242 _COMMON_LVB_UNDERSCORE = 0x8000
242 _COMMON_LVB_UNDERSCORE = 0x8000
243
243
244 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
244 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
245 w32effects = {
245 w32effects = {
246 'none': -1,
246 'none': -1,
247 'black': 0,
247 'black': 0,
248 'red': _FOREGROUND_RED,
248 'red': _FOREGROUND_RED,
249 'green': _FOREGROUND_GREEN,
249 'green': _FOREGROUND_GREEN,
250 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
250 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
251 'blue': _FOREGROUND_BLUE,
251 'blue': _FOREGROUND_BLUE,
252 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
252 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
253 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
253 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
254 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
254 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
255 'bold': _FOREGROUND_INTENSITY,
255 'bold': _FOREGROUND_INTENSITY,
256 'black_background': 0x100, # unused value > 0x0f
256 'black_background': 0x100, # unused value > 0x0f
257 'red_background': _BACKGROUND_RED,
257 'red_background': _BACKGROUND_RED,
258 'green_background': _BACKGROUND_GREEN,
258 'green_background': _BACKGROUND_GREEN,
259 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
259 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
260 'blue_background': _BACKGROUND_BLUE,
260 'blue_background': _BACKGROUND_BLUE,
261 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
261 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
262 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
262 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
263 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
263 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
264 _BACKGROUND_BLUE),
264 _BACKGROUND_BLUE),
265 'bold_background': _BACKGROUND_INTENSITY,
265 'bold_background': _BACKGROUND_INTENSITY,
266 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
266 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
267 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
267 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
268 }
268 }
269
269
270 passthrough = set([_FOREGROUND_INTENSITY,
270 passthrough = set([_FOREGROUND_INTENSITY,
271 _BACKGROUND_INTENSITY,
271 _BACKGROUND_INTENSITY,
272 _COMMON_LVB_UNDERSCORE,
272 _COMMON_LVB_UNDERSCORE,
273 _COMMON_LVB_REVERSE_VIDEO])
273 _COMMON_LVB_REVERSE_VIDEO])
274
274
275 stdout = _kernel32.GetStdHandle(
275 stdout = _kernel32.GetStdHandle(
276 _STD_OUTPUT_HANDLE) # don't close the handle returned
276 _STD_OUTPUT_HANDLE) # don't close the handle returned
277 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
277 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
278 w32effects = None
278 w32effects = None
279 else:
279 else:
280 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
280 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
281 if not _kernel32.GetConsoleScreenBufferInfo(
281 if not _kernel32.GetConsoleScreenBufferInfo(
282 stdout, ctypes.byref(csbi)):
282 stdout, ctypes.byref(csbi)):
283 # stdout may not support GetConsoleScreenBufferInfo()
283 # stdout may not support GetConsoleScreenBufferInfo()
284 # when called from subprocess or redirected
284 # when called from subprocess or redirected
285 w32effects = None
285 w32effects = None
286 else:
286 else:
287 origattr = csbi.wAttributes
287 origattr = csbi.wAttributes
288 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
288 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
289 re.MULTILINE | re.DOTALL)
289 re.MULTILINE | re.DOTALL)
290
290
291 def win32print(text, orig, **opts):
291 def win32print(text, writefunc, **opts):
292 label = opts.get('label', '')
292 label = opts.get('label', '')
293 attr = origattr
293 attr = origattr
294
294
295 def mapcolor(val, attr):
295 def mapcolor(val, attr):
296 if val == -1:
296 if val == -1:
297 return origattr
297 return origattr
298 elif val in passthrough:
298 elif val in passthrough:
299 return attr | val
299 return attr | val
300 elif val > 0x0f:
300 elif val > 0x0f:
301 return (val & 0x70) | (attr & 0x8f)
301 return (val & 0x70) | (attr & 0x8f)
302 else:
302 else:
303 return (val & 0x07) | (attr & 0xf8)
303 return (val & 0x07) | (attr & 0xf8)
304
304
305 # determine console attributes based on labels
305 # determine console attributes based on labels
306 for l in label.split():
306 for l in label.split():
307 style = _styles.get(l, '')
307 style = _styles.get(l, '')
308 for effect in style.split():
308 for effect in style.split():
309 try:
309 try:
310 attr = mapcolor(w32effects[effect], attr)
310 attr = mapcolor(w32effects[effect], attr)
311 except KeyError:
311 except KeyError:
312 # w32effects could not have certain attributes so we skip
312 # w32effects could not have certain attributes so we skip
313 # them if not found
313 # them if not found
314 pass
314 pass
315 # hack to ensure regexp finds data
315 # hack to ensure regexp finds data
316 if not text.startswith('\033['):
316 if not text.startswith('\033['):
317 text = '\033[m' + text
317 text = '\033[m' + text
318
318
319 # Look for ANSI-like codes embedded in text
319 # Look for ANSI-like codes embedded in text
320 m = re.match(ansire, text)
320 m = re.match(ansire, text)
321
321
322 try:
322 try:
323 while m:
323 while m:
324 for sattr in m.group(1).split(';'):
324 for sattr in m.group(1).split(';'):
325 if sattr:
325 if sattr:
326 attr = mapcolor(int(sattr), attr)
326 attr = mapcolor(int(sattr), attr)
327 _kernel32.SetConsoleTextAttribute(stdout, attr)
327 _kernel32.SetConsoleTextAttribute(stdout, attr)
328 orig(m.group(2), **opts)
328 writefunc(m.group(2), **opts)
329 m = re.match(ansire, m.group(3))
329 m = re.match(ansire, m.group(3))
330 finally:
330 finally:
331 # Explicitly reset original attributes
331 # Explicitly reset original attributes
332 _kernel32.SetConsoleTextAttribute(stdout, origattr)
332 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now