[PATCH 1 of 3] windows: add a method to enable ANSI color code processing on Windows 10

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Tue May 23 10:35:24 EDT 2017


At Tue, 23 May 2017 08:34:13 -0400,
Matt Harbison wrote:
> 
> # HG changeset patch
> # User Matt Harbison <matt_harbison at yahoo.com>
> # Date 1495504856 14400
> #      Mon May 22 22:00:56 2017 -0400
> # Node ID 58dbbf67bd726092374403b2805d9ef9f5118ebe
> # Parent  8db2feb04cebc1878c6232dd2650f2c5468d350e
> windows: add a method to enable ANSI color code processing on Windows 10
> 
> SetConsoleMode() fails with an invalid parameter error if given this option
> prior to Windows 10, so indicate that to the caller instead of doing explicit
> version checks.
> 
> diff --git a/mercurial/posix.py b/mercurial/posix.py
> --- a/mercurial/posix.py
> +++ b/mercurial/posix.py
> @@ -557,6 +557,14 @@
>      """
>      pass
>  
> +def enablevtmode():
> +    """Enable virtual terminal mode for the associated console.  Return True if
> +    enabled, else False.
> +
> +    Used to enable ANSI color support on Windows 10.
> +    """
> +    return True
> +
>  class cachestat(object):
>      def __init__(self, path):
>          self.stat = os.stat(path)
> diff --git a/mercurial/util.py b/mercurial/util.py
> --- a/mercurial/util.py
> +++ b/mercurial/util.py
> @@ -96,6 +96,7 @@
>  checkexec = platform.checkexec
>  checklink = platform.checklink
>  copymode = platform.copymode
> +enablevtmode = platform.enablevtmode
>  executablepath = platform.executablepath
>  expandglobs = platform.expandglobs
>  explainexit = platform.explainexit
> diff --git a/mercurial/win32.py b/mercurial/win32.py
> --- a/mercurial/win32.py
> +++ b/mercurial/win32.py
> @@ -202,6 +202,12 @@
>  _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
>  _kernel32.SetConsoleCtrlHandler.restype = _BOOL
>  
> +_kernel32.SetConsoleMode.argtypes = [_HANDLE, _DWORD]
> +_kernel32.SetConsoleMode.restype = _BOOL
> +
> +_kernel32.GetConsoleMode.argtypes = [_HANDLE, ctypes.c_void_p]
> +_kernel32.GetConsoleMode.restype = _BOOL
> +
>  _kernel32.GetStdHandle.argtypes = [_DWORD]
>  _kernel32.GetStdHandle.restype = _HANDLE
>  
> @@ -372,6 +378,30 @@
>      height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1
>      return width, height
>  
> +def enablevtmode():
> +    '''Enable virtual terminal mode for the associated console.  Return True if
> +    enabled, else False.'''
> +
> +    ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
> +
> +    # Query stderr, like termsize().  Either stdout or stderr will work.
> +    handle = _kernel32.GetStdHandle(_STD_ERROR_HANDLE) # don't close the handle

Current color.py implementation uses only STD_OUTPUT_HANDLE in order
to initialize for colorization, and redirection of stdout disables
colorization itself.

Therefore, GetConsoleMode() on STD_OUTPUT_HANDLE seems consistent with
color.py, at least now, even though win32.termsize() uses
STD_ERROR_HANDLE for safety at redirection of stdout.

BTW, I'm planning to examine both stdout/stderr for console
information on Windows (termsize, colorization, and so on) for strict
detection, because only stderr might be redirected at runtime. Of
course, it should be very rare case, though :-)


> +    if handle == _INVALID_HANDLE_VALUE:
> +        return False
> +
> +    mode = _DWORD(0)
> +
> +    if not _kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
> +        return False
> +
> +    if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
> +        mode.value |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
> +
> +        if not _kernel32.SetConsoleMode(handle, mode):
> +            return False
> +
> +    return True
> +

Is there any reason to place this implementation in win32.py, and to
use it via util.py proxy ?

color.py already has win32 specific code path, and it seems better to
place this implementation in color.py, IMHO. This can centralize
colorization specific logic into color.py. Introducing empty
enablevtmode() into posix.py seems redundant, too.

BTW, enabling VTP is needed only once. Therefore, if this
implementation is placed in color.py, its result can be cached to
reuse :-)


Implementation itself LGTM!

>  def _1stchild(pid):
>      '''return the 1st found child of the given pid
>  
> diff --git a/mercurial/windows.py b/mercurial/windows.py
> --- a/mercurial/windows.py
> +++ b/mercurial/windows.py
> @@ -30,6 +30,7 @@
>  
>  osutil = policy.importmod(r'osutil')
>  
> +enablevtmode = win32.enablevtmode
>  executablepath = win32.executablepath
>  getuser = win32.getuser
>  hidewindow = win32.hidewindow
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

-- 
----------------------------------------------------------------------
[FUJIWARA Katsunori]                             foozy at lares.dti.ne.jp


More information about the Mercurial-devel mailing list