##// END OF EJS Templates
color: add multiple messages input support to 'win32print'...
Pierre-Yves David -
r31089:a2ee25ff default
parent child Browse files
Show More
@@ -1,403 +1,401
1 1 # color.py color output for Mercurial commands
2 2 #
3 3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''colorize output from some commands
9 9
10 10 The color extension colorizes output from several Mercurial commands.
11 11 For example, the diff command shows additions in green and deletions
12 12 in red, while the status command shows modified files in magenta. Many
13 13 other commands have analogous colors. It is possible to customize
14 14 these colors.
15 15
16 16 Effects
17 17 -------
18 18
19 19 Other effects in addition to color, like bold and underlined text, are
20 20 also available. By default, the terminfo database is used to find the
21 21 terminal codes used to change color and effect. If terminfo is not
22 22 available, then effects are rendered with the ECMA-48 SGR control
23 23 function (aka ANSI escape codes).
24 24
25 25 The available effects in terminfo mode are 'blink', 'bold', 'dim',
26 26 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
27 27 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
28 28 'underline'. How each is rendered depends on the terminal emulator.
29 29 Some may not be available for a given terminal type, and will be
30 30 silently ignored.
31 31
32 32 If the terminfo entry for your terminal is missing codes for an effect
33 33 or has the wrong codes, you can add or override those codes in your
34 34 configuration::
35 35
36 36 [color]
37 37 terminfo.dim = \E[2m
38 38
39 39 where '\E' is substituted with an escape character.
40 40
41 41 Labels
42 42 ------
43 43
44 44 Text receives color effects depending on the labels that it has. Many
45 45 default Mercurial commands emit labelled text. You can also define
46 46 your own labels in templates using the label function, see :hg:`help
47 47 templates`. A single portion of text may have more than one label. In
48 48 that case, effects given to the last label will override any other
49 49 effects. This includes the special "none" effect, which nullifies
50 50 other effects.
51 51
52 52 Labels are normally invisible. In order to see these labels and their
53 53 position in the text, use the global --color=debug option. The same
54 54 anchor text may be associated to multiple labels, e.g.
55 55
56 56 [log.changeset changeset.secret|changeset: 22611:6f0a53c8f587]
57 57
58 58 The following are the default effects for some default labels. Default
59 59 effects may be overridden from your configuration file::
60 60
61 61 [color]
62 62 status.modified = blue bold underline red_background
63 63 status.added = green bold
64 64 status.removed = red bold blue_background
65 65 status.deleted = cyan bold underline
66 66 status.unknown = magenta bold underline
67 67 status.ignored = black bold
68 68
69 69 # 'none' turns off all effects
70 70 status.clean = none
71 71 status.copied = none
72 72
73 73 qseries.applied = blue bold underline
74 74 qseries.unapplied = black bold
75 75 qseries.missing = red bold
76 76
77 77 diff.diffline = bold
78 78 diff.extended = cyan bold
79 79 diff.file_a = red bold
80 80 diff.file_b = green bold
81 81 diff.hunk = magenta
82 82 diff.deleted = red
83 83 diff.inserted = green
84 84 diff.changed = white
85 85 diff.tab =
86 86 diff.trailingwhitespace = bold red_background
87 87
88 88 # Blank so it inherits the style of the surrounding label
89 89 changeset.public =
90 90 changeset.draft =
91 91 changeset.secret =
92 92
93 93 resolve.unresolved = red bold
94 94 resolve.resolved = green bold
95 95
96 96 bookmarks.active = green
97 97
98 98 branches.active = none
99 99 branches.closed = black bold
100 100 branches.current = green
101 101 branches.inactive = none
102 102
103 103 tags.normal = green
104 104 tags.local = black bold
105 105
106 106 rebase.rebased = blue
107 107 rebase.remaining = red bold
108 108
109 109 shelve.age = cyan
110 110 shelve.newest = green bold
111 111 shelve.name = blue bold
112 112
113 113 histedit.remaining = red bold
114 114
115 115 Custom colors
116 116 -------------
117 117
118 118 Because there are only eight standard colors, this module allows you
119 119 to define color names for other color slots which might be available
120 120 for your terminal type, assuming terminfo mode. For instance::
121 121
122 122 color.brightblue = 12
123 123 color.pink = 207
124 124 color.orange = 202
125 125
126 126 to set 'brightblue' to color slot 12 (useful for 16 color terminals
127 127 that have brighter colors defined in the upper eight) and, 'pink' and
128 128 'orange' to colors in 256-color xterm's default color cube. These
129 129 defined colors may then be used as any of the pre-defined eight,
130 130 including appending '_background' to set the background to that color.
131 131
132 132 Modes
133 133 -----
134 134
135 135 By default, the color extension will use ANSI mode (or win32 mode on
136 136 Windows) if it detects a terminal. To override auto mode (to enable
137 137 terminfo mode, for example), set the following configuration option::
138 138
139 139 [color]
140 140 mode = terminfo
141 141
142 142 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
143 143 disable color.
144 144
145 145 Note that on some systems, terminfo mode may cause problems when using
146 146 color with the pager extension and less -R. less with the -R option
147 147 will only display ECMA-48 color codes, and terminfo mode may sometimes
148 148 emit codes that less doesn't understand. You can work around this by
149 149 either using ansi mode (or auto mode), or by using less -r (which will
150 150 pass through all terminal control codes, not just color control
151 151 codes).
152 152
153 153 On some systems (such as MSYS in Windows), the terminal may support
154 154 a different color mode than the pager (activated via the "pager"
155 155 extension). It is possible to define separate modes depending on whether
156 156 the pager is active::
157 157
158 158 [color]
159 159 mode = auto
160 160 pagermode = ansi
161 161
162 162 If ``pagermode`` is not defined, the ``mode`` will be used.
163 163 '''
164 164
165 165 from __future__ import absolute_import
166 166
167 167 try:
168 168 import curses
169 169 curses.COLOR_BLACK # force import
170 170 except ImportError:
171 171 curses = None
172 172
173 173 from mercurial.i18n import _
174 174 from mercurial import (
175 175 cmdutil,
176 176 color,
177 177 commands,
178 178 dispatch,
179 179 encoding,
180 180 extensions,
181 181 pycompat,
182 182 subrepo,
183 183 ui as uimod,
184 184 util,
185 185 )
186 186
187 187 cmdtable = {}
188 188 command = cmdutil.command(cmdtable)
189 189 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
190 190 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
191 191 # be specifying the version(s) of Mercurial they are tested with, or
192 192 # leave the attribute unspecified.
193 193 testedwith = 'ships-with-hg-core'
194 194
195 195 def _terminfosetup(ui, mode):
196 196 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
197 197
198 198 # If we failed to load curses, we go ahead and return.
199 199 if curses is None:
200 200 return
201 201 # Otherwise, see what the config file says.
202 202 if mode not in ('auto', 'terminfo'):
203 203 return
204 204
205 205 for key, val in ui.configitems('color'):
206 206 if key.startswith('color.'):
207 207 newval = (False, int(val), '')
208 208 color._terminfo_params[key[6:]] = newval
209 209 elif key.startswith('terminfo.'):
210 210 newval = (True, '', val.replace('\\E', '\x1b'))
211 211 color._terminfo_params[key[9:]] = newval
212 212 try:
213 213 curses.setupterm()
214 214 except curses.error as e:
215 215 color._terminfo_params.clear()
216 216 return
217 217
218 218 for key, (b, e, c) in color._terminfo_params.items():
219 219 if not b:
220 220 continue
221 221 if not c and not curses.tigetstr(e):
222 222 # Most terminals don't support dim, invis, etc, so don't be
223 223 # noisy and use ui.debug().
224 224 ui.debug("no terminfo entry for %s\n" % e)
225 225 del color._terminfo_params[key]
226 226 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
227 227 # Only warn about missing terminfo entries if we explicitly asked for
228 228 # terminfo mode.
229 229 if mode == "terminfo":
230 230 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
231 231 "ECMA-48 color\n"))
232 232 color._terminfo_params.clear()
233 233
234 234 def _modesetup(ui, coloropt):
235 235 if coloropt == 'debug':
236 236 return 'debug'
237 237
238 238 auto = (coloropt == 'auto')
239 239 always = not auto and util.parsebool(coloropt)
240 240 if not always and not auto:
241 241 return None
242 242
243 243 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
244 244 and ui.formatted()))
245 245
246 246 mode = ui.config('color', 'mode', 'auto')
247 247
248 248 # If pager is active, color.pagermode overrides color.mode.
249 249 if getattr(ui, 'pageractive', False):
250 250 mode = ui.config('color', 'pagermode', mode)
251 251
252 252 realmode = mode
253 253 if mode == 'auto':
254 254 if pycompat.osname == 'nt':
255 255 term = encoding.environ.get('TERM')
256 256 # TERM won't be defined in a vanilla cmd.exe environment.
257 257
258 258 # UNIX-like environments on Windows such as Cygwin and MSYS will
259 259 # set TERM. They appear to make a best effort attempt at setting it
260 260 # to something appropriate. However, not all environments with TERM
261 261 # defined support ANSI. Since "ansi" could result in terminal
262 262 # gibberish, we error on the side of selecting "win32". However, if
263 263 # w32effects is not defined, we almost certainly don't support
264 264 # "win32", so don't even try.
265 265 if (term and 'xterm' in term) or not color.w32effects:
266 266 realmode = 'ansi'
267 267 else:
268 268 realmode = 'win32'
269 269 else:
270 270 realmode = 'ansi'
271 271
272 272 def modewarn():
273 273 # only warn if color.mode was explicitly set and we're in
274 274 # a formatted terminal
275 275 if mode == realmode and ui.formatted():
276 276 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
277 277
278 278 if realmode == 'win32':
279 279 color._terminfo_params.clear()
280 280 if not color.w32effects:
281 281 modewarn()
282 282 return None
283 283 color._effects.update(color.w32effects)
284 284 elif realmode == 'ansi':
285 285 color._terminfo_params.clear()
286 286 elif realmode == 'terminfo':
287 287 _terminfosetup(ui, mode)
288 288 if not color._terminfo_params:
289 289 ## FIXME Shouldn't we return None in this case too?
290 290 modewarn()
291 291 realmode = 'ansi'
292 292 else:
293 293 return None
294 294
295 295 if always or (auto and formatted):
296 296 return realmode
297 297 return None
298 298
299 299 class colorui(uimod.ui):
300 300 def write(self, *args, **opts):
301 301 if self._colormode is None:
302 302 return super(colorui, self).write(*args, **opts)
303 303
304 304 label = opts.get('label', '')
305 305 if self._buffers and not opts.get('prompt', False):
306 306 if self._bufferapplylabels:
307 307 self._buffers[-1].extend(self.label(a, label) for a in args)
308 308 else:
309 309 self._buffers[-1].extend(args)
310 310 elif self._colormode == 'win32':
311 for a in args:
312 color.win32print(a, super(colorui, self).write, **opts)
311 color.win32print(super(colorui, self).write, *args, **opts)
313 312 else:
314 313 return super(colorui, self).write(
315 314 *[self.label(a, label) for a in args], **opts)
316 315
317 316 def write_err(self, *args, **opts):
318 317 if self._colormode is None:
319 318 return super(colorui, self).write_err(*args, **opts)
320 319
321 320 label = opts.get('label', '')
322 321 if self._bufferstates and self._bufferstates[-1][0]:
323 322 return self.write(*args, **opts)
324 323 if self._colormode == 'win32':
325 for a in args:
326 color.win32print(a, super(colorui, self).write_err, **opts)
324 color.win32print(super(colorui, self).write_err, *args, **opts)
327 325 else:
328 326 return super(colorui, self).write_err(
329 327 *[self.label(a, label) for a in args], **opts)
330 328
331 329 def uisetup(ui):
332 330 if ui.plain():
333 331 return
334 332 if not isinstance(ui, colorui):
335 333 colorui.__bases__ = (ui.__class__,)
336 334 ui.__class__ = colorui
337 335 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
338 336 mode = _modesetup(ui_, opts['color'])
339 337 colorui._colormode = mode
340 338 if mode and mode != 'debug':
341 339 color.configstyles(ui_)
342 340 return orig(ui_, opts, cmd, cmdfunc)
343 341 def colorgit(orig, gitsub, commands, env=None, stream=False, cwd=None):
344 342 if gitsub.ui._colormode and len(commands) and commands[0] == "diff":
345 343 # insert the argument in the front,
346 344 # the end of git diff arguments is used for paths
347 345 commands.insert(1, '--color')
348 346 return orig(gitsub, commands, env, stream, cwd)
349 347 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
350 348 extensions.wrapfunction(subrepo.gitsubrepo, '_gitnodir', colorgit)
351 349
352 350 def extsetup(ui):
353 351 commands.globalopts.append(
354 352 ('', 'color', 'auto',
355 353 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
356 354 # and should not be translated
357 355 _("when to colorize (boolean, always, auto, never, or debug)"),
358 356 _('TYPE')))
359 357
360 358 @command('debugcolor',
361 359 [('', 'style', None, _('show all configured styles'))],
362 360 'hg debugcolor')
363 361 def debugcolor(ui, repo, **opts):
364 362 """show available color, effects or style"""
365 363 ui.write(('color mode: %s\n') % ui._colormode)
366 364 if opts.get('style'):
367 365 return _debugdisplaystyle(ui)
368 366 else:
369 367 return _debugdisplaycolor(ui)
370 368
371 369 def _debugdisplaycolor(ui):
372 370 oldstyle = color._styles.copy()
373 371 try:
374 372 color._styles.clear()
375 373 for effect in color._effects.keys():
376 374 color._styles[effect] = effect
377 375 if color._terminfo_params:
378 376 for k, v in ui.configitems('color'):
379 377 if k.startswith('color.'):
380 378 color._styles[k] = k[6:]
381 379 elif k.startswith('terminfo.'):
382 380 color._styles[k] = k[9:]
383 381 ui.write(_('available colors:\n'))
384 382 # sort label with a '_' after the other to group '_background' entry.
385 383 items = sorted(color._styles.items(),
386 384 key=lambda i: ('_' in i[0], i[0], i[1]))
387 385 for colorname, label in items:
388 386 ui.write(('%s\n') % colorname, label=label)
389 387 finally:
390 388 color._styles.clear()
391 389 color._styles.update(oldstyle)
392 390
393 391 def _debugdisplaystyle(ui):
394 392 ui.write(_('available style:\n'))
395 393 width = max(len(s) for s in color._styles)
396 394 for label, effects in sorted(color._styles.items()):
397 395 ui.write('%s' % label, label=label)
398 396 if effects:
399 397 # 50
400 398 ui.write(': ')
401 399 ui.write(' ' * (max(0, width - len(label))))
402 400 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
403 401 ui.write('\n')
@@ -1,332 +1,336
1 1 # utility for color output for Mercurial commands
2 2 #
3 3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com> and other
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from .i18n import _
11 11
12 12 from . import pycompat
13 13
14 14 try:
15 15 import curses
16 16 # Mapping from effect name to terminfo attribute name (or raw code) or
17 17 # color number. This will also force-load the curses module.
18 18 _terminfo_params = {'none': (True, 'sgr0', ''),
19 19 'standout': (True, 'smso', ''),
20 20 'underline': (True, 'smul', ''),
21 21 'reverse': (True, 'rev', ''),
22 22 'inverse': (True, 'rev', ''),
23 23 'blink': (True, 'blink', ''),
24 24 'dim': (True, 'dim', ''),
25 25 'bold': (True, 'bold', ''),
26 26 'invisible': (True, 'invis', ''),
27 27 'italic': (True, 'sitm', ''),
28 28 'black': (False, curses.COLOR_BLACK, ''),
29 29 'red': (False, curses.COLOR_RED, ''),
30 30 'green': (False, curses.COLOR_GREEN, ''),
31 31 'yellow': (False, curses.COLOR_YELLOW, ''),
32 32 'blue': (False, curses.COLOR_BLUE, ''),
33 33 'magenta': (False, curses.COLOR_MAGENTA, ''),
34 34 'cyan': (False, curses.COLOR_CYAN, ''),
35 35 'white': (False, curses.COLOR_WHITE, '')}
36 36 except ImportError:
37 37 curses = None
38 38 _terminfo_params = {}
39 39
40 40 # start and stop parameters for effects
41 41 _effects = {'none': 0,
42 42 'black': 30,
43 43 'red': 31,
44 44 'green': 32,
45 45 'yellow': 33,
46 46 'blue': 34,
47 47 'magenta': 35,
48 48 'cyan': 36,
49 49 'white': 37,
50 50 'bold': 1,
51 51 'italic': 3,
52 52 'underline': 4,
53 53 'inverse': 7,
54 54 'dim': 2,
55 55 'black_background': 40,
56 56 'red_background': 41,
57 57 'green_background': 42,
58 58 'yellow_background': 43,
59 59 'blue_background': 44,
60 60 'purple_background': 45,
61 61 'cyan_background': 46,
62 62 'white_background': 47}
63 63
64 64 _styles = {'grep.match': 'red bold',
65 65 'grep.linenumber': 'green',
66 66 'grep.rev': 'green',
67 67 'grep.change': 'green',
68 68 'grep.sep': 'cyan',
69 69 'grep.filename': 'magenta',
70 70 'grep.user': 'magenta',
71 71 'grep.date': 'magenta',
72 72 'bookmarks.active': 'green',
73 73 'branches.active': 'none',
74 74 'branches.closed': 'black bold',
75 75 'branches.current': 'green',
76 76 'branches.inactive': 'none',
77 77 'diff.changed': 'white',
78 78 'diff.deleted': 'red',
79 79 'diff.diffline': 'bold',
80 80 'diff.extended': 'cyan bold',
81 81 'diff.file_a': 'red bold',
82 82 'diff.file_b': 'green bold',
83 83 'diff.hunk': 'magenta',
84 84 'diff.inserted': 'green',
85 85 'diff.tab': '',
86 86 'diff.trailingwhitespace': 'bold red_background',
87 87 'changeset.public' : '',
88 88 'changeset.draft' : '',
89 89 'changeset.secret' : '',
90 90 'diffstat.deleted': 'red',
91 91 'diffstat.inserted': 'green',
92 92 'histedit.remaining': 'red bold',
93 93 'ui.prompt': 'yellow',
94 94 'log.changeset': 'yellow',
95 95 'patchbomb.finalsummary': '',
96 96 'patchbomb.from': 'magenta',
97 97 'patchbomb.to': 'cyan',
98 98 'patchbomb.subject': 'green',
99 99 'patchbomb.diffstats': '',
100 100 'rebase.rebased': 'blue',
101 101 'rebase.remaining': 'red bold',
102 102 'resolve.resolved': 'green bold',
103 103 'resolve.unresolved': 'red bold',
104 104 'shelve.age': 'cyan',
105 105 'shelve.newest': 'green bold',
106 106 'shelve.name': 'blue bold',
107 107 'status.added': 'green bold',
108 108 'status.clean': 'none',
109 109 'status.copied': 'none',
110 110 'status.deleted': 'cyan bold underline',
111 111 'status.ignored': 'black bold',
112 112 'status.modified': 'blue bold',
113 113 'status.removed': 'red bold',
114 114 'status.unknown': 'magenta bold underline',
115 115 'tags.normal': 'green',
116 116 'tags.local': 'black bold'}
117 117
118 118 def loadcolortable(ui, extname, colortable):
119 119 _styles.update(colortable)
120 120
121 121 def configstyles(ui):
122 122 for status, cfgeffects in ui.configitems('color'):
123 123 if '.' not in status or status.startswith(('color.', 'terminfo.')):
124 124 continue
125 125 cfgeffects = ui.configlist('color', status)
126 126 if cfgeffects:
127 127 good = []
128 128 for e in cfgeffects:
129 129 if valideffect(e):
130 130 good.append(e)
131 131 else:
132 132 ui.warn(_("ignoring unknown color/effect %r "
133 133 "(configured in color.%s)\n")
134 134 % (e, status))
135 135 _styles[status] = ' '.join(good)
136 136
137 137 def valideffect(effect):
138 138 'Determine if the effect is valid or not.'
139 139 return ((not _terminfo_params and effect in _effects)
140 140 or (effect in _terminfo_params
141 141 or effect[:-11] in _terminfo_params))
142 142
143 143 def _effect_str(effect):
144 144 '''Helper function for render_effects().'''
145 145
146 146 bg = False
147 147 if effect.endswith('_background'):
148 148 bg = True
149 149 effect = effect[:-11]
150 150 try:
151 151 attr, val, termcode = _terminfo_params[effect]
152 152 except KeyError:
153 153 return ''
154 154 if attr:
155 155 if termcode:
156 156 return termcode
157 157 else:
158 158 return curses.tigetstr(val)
159 159 elif bg:
160 160 return curses.tparm(curses.tigetstr('setab'), val)
161 161 else:
162 162 return curses.tparm(curses.tigetstr('setaf'), val)
163 163
164 164 def _render_effects(text, effects):
165 165 'Wrap text in commands to turn on each effect.'
166 166 if not text:
167 167 return text
168 168 if _terminfo_params:
169 169 start = ''.join(_effect_str(effect)
170 170 for effect in ['none'] + effects.split())
171 171 stop = _effect_str('none')
172 172 else:
173 173 start = [str(_effects[e]) for e in ['none'] + effects.split()]
174 174 start = '\033[' + ';'.join(start) + 'm'
175 175 stop = '\033[' + str(_effects['none']) + 'm'
176 176 return ''.join([start, text, stop])
177 177
178 178 def colorlabel(ui, msg, label):
179 179 """add color control code according to the mode"""
180 180 if ui._colormode == 'debug':
181 181 if label and msg:
182 182 if msg[-1] == '\n':
183 183 msg = "[%s|%s]\n" % (label, msg[:-1])
184 184 else:
185 185 msg = "[%s|%s]" % (label, msg)
186 186 elif ui._colormode is not None:
187 187 effects = []
188 188 for l in label.split():
189 189 s = _styles.get(l, '')
190 190 if s:
191 191 effects.append(s)
192 192 elif valideffect(l):
193 193 effects.append(l)
194 194 effects = ' '.join(effects)
195 195 if effects:
196 196 msg = '\n'.join([_render_effects(line, effects)
197 197 for line in msg.split('\n')])
198 198 return msg
199 199
200 200 w32effects = None
201 201 if pycompat.osname == 'nt':
202 202 import ctypes
203 203 import re
204 204
205 205 _kernel32 = ctypes.windll.kernel32
206 206
207 207 _WORD = ctypes.c_ushort
208 208
209 209 _INVALID_HANDLE_VALUE = -1
210 210
211 211 class _COORD(ctypes.Structure):
212 212 _fields_ = [('X', ctypes.c_short),
213 213 ('Y', ctypes.c_short)]
214 214
215 215 class _SMALL_RECT(ctypes.Structure):
216 216 _fields_ = [('Left', ctypes.c_short),
217 217 ('Top', ctypes.c_short),
218 218 ('Right', ctypes.c_short),
219 219 ('Bottom', ctypes.c_short)]
220 220
221 221 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
222 222 _fields_ = [('dwSize', _COORD),
223 223 ('dwCursorPosition', _COORD),
224 224 ('wAttributes', _WORD),
225 225 ('srWindow', _SMALL_RECT),
226 226 ('dwMaximumWindowSize', _COORD)]
227 227
228 228 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
229 229 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
230 230
231 231 _FOREGROUND_BLUE = 0x0001
232 232 _FOREGROUND_GREEN = 0x0002
233 233 _FOREGROUND_RED = 0x0004
234 234 _FOREGROUND_INTENSITY = 0x0008
235 235
236 236 _BACKGROUND_BLUE = 0x0010
237 237 _BACKGROUND_GREEN = 0x0020
238 238 _BACKGROUND_RED = 0x0040
239 239 _BACKGROUND_INTENSITY = 0x0080
240 240
241 241 _COMMON_LVB_REVERSE_VIDEO = 0x4000
242 242 _COMMON_LVB_UNDERSCORE = 0x8000
243 243
244 244 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
245 245 w32effects = {
246 246 'none': -1,
247 247 'black': 0,
248 248 'red': _FOREGROUND_RED,
249 249 'green': _FOREGROUND_GREEN,
250 250 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
251 251 'blue': _FOREGROUND_BLUE,
252 252 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
253 253 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
254 254 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
255 255 'bold': _FOREGROUND_INTENSITY,
256 256 'black_background': 0x100, # unused value > 0x0f
257 257 'red_background': _BACKGROUND_RED,
258 258 'green_background': _BACKGROUND_GREEN,
259 259 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
260 260 'blue_background': _BACKGROUND_BLUE,
261 261 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
262 262 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
263 263 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
264 264 _BACKGROUND_BLUE),
265 265 'bold_background': _BACKGROUND_INTENSITY,
266 266 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
267 267 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
268 268 }
269 269
270 270 passthrough = set([_FOREGROUND_INTENSITY,
271 271 _BACKGROUND_INTENSITY,
272 272 _COMMON_LVB_UNDERSCORE,
273 273 _COMMON_LVB_REVERSE_VIDEO])
274 274
275 275 stdout = _kernel32.GetStdHandle(
276 276 _STD_OUTPUT_HANDLE) # don't close the handle returned
277 277 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
278 278 w32effects = None
279 279 else:
280 280 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
281 281 if not _kernel32.GetConsoleScreenBufferInfo(
282 282 stdout, ctypes.byref(csbi)):
283 283 # stdout may not support GetConsoleScreenBufferInfo()
284 284 # when called from subprocess or redirected
285 285 w32effects = None
286 286 else:
287 287 origattr = csbi.wAttributes
288 288 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
289 289 re.MULTILINE | re.DOTALL)
290 290
291 def win32print(text, writefunc, **opts):
291 def win32print(writefunc, *msgs, **opts):
292 for text in msgs:
293 _win32print(text, writefunc, **opts)
294
295 def _win32print(text, writefunc, **opts):
292 296 label = opts.get('label', '')
293 297 attr = origattr
294 298
295 299 def mapcolor(val, attr):
296 300 if val == -1:
297 301 return origattr
298 302 elif val in passthrough:
299 303 return attr | val
300 304 elif val > 0x0f:
301 305 return (val & 0x70) | (attr & 0x8f)
302 306 else:
303 307 return (val & 0x07) | (attr & 0xf8)
304 308
305 309 # determine console attributes based on labels
306 310 for l in label.split():
307 311 style = _styles.get(l, '')
308 312 for effect in style.split():
309 313 try:
310 314 attr = mapcolor(w32effects[effect], attr)
311 315 except KeyError:
312 316 # w32effects could not have certain attributes so we skip
313 317 # them if not found
314 318 pass
315 319 # hack to ensure regexp finds data
316 320 if not text.startswith('\033['):
317 321 text = '\033[m' + text
318 322
319 323 # Look for ANSI-like codes embedded in text
320 324 m = re.match(ansire, text)
321 325
322 326 try:
323 327 while m:
324 328 for sattr in m.group(1).split(';'):
325 329 if sattr:
326 330 attr = mapcolor(int(sattr), attr)
327 331 _kernel32.SetConsoleTextAttribute(stdout, attr)
328 332 writefunc(m.group(2), **opts)
329 333 m = re.match(ansire, m.group(3))
330 334 finally:
331 335 # Explicitly reset original attributes
332 336 _kernel32.SetConsoleTextAttribute(stdout, origattr)
General Comments 0
You need to be logged in to leave comments. Login now