merge master

This commit is contained in:
Michael Niksa 2018-10-02 09:25:28 -07:00
commit f1627dd571
26 changed files with 1244 additions and 514 deletions

View file

@ -6,6 +6,8 @@ This repo is monitored by the Windows Console engineering team, and provides a b
The Windows Console engineering team greatly appreciate issues containing concise, detailed issues containing repro-steps, and screenshots where appropriate :)
We also appreciate not +1-ing issues with no additional or actionable information. Please use a reaction to show your support of an existing comment on the thread and/or subscribe to notifications using the button in the sidebar in lieu of providing a low-value comment.
### Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact opencode@microsoft.com with any additional questions or comments.

View file

@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2026
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EchoCon", "EchoCon\EchoCon.vcxproj", "{96274800-9574-423E-892A-909FBE2AC8BE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{556CAA54-33E0-4F99-95C8-0DFD6E8F6C6B}"
ProjectSection(SolutionItems) = preProject
readme.md = readme.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{96274800-9574-423E-892A-909FBE2AC8BE}.Debug|x64.ActiveCfg = Debug|x64
{96274800-9574-423E-892A-909FBE2AC8BE}.Debug|x64.Build.0 = Debug|x64
{96274800-9574-423E-892A-909FBE2AC8BE}.Debug|x86.ActiveCfg = Debug|Win32
{96274800-9574-423E-892A-909FBE2AC8BE}.Debug|x86.Build.0 = Debug|Win32
{96274800-9574-423E-892A-909FBE2AC8BE}.Release|x64.ActiveCfg = Release|x64
{96274800-9574-423E-892A-909FBE2AC8BE}.Release|x64.Build.0 = Release|x64
{96274800-9574-423E-892A-909FBE2AC8BE}.Release|x86.ActiveCfg = Release|Win32
{96274800-9574-423E-892A-909FBE2AC8BE}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B27C5007-61E2-4080-965D-8C934367BA4F}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,189 @@
// EchoCon.cpp : Entry point for the EchoCon Pseudo-Consle sample application.
// Copyright © 2018, Microsoft
#include "stdafx.h"
#include <Windows.h>
#include <process.h>
// Forward declarations
HRESULT CreatePseudoConsoleAndPipes(HPCON*, HANDLE*, HANDLE*);
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX*, HPCON);
void __cdecl PipeListener(LPVOID);
int main()
{
wchar_t szCommand[]{ L"ping localhost" };
HRESULT hr{ E_UNEXPECTED };
HANDLE hConsole = { GetStdHandle(STD_OUTPUT_HANDLE) };
// Enable Console VT Processing
DWORD consoleMode{};
GetConsoleMode(hConsole, &consoleMode);
hr = SetConsoleMode(hConsole, consoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
? S_OK
: GetLastError();
if (S_OK == hr)
{
HPCON hPC{ INVALID_HANDLE_VALUE };
// Create the Pseudo Console and pipes to it
HANDLE hPipeIn{ INVALID_HANDLE_VALUE };
HANDLE hPipeOut{ INVALID_HANDLE_VALUE };
hr = CreatePseudoConsoleAndPipes(&hPC, &hPipeIn, &hPipeOut);
if (S_OK == hr)
{
// Create & start thread to listen to the incoming pipe
// Note: Using CRT-safe _beginthread() rather than CreateThread()
HANDLE hPipeListenerThread{ reinterpret_cast<HANDLE>(_beginthread(PipeListener, 0, hPipeIn)) };
// Initialize the necessary startup info struct
STARTUPINFOEX startupInfo{};
if (S_OK == InitializeStartupInfoAttachedToPseudoConsole(&startupInfo, hPC))
{
// Launch ping to emit some text back via the pipe
PROCESS_INFORMATION piClient{};
hr = CreateProcess(
NULL, // No module name - use Command Line
szCommand, // Command Line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Inherit handles
EXTENDED_STARTUPINFO_PRESENT, // Creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&startupInfo.StartupInfo, // Pointer to STARTUPINFO
&piClient) // Pointer to PROCESS_INFORMATION
? S_OK
: GetLastError();
if (S_OK == hr)
{
// Wait up to 10s for ping process to complete
WaitForSingleObject(piClient.hThread, 10 * 1000);
// Allow listening thread to catch-up with final output!
Sleep(500);
}
// --- CLOSEDOWN ---
// Now safe to clean-up client app's process-info & thread
CloseHandle(piClient.hThread);
CloseHandle(piClient.hProcess);
// Cleanup attribute list
DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
free(startupInfo.lpAttributeList);
}
// Close ConPTY - this will terminate client process if running
ClosePseudoConsole(hPC);
// Clean-up the pipes
if (INVALID_HANDLE_VALUE != hPipeOut) CloseHandle(hPipeOut);
if (INVALID_HANDLE_VALUE != hPipeIn) CloseHandle(hPipeIn);
}
}
return S_OK == hr ? EXIT_SUCCESS : EXIT_FAILURE;
}
HRESULT CreatePseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut)
{
HRESULT hr{ E_UNEXPECTED };
HANDLE hPipePTYIn{ INVALID_HANDLE_VALUE };
HANDLE hPipePTYOut{ INVALID_HANDLE_VALUE };
// Create the pipes to which the ConPTY will connect
if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) &&
CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
{
// Determine required size of Pseudo Console
COORD consoleSize{};
CONSOLE_SCREEN_BUFFER_INFO csbi{};
HANDLE hConsole{ GetStdHandle(STD_OUTPUT_HANDLE) };
if (GetConsoleScreenBufferInfo(hConsole, &csbi))
{
consoleSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1;
consoleSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
}
// Create the Pseudo Console of the required size, attached to the PTY-end of the pipes
hr = CreatePseudoConsole(consoleSize, hPipePTYIn, hPipePTYOut, 0, phPC);
// Note: We can close the handles to the PTY-end of the pipes here
// because the handles are dup'ed into the ConHost and will be released
// when the ConPTY is destroyed.
if (INVALID_HANDLE_VALUE != hPipePTYOut) CloseHandle(hPipePTYOut);
if (INVALID_HANDLE_VALUE != hPipePTYIn) CloseHandle(hPipePTYIn);
}
return hr;
}
// Initializes the specified startup info struct with the required properties and
// updates its thread attribute list with the specified ConPTY handle
HRESULT InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC)
{
HRESULT hr{ E_UNEXPECTED };
if (pStartupInfo)
{
size_t attrListSize{};
pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX);
// Get the size of the thread attribute list.
InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);
// Allocate a thread attribute list of the correct size
pStartupInfo->lpAttributeList =
reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attrListSize));
// Initialize thread attribute list
if (pStartupInfo->lpAttributeList
&& InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize))
{
// Set Pseudo Console attribute
hr = UpdateProcThreadAttribute(
pStartupInfo->lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
hPC,
sizeof(HPCON),
NULL,
NULL)
? S_OK
: HRESULT_FROM_WIN32(GetLastError());
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}
void __cdecl PipeListener(LPVOID pipe)
{
HANDLE hPipe{ pipe };
HANDLE hConsole{ GetStdHandle(STD_OUTPUT_HANDLE) };
const DWORD BUFF_SIZE{ 512 };
char szBuffer[BUFF_SIZE]{};
DWORD dwBytesWritten{};
DWORD dwBytesRead{};
BOOL fRead{ FALSE };
do
{
// Read from the pipe
fRead = ReadFile(hPipe, szBuffer, BUFF_SIZE, &dwBytesRead, NULL);
// Write received text to the Console
// Note: Write to the Console using WriteFile(hConsole...), not printf()/puts() to
// prevent partially-read VT sequences from corrupting output
WriteFile(hConsole, szBuffer, dwBytesRead, &dwBytesWritten, NULL);
} while (fRead && dwBytesRead >= 0);
}

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{96274800-9574-423E-892A-909FBE2AC8BE}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>EchoCon</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17738.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="EchoCon.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EchoCon.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// EchoCon.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

View file

@ -0,0 +1,13 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
// TODO: reference additional headers your program requires here

View file

@ -0,0 +1,8 @@
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>

View file

@ -0,0 +1,34 @@
# "EchoCon" ConPTY Sample App
This is a very simple sample application that illustrates how to use the new Win32 Pseudo Console
(ConPTY) by:
1. Creating an input and an output pipe
1. Calling `CreatePseudoConsole()` to create a ConPTY instance attached to the other end of the pipes
1. Spawning an instance of `ping.exe` connected to the ConPTY
1. Running a thread that listens for output from ping.exe, writing received text to the Console
# Pre-Requirements
To build and run this sample, you must install:
* Windows 10 Insider build 17733 or later
* [Latest Windows 10 Insider SDK](https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewSDK)
# Running the sample
Once successfully built, running EchoCon should clear the screen and display the results of the
echo command:
```
Pinging Rincewind [::1] with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
```
# Resources
For more information on the new Pseudo Console infrastructure and API, please review
[this blog post](https://blogs.msdn.microsoft.com/commandline/2018/08/02/windows-command-line-introducing-the-windows-pseudo-console-conpty/)

View file

@ -0,0 +1,94 @@
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using System.Linq;
namespace ColorTool
{
public class ColorScheme
{
public uint[] colorTable = null;
public uint? foreground = null;
public uint? background = null;
public int CalculateIndex(uint value) =>
colorTable.Select((color, idx) => Tuple.Create(color, idx))
.OrderBy(Difference(value))
.First().Item2;
private static Func<Tuple<uint, int>, double> Difference(uint c1) =>
// heuristic 1: nearest neighbor in RGB space
// tup => Distance(RGB(c1), RGB(tup.Item1));
// heuristic 2: nearest neighbor in RGB space
// tup => Distance(HSV(c1), HSV(tup.Item1));
// heuristic 3: weighted RGB L2 distance
tup => WeightedRGBSimilarity(c1, tup.Item1);
private static double WeightedRGBSimilarity(uint c1, uint c2)
{
var rgb1 = RGB(c1);
var rgb2 = RGB(c2);
var dist = rgb1.Zip(rgb2, (a, b) => Math.Pow((int)a - (int)b, 2)).ToArray();
var rbar = (rgb1[0] + rgb1[0]) / 2.0;
return Math.Sqrt(dist[0] * (2 + rbar / 256.0) + dist[1] * 4 + dist[2] * (2 + (255 - rbar) / 256.0));
}
private static double Distance(uint[] c1c, uint[] c2c)
=> Math.Sqrt(c1c.Zip(c2c, (a, b) => Math.Pow((int)a - (int)b, 2)).Sum());
internal static uint[] RGB(uint c) => new[] { c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF };
internal static uint[] HSV(uint c)
{
var rgb = RGB(c).Select(_ => (int)_).ToArray();
int max = rgb.Max();
int min = rgb.Min();
int d = max - min;
int h = 0;
int s = (int)(255 * ((max == 0) ? 0 : d / (double)max));
int v = max;
if (d != 0)
{
double dh;
if (rgb[0] == max) dh = ((rgb[1] - rgb[2]) / (double)d);
else if (rgb[1] == max) dh = 2.0 + ((rgb[2] - rgb[0]) / (double)d);
else /* if (rgb[2] == max) */ dh = 4.0 + ((rgb[0] - rgb[1]) / (double)d);
dh *= 60;
if (dh < 0) dh += 360.0;
h = (int)(dh * 255.0 / 360.0);
}
return new[] { (uint)h, (uint)s, (uint)v };
}
internal void Dump()
{
Action<string, uint> _dump = (str, c) =>
{
var rgb = RGB(c);
var hsv = HSV(c);
Console.WriteLine($"{str} =\tRGB({rgb[0]}, {rgb[1]}, {rgb[2]}),\tHSV({hsv[0]}, {hsv[1]}, {hsv[2]})");
};
for (int i = 0; i < 16; ++i)
{
_dump($"Color[{i}]", colorTable[i]);
}
if (foreground != null)
{
_dump("FG ", foreground.Value);
}
if (background != null)
{
_dump("BG ", background.Value);
}
}
}
}

View file

@ -1,69 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{97F4550F-5775-4E40-8ECF-A03479884120}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>ColorTool</RootNamespace>
<AssemblyName>colortool</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<TargetFramework>net46</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ConsoleAPI.cs" />
<Compile Include="IniSchemeParser.cs" />
<Compile Include="ISchemeParser.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="XmlSchemeParser.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

View file

@ -42,7 +42,7 @@ namespace ColorTool
public uint cbSize;
public COORD dwSize;
public COORD dwCursorPosition;
public short wAttributes;
public ushort wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;

View file

@ -7,6 +7,8 @@ namespace ColorTool
{
interface ISchemeParser
{
uint[] ParseScheme(string schemeName);
string Name { get; }
ColorScheme ParseScheme(string schemeName, bool reportErrors = true);
}
}

View file

@ -4,6 +4,7 @@
//
using System;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using static ColorTool.ConsoleAPI;
@ -16,8 +17,8 @@ namespace ColorTool
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
// These are in Windows Color table order - BRG, not RGB.
static string[] COLOR_NAMES = {
// These are in Windows Color table order - BRG, not RGB.
public static string[] COLOR_NAMES = {
"DARK_BLACK",
"DARK_BLUE",
"DARK_GREEN",
@ -36,6 +37,8 @@ namespace ColorTool
"BRIGHT_WHITE"
};
public string Name => "INI File Parser";
static uint ParseHex(string arg)
{
System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg);
@ -44,11 +47,12 @@ namespace ColorTool
static uint ParseRgb(string arg)
{
int[] components = { 0, 0, 0};
int[] components = { 0, 0, 0 };
string[] args = arg.Split(',');
if (args.Length != components.Length) throw new Exception("Invalid color format \"" + arg + "\"");
if (args.Length != 3) throw new Exception("Invalid color format \"" + arg + "\"");
for (int i = 0; i < args.Length; i++){
for (int i = 0; i < args.Length; i++)
{
components[i] = Int32.Parse(args[i]);
}
@ -67,44 +71,12 @@ namespace ColorTool
}
}
// TODO: Abstract the locating of a scheme into a function the implementation can call into
// Both parsers duplicate the searching, they should just pass in their extension and
// a callback for initally validating the file
static string FindIniScheme(string schemeName)
{
string exeDir = System.IO.Directory.GetParent(System.Reflection.Assembly.GetEntryAssembly().Location).FullName;
string filename = schemeName + ".ini";
string exeSchemes = exeDir + "/schemes/";
string cwd = "./";
string cwdSchemes = "./schemes/";
// Search order, for argument "name", where 'exe' is the dir of the exe.
// 1. ./name
// 2. ./name.ini
// 3. ./schemes/name
// 4. ./schemes/name.ini
// 5. exe/schemes/name
// 6. exe/schemes/name.ini
// 7. name (as an absolute path)
string[] paths = {
cwd + schemeName,
cwd + filename,
cwdSchemes + schemeName,
cwdSchemes + filename,
exeSchemes + schemeName,
exeSchemes + filename,
schemeName,
};
foreach (string path in paths)
{
if (File.Exists(path))
{
return path;
}
}
return null;
return Scheme.GetSearchPaths(schemeName, ".ini").FirstOrDefault(File.Exists);
}
public uint[] ParseScheme(string schemeName)
public ColorScheme ParseScheme(string schemeName, bool reportErrors = true)
{
bool success = true;
@ -124,7 +96,10 @@ namespace ColorTool
if (tableStrings[i].Length <= 0)
{
success = false;
Console.WriteLine(string.Format(Resources.IniParseError, filename, name, tableStrings[i]));
if (reportErrors)
{
Console.WriteLine(string.Format(Resources.IniParseError, filename, name, tableStrings[i]));
}
break;
}
}
@ -141,13 +116,23 @@ namespace ColorTool
}
catch (Exception /*e*/)
{
Console.WriteLine(string.Format(Resources.IniLoadError, filename));
if (reportErrors)
{
Console.WriteLine(string.Format(Resources.IniLoadError, filename));
}
colorTable = null;
}
}
return colorTable;
if (colorTable != null)
{
return new ColorScheme { colorTable = colorTable };
}
else
{
return null;
}
}
}
}

View file

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using static ColorTool.ConsoleAPI;
namespace ColorTool
{
class JsonParser : ISchemeParser
{
static string[] CONCFG_COLOR_NAMES = {
"black", // DARK_BLACK
"dark_blue", // DARK_BLUE
"dark_green", // DARK_GREEN
"dark_cyan", // DARK_CYAN
"dark_red", // DARK_RED
"dark_magenta", // DARK_MAGENTA
"dark_yellow", // DARK_YELLOW
"gray", // DARK_WHITE
"dark_gray", // BRIGHT_BLACK
"blue", // BRIGHT_BLUE
"green", // BRIGHT_GREEN
"cyan", // BRIGHT_CYAN
"red", // BRIGHT_RED
"magenta", // BRIGHT_MAGENTA
"yellow", // BRIGHT_YELLOW
"white" // BRIGHT_WHITE
};
public string Name => "concfg Parser";
static uint ParseColor(string arg)
{
System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg);
return RGB(col.R, col.G, col.B);
}
static XmlDocument loadJsonFile(string schemeName)
{
XmlDocument xmlDoc = new XmlDocument();
foreach (string path in Scheme.GetSearchPaths(schemeName, ".json")
.Where(File.Exists))
{
try
{
var data = File.ReadAllBytes(path);
var reader = JsonReaderWriterFactory.CreateJsonReader(data, System.Xml.XmlDictionaryReaderQuotas.Max);
xmlDoc.Load(reader);
return xmlDoc;
}
catch (XmlException /*e*/) { /* failed to parse */ }
catch (IOException /*e*/) { /* failed to find */ }
catch (UnauthorizedAccessException /*e*/) { /* unauthorized */ }
}
return null;
}
public ColorScheme ParseScheme(string schemeName, bool reportErrors = true)
{
XmlDocument xmlDoc = loadJsonFile(schemeName);
if (xmlDoc == null) return null;
try
{
XmlNode root = xmlDoc.DocumentElement;
XmlNodeList children = root.ChildNodes;
uint[] colorTable = new uint[COLOR_TABLE_SIZE]; ;
for (int i = 0; i < COLOR_TABLE_SIZE; i++)
{
string name = CONCFG_COLOR_NAMES[i];
var node = children.OfType<XmlNode>().Where(n => n.Name == name).Single();
colorTable[i] = ParseColor(node.InnerText);
}
return new ColorScheme { colorTable = colorTable };
}
catch (Exception /*e*/)
{
if (reportErrors)
{
Console.WriteLine("failes to load json scheme");
}
return null;
}
}
}
}

View file

@ -3,10 +3,15 @@
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using static ColorTool.ConsoleAPI;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using static ColorTool.ConsoleAPI;
namespace ColorTool
{
@ -107,7 +112,13 @@ namespace ColorTool
static void Usage()
{
Console.WriteLine(Resources.Usage);
Console.WriteLine(Resources.Usage,
string.Join($"{Environment.NewLine} ", GetParsers().Select(p => p.Name)));
}
static void OutputUsage()
{
Console.WriteLine(Resources.OutputUsage);
}
static void Version()
@ -155,7 +166,7 @@ namespace ColorTool
"46m",
"47m"
};
Console.Write("\t");
for (int bg = 0; bg < BGs.Length; bg++)
{
@ -286,7 +297,56 @@ namespace ColorTool
Console.Write("\x1b[m");
}
static bool SetProperties(uint[] colorTable)
static void PrintSchemes()
{
var schemeDirectory = new FileInfo(new Uri(Assembly.GetEntryAssembly().GetName().CodeBase).AbsolutePath).Directory.FullName + "/schemes";
if (Directory.Exists(schemeDirectory))
{
IntPtr handle = GetStdHandle(-11);
GetConsoleMode(handle, out var mode);
SetConsoleMode(handle, mode | 0x4);
int consoleWidth = Console.WindowWidth;
string fgText = " gYw ";
foreach (string schemeName in Directory.GetFiles(schemeDirectory).Select(Path.GetFileName))
{
ColorScheme colorScheme = GetScheme(schemeName, false);
if (colorScheme != null)
{
string colors = string.Empty;
for (var index = 0; index < 8; index++)
{
uint t = colorScheme.colorTable[index];
var color = UIntToColor(t);
// Set the background color to the color in the scheme, plus some text to show how it looks
colors += $"\x1b[48;2;{color.R};{color.G};{color.B}m{fgText}";
}
// Align scheme colors right, or on newline if it doesn't fit
int schemeTextLength = fgText.Length * 8;
int bufferLength = consoleWidth - (schemeName.Length + schemeTextLength);
string bufferString = bufferLength >= 0
? new string(' ', bufferLength)
: "\n" + new string(' ', consoleWidth - schemeTextLength);
string outputString = schemeName + bufferString + colors;
Console.WriteLine(outputString);
Console.ResetColor();
}
}
}
}
private static Color UIntToColor(uint color)
{
byte r = (byte)(color >> 0);
byte g = (byte)(color >> 8);
byte b = (byte)(color >> 16);
return Color.FromArgb(r, g, b);
}
static bool SetProperties(ColorScheme colorScheme)
{
CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create();
int STD_OUTPUT_HANDLE = -11;
@ -297,7 +357,13 @@ namespace ColorTool
csbiex.srWindow.Bottom++;
for (int i = 0; i < 16; i++)
{
csbiex.ColorTable[i] = colorTable[i];
csbiex.ColorTable[i] = colorScheme.colorTable[i];
}
if(colorScheme.background != null && colorScheme.foreground != null)
{
int fgidx = colorScheme.CalculateIndex(colorScheme.foreground.Value);
int bgidx = colorScheme.CalculateIndex(colorScheme.background.Value);
csbiex.wAttributes = (ushort)(fgidx | (bgidx << 4));
}
SetConsoleScreenBufferInfoEx(hOut, ref csbiex);
}
@ -311,7 +377,7 @@ namespace ColorTool
return success;
}
static bool SetPropertiesWithVt(uint[] colorTable)
static bool SetPropertiesWithVt(ColorScheme colorScheme)
{
int STD_OUTPUT_HANDLE = -11;
IntPtr hOut = GetStdHandle(STD_OUTPUT_HANDLE);
@ -325,10 +391,10 @@ namespace ColorTool
}
for (int i = 0; i < 16; i++)
for (int i = 0; i < colorScheme.colorTable.Length; i++)
{
int vtIndex = VT_INDICIES[i];
uint rgb = colorTable[i];
uint rgb = colorScheme.colorTable[i];
string s = "\x1b]4;" + vtIndex + ";rgb:" + Rvalue(rgb).ToString("X") + "/"+ Gvalue(rgb).ToString("X") + "/"+ Bvalue(rgb).ToString("X") + "\x7";
Console.Write(s);
}
@ -344,19 +410,49 @@ namespace ColorTool
return true;
}
static bool SetDefaults(uint[] colorTable)
static bool SetDefaults(ColorScheme colorScheme)
{
//TODO
RegistryKey consoleKey = Registry.CurrentUser.OpenSubKey("Console", true);
for (int i = 0; i < colorTable.Length; i++)
for (int i = 0; i < colorScheme.colorTable.Length; i++)
{
string valueName = "ColorTable" + (i < 10 ? "0" : "") + i;
consoleKey.SetValue(valueName, colorTable[i], RegistryValueKind.DWord);
consoleKey.SetValue(valueName, colorScheme.colorTable[i], RegistryValueKind.DWord);
}
Console.WriteLine(Resources.WroteToDefaults);
return true;
}
static bool ExportCurrentAsIni(string outputPath)
{
CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create();
int STD_OUTPUT_HANDLE = -11;
IntPtr hOut = GetStdHandle(STD_OUTPUT_HANDLE);
bool success = GetConsoleScreenBufferInfoEx(hOut, ref csbiex);
if (success)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(outputPath))
{
file.WriteLine("[table]");
for (int i = 0; i < 16; i++)
{
string line = IniSchemeParser.COLOR_NAMES[i];
line += " = ";
uint color = csbiex.ColorTable[i];
uint r = color & (0x000000ff);
uint g = (color & (0x0000ff00)) >> 8;
uint b = (color & (0x00ff0000)) >> 16;
line += r + "," + g + "," + b;
file.WriteLine(line);
}
}
}
else
{
Console.WriteLine("Failed to get console information.");
}
return success;
}
static void Main(string[] args)
{
@ -365,9 +461,9 @@ namespace ColorTool
Usage();
return;
}
foreach (string arg in args)
for (int i = 0; i < args.Length; i++)
{
string arg = args[i];
switch (arg)
{
case "-c":
@ -401,6 +497,21 @@ namespace ColorTool
setUnixStyle = true;
setProperties = true;
break;
case "-o":
case "--output":
if (i+1 < args.Length)
{
ExportCurrentAsIni(args[i + 1]);
}
else
{
OutputUsage();
}
return;
case "-s":
case "--schemes":
PrintSchemes();
return;
default:
break;
}
@ -408,19 +519,9 @@ namespace ColorTool
string schemeName = args[args.Length - 1];
uint[] colorTable = null;
ISchemeParser[] parsers = { new XmlSchemeParser(), new IniSchemeParser() };
foreach (var parser in parsers)
{
uint[] table = parser.ParseScheme(schemeName);
if (table != null)
{
colorTable = table;
break;
}
}
ColorScheme colorScheme = GetScheme(schemeName);
if (colorTable == null)
if (colorScheme == null)
{
Console.WriteLine(string.Format(Resources.SchemeNotFound, schemeName));
return;
@ -428,21 +529,40 @@ namespace ColorTool
if (setDefaults)
{
SetDefaults(colorTable);
SetDefaults(colorScheme);
}
if (setProperties)
{
if (setUnixStyle)
{
SetPropertiesWithVt(colorTable);
SetPropertiesWithVt(colorScheme);
}
else
{
SetProperties(colorTable);
SetProperties(colorScheme);
}
}
}
private static IEnumerable<ISchemeParser> GetParsers()
{
return typeof(Program).Assembly.GetTypes()
.Where(t => !t.IsAbstract && typeof(ISchemeParser).IsAssignableFrom(t))
.Select(t => (ISchemeParser)Activator.CreateInstance(t));
}
private static ColorScheme GetScheme(string schemeName, bool reportErrors = true)
{
foreach (var parser in GetParsers())
{
ColorScheme scheme = parser.ParseScheme(schemeName, reportErrors);
if (scheme != null)
{
return scheme;
}
}
return null;
}
}
}

View file

@ -1,34 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ColorTool")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ColorTool")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("97f4550f-5775-4e40-8ecf-a03479884120")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]

View file

@ -0,0 +1,7 @@
{
"profiles": {
"ColorTool": {
"commandName": "Project"
}
}
}

View file

@ -1,134 +1,143 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ColorTool {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ColorTool.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Error loading ini file &quot;{0}&quot;.
/// </summary>
public static string IniLoadError {
get {
return ResourceManager.GetString("IniLoadError", resourceCulture);
}
}
/// <summary>
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ColorTool {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ColorTool.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Error loading ini file &quot;{0}&quot;.
/// </summary>
public static string IniLoadError {
get {
return ResourceManager.GetString("IniLoadError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error loading ini file &quot;{0}&quot;
/// for key &quot;{1}&quot;
/// the value &quot;{2}&quot; is invalid.
/// </summary>
public static string IniParseError {
get {
return ResourceManager.GetString("IniParseError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid Color.
/// </summary>
public static string InvalidColor {
get {
return ResourceManager.GetString("InvalidColor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid scheme - did not find 16 colors.
/// </summary>
public static string InvalidNumberOfColors {
get {
return ResourceManager.GetString("InvalidNumberOfColors", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Could not find or load &quot;{0}&quot;.
/// </summary>
public static string SchemeNotFound {
get {
return ResourceManager.GetString("SchemeNotFound", resourceCulture);
}
}
/// <summary>
/// the value &quot;{2}&quot; is invalid.
/// </summary>
public static string IniParseError {
get {
return ResourceManager.GetString("IniParseError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid Color.
/// </summary>
public static string InvalidColor {
get {
return ResourceManager.GetString("InvalidColor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid scheme - did not find 16 colors.
/// </summary>
public static string InvalidNumberOfColors {
get {
return ResourceManager.GetString("InvalidNumberOfColors", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Usage: colortool -o &lt;filename&gt;.
/// </summary>
public static string OutputUsage {
get {
return ResourceManager.GetString("OutputUsage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Could not find or load &quot;{0}&quot;.
/// </summary>
public static string SchemeNotFound {
get {
return ResourceManager.GetString("SchemeNotFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Usage:
/// colortool.exe [options] &lt;schemename&gt;
///ColorTool is a utility for helping to set the color palette of the Windows Console.
///By default, applies the colors in the specified .itermcolors or .ini file to the current console window.
///This does NOT save the properties automatically. For that, you&apos;ll need to open the properties sheet and hit &quot;Ok&quot;.
///Included should be a `schemes/` directory with a selection of schemes of both formats for examples.
///Feel free to add your own preferred scheme to that dire [rest of string was truncated]&quot;;.
/// </summary>
public static string Usage {
get {
return ResourceManager.GetString("Usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Wrote selected scheme to the defaults..
/// </summary>
public static string WroteToDefaults {
get {
return ResourceManager.GetString("WroteToDefaults", resourceCulture);
}
}
}
}
///Feel free to add your own preferred scheme to that directory. [rest of string was truncated]&quot;;.
/// </summary>
public static string Usage {
get {
return ResourceManager.GetString("Usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Wrote selected scheme to the defaults..
/// </summary>
public static string WroteToDefaults {
get {
return ResourceManager.GetString("WroteToDefaults", resourceCulture);
}
}
}
}

View file

@ -1,140 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="IniLoadError" xml:space="preserve">
<value>Error loading ini file "{0}"</value>
</data>
<data name="IniParseError" xml:space="preserve">
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="IniLoadError" xml:space="preserve">
<value>Error loading ini file "{0}"</value>
</data>
<data name="IniParseError" xml:space="preserve">
<value>Error loading ini file "{0}"
for key "{1}"
the value "{2}" is invalid</value>
</data>
<data name="InvalidColor" xml:space="preserve">
<value>Invalid Color</value>
</data>
<data name="InvalidNumberOfColors" xml:space="preserve">
<value>Invalid scheme - did not find 16 colors</value>
</data>
<data name="SchemeNotFound" xml:space="preserve">
<value>Could not find or load "{0}"</value>
</data>
<data name="Usage" xml:space="preserve">
the value "{2}" is invalid</value>
</data>
<data name="InvalidColor" xml:space="preserve">
<value>Invalid Color</value>
</data>
<data name="InvalidNumberOfColors" xml:space="preserve">
<value>Invalid scheme - did not find 16 colors</value>
</data>
<data name="OutputUsage" xml:space="preserve">
<value>Usage: colortool -o &lt;filename&gt;</value>
</data>
<data name="SchemeNotFound" xml:space="preserve">
<value>Could not find or load "{0}"</value>
</data>
<data name="Usage" xml:space="preserve">
<value>Usage:
colortool.exe [options] &lt;schemename&gt;
ColorTool is a utility for helping to set the color palette of the Windows Console.
@ -153,9 +156,14 @@ Options:
-b, --both : Apply the scheme to both the current console and the defaults.
-x, --xterm : Set the colors using VT sequences. Used for setting the colors in WSL.
Only works in Windows versions &gt;= 17048.
-v, --version : Display the version number</value>
</data>
<data name="WroteToDefaults" xml:space="preserve">
<value>Wrote selected scheme to the defaults.</value>
</data>
-s, --schemes : Displays all available schemes
-v, --version : Display the version number
-o, --output &lt;filename&gt; : output the current color table to an file (in .ini format)
Available importers:
{0}</value>
</data>
<data name="WroteToDefaults" xml:space="preserve">
<value>Wrote selected scheme to the defaults.</value>
</data>
</root>

View file

@ -0,0 +1,41 @@
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System.Collections.Generic;
using System.IO;
namespace ColorTool
{
static class Scheme
{
public static IEnumerable<string> GetSearchPaths(string schemeName, string extension)
{
// Search order, for argument "name", where 'exe' is the dir of the exe.
// 1. ./name
// 2. ./name.ext
// 3. ./schemes/name
// 4. ./schemes/name.ext
// 5. exe/schemes/name
// 6. exe/schemes/name.ext
// 7. name (as an absolute path)
string cwd = "./";
yield return cwd + schemeName;
string filename = schemeName + extension;
yield return cwd + filename;
string cwdSchemes = "./schemes/";
yield return cwdSchemes + schemeName;
yield return cwdSchemes + filename;
string exeDir = Directory.GetParent(System.Reflection.Assembly.GetEntryAssembly().Location).FullName;
string exeSchemes = exeDir + "/schemes/";
yield return exeSchemes + schemeName;
yield return exeSchemes + filename;
yield return schemeName;
}
}
}

View file

@ -4,6 +4,9 @@
//
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Xml;
using static ColorTool.ConsoleAPI;
@ -30,10 +33,14 @@ namespace ColorTool
"Ansi 11 Color", // BRIGHT_YELLOW
"Ansi 15 Color" // BRIGHT_WHITE
};
static string FG_KEY = "Foreground Color";
static string BG_KEY = "Background Color";
static string RED_KEY = "Red Component";
static string GREEN_KEY = "Green Component";
static string BLUE_KEY = "Blue Component";
public string Name => "iTerm Parser";
static bool parseRgbFromXml(XmlNode components, ref uint rgb)
{
int r = -1;
@ -75,53 +82,24 @@ namespace ColorTool
static XmlDocument loadXmlScheme(string schemeName)
{
XmlDocument xmlDoc = new XmlDocument(); // Create an XML document object
string exeDir = System.IO.Directory.GetParent(System.Reflection.Assembly.GetEntryAssembly().Location).FullName;
bool found = false;
string filename = schemeName + ".itermcolors";
string exeSchemes = exeDir + "/schemes/";
string cwd = "./";
string cwdSchemes = "./schemes/";
// Search order, for argument "name", where 'exe' is the dir of the exe.
// 1. ./name
// 2. ./name.itermcolors
// 3. ./schemes/name
// 4. ./schemes/name.itermcolors
// 5. exe/schemes/name
// 6. exe/schemes/name.itermcolors
// 7. name (as an absolute path)
string[] paths = {
cwd + schemeName,
cwd + filename,
cwdSchemes + schemeName,
cwdSchemes + filename,
exeSchemes + schemeName,
exeSchemes + filename,
schemeName,
};
foreach (string path in paths)
foreach (string path in Scheme.GetSearchPaths(schemeName, ".itermcolors")
.Where(File.Exists))
{
try
{
xmlDoc.Load(path);
found = true;
break;
}
catch (Exception /*e*/)
{
// We can either fail to find the file,
// or fail to parse the XML here.
return xmlDoc;
}
catch (XmlException /*e*/) { /* failed to parse */ }
catch (IOException /*e*/) { /* failed to find */ }
catch (UnauthorizedAccessException /*e*/) { /* unauthorized */ }
}
if (!found)
{
return null;
}
return xmlDoc;
return null;
}
public uint[] ParseScheme(string schemeName)
public ColorScheme ParseScheme(string schemeName, bool reportErrors = true)
{
XmlDocument xmlDoc = loadXmlScheme(schemeName); // Create an XML document object
if (xmlDoc == null) return null;
@ -129,51 +107,35 @@ namespace ColorTool
XmlNodeList children = root.ChildNodes;
uint[] colorTable = new uint[COLOR_TABLE_SIZE];
uint? fgColor = null, bgColor = null;
int colorsFound = 0;
bool success = false;
foreach (XmlNode tableEntry in children)
foreach (var tableEntry in children.OfType<XmlNode>().Where(_ => _.Name == "key"))
{
if (tableEntry.Name == "key")
{
int index = -1;
for (int i = 0; i < COLOR_TABLE_SIZE; i++)
{
if (PLIST_COLOR_NAMES[i] == tableEntry.InnerText)
{
index = i;
break;
}
}
if (index == -1)
{
continue;
}
uint rgb = 0; ;
XmlNode components = tableEntry.NextSibling;
success = parseRgbFromXml(components, ref rgb);
if (!success)
{
break;
}
else
{
colorTable[index] = rgb;
colorsFound++;
}
}
uint rgb = 0;
int index = -1;
XmlNode components = tableEntry.NextSibling;
success = parseRgbFromXml(components, ref rgb);
if (!success) { break; }
else if (tableEntry.InnerText == FG_KEY) { fgColor = rgb; }
else if (tableEntry.InnerText == BG_KEY) { bgColor = rgb; }
else if (-1 != (index = Array.IndexOf(PLIST_COLOR_NAMES, tableEntry.InnerText)))
{ colorTable[index] = rgb; colorsFound++; }
}
if (colorsFound < COLOR_TABLE_SIZE)
{
Console.WriteLine(Resources.InvalidNumberOfColors);
if (reportErrors)
{
Console.WriteLine(Resources.InvalidNumberOfColors);
}
success = false;
}
if (!success)
{
return null;
}
return colorTable;
return new ColorScheme { colorTable = colorTable, foreground = fgColor, background = bgColor };
}
}
}

View file

@ -1,4 +1,6 @@
# ColorTool - colortool.exe
# ColorTool
ColorTool makes it easy to change the Windows console to your desired scheme. Includes support for iTerm themes!
```
Usage:
@ -17,6 +19,7 @@ Options:
-q, --quiet : Don't print the color table after applying
-d, --defaults : Apply the scheme to only the defaults in the registry
-b, --both : Apply the scheme to both the current console and the defaults.
-s, --schemes : Display all available schemes
-v, --version : Display the version number
```
@ -35,6 +38,12 @@ Options:
You can also add color schemes to the colortool easily. Take any existing scheme in `.itermcolors` format, and paste it in the `schemes/` directory.
I recommend the excellent [iTerm2-Color-Schemes](https://github.com/mbadolato/iTerm2-Color-Schemes) repo, which has TONS of schemes to choose from, and previews.
You can also easily visually edit `.itermcolors` color schemes using [terminal.sexy](https://terminal.sexy). Use the **Import** and **Export** tabs with `iTerm2` as the format.
## Installing
Just [download the latest colortool release](https://github.com/Microsoft/console/releases) and extract the zip file.
## Building

View file

@ -14,6 +14,14 @@ if exist "%ProgramFiles%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin
set MSBUILD="%ProgramFiles%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
goto :FOUND_MSBUILD
)
if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\msbuild.exe" (
set MSBUILD="%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\msbuild.exe"
goto :FOUND_MSBUILD
)
if exist "%ProgramFiles%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\msbuild.exe" (
set MSBUILD="%ProgramFiles%\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\msbuild.exe"
goto :FOUND_MSBUILD
)
if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\msbuild.exe" (
set MSBUILD="%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\msbuild.exe"
goto :FOUND_MSBUILD

View file

@ -221,13 +221,13 @@
<key>Alpha Component</key>
<real>1</real>
<key>Blue Component</key>
<real>0.203921568627</real>
<real>0.4549019607843137</real>
<key>Color Space</key>
<string>Calibrated</string>
<key>Green Component</key>
<real>0.172549019608</real>
<real>0.38823529411764707</real>
<key>Red Component</key>
<real>0.156862745098</real>
<real>0.35294117647058826</real>
</dict>
<key>Ansi 9 Color</key>
<dict>

View file

@ -161,9 +161,9 @@
<key>Ansi 8 Color</key>
<dict>
<key>Blue Component</key>
<real>0.15170273184776306</real>
<real>0.39215686274509803</real>
<key>Green Component</key>
<real>0.11783610284328461</real>
<real>0.30196078431372547</real>
<key>Red Component</key>
<real>0.0</real>
</dict>