diff --git a/Help/envvar/CCMAKE_COLORS.rst b/Help/envvar/CCMAKE_COLORS.rst new file mode 100644 index 0000000000..d4750c3682 --- /dev/null +++ b/Help/envvar/CCMAKE_COLORS.rst @@ -0,0 +1,34 @@ +CCMAKE_COLORS +------------- + +Determines what colors are used by the CMake curses interface, +when run on a terminal that supports colors. +The syntax follows the same conventions as ``LS_COLORS``; +that is, a list of key/value pairs separated by ``:``. + +Keys are a single letter corresponding to a CMake cache variable type: + +- ``s``: A ``STRING``. +- ``p``: A ``FILEPATH``. +- ``c``: A value which has an associated list of choices. +- ``y``: A ``BOOL`` which has a true-like value (e.g. ``ON``, ``YES``). +- ``n``: A ``BOOL`` which has a false-like value (e.g. ``OFF``, ``NO``). + +Values are an integer number that specifies what color to use. +``0`` is black (you probably don't want to use that). +Others are determined by your terminal's color support. +Most (color) terminals will support at least 8 or 16 colors. +Some will support up to 256 colors. The colors will likely match +`this chart `_, +although the first 16 colors may match the original +`CGA color palette `_. +(Many modern terminal emulators also allow their color palette, +at least for the first 16 colors, to be configured by the user.) + +Note that fairly minimal checking is done for bad colors +(although a value higher than what curses believes your terminal supports +will be silently ignored) or bad syntax. + +For example:: + + CCMAKE_COLORS='s=39:p=220:c=207:n=196:y=46' diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst index dfdf415c30..ee837999f8 100644 --- a/Help/manual/cmake-env-variables.7.rst +++ b/Help/manual/cmake-env-variables.7.rst @@ -80,3 +80,11 @@ Environment Variables for CTest /envvar/CTEST_PROGRESS_OUTPUT /envvar/CTEST_USE_LAUNCHERS_DEFAULT /envvar/DASHBOARD_TEST_FROM_CTEST + +Environment Variables for the CMake curses interface +==================================================== + +.. toctree:: + :maxdepth: 1 + + /envvar/CCMAKE_COLORS diff --git a/Help/release/dev/ccmake-custom-colors.rst b/Help/release/dev/ccmake-custom-colors.rst new file mode 100644 index 0000000000..fcabe56179 --- /dev/null +++ b/Help/release/dev/ccmake-custom-colors.rst @@ -0,0 +1,5 @@ +ccmake-custom-colors +-------------------- + +* :manual:`ccmake(1)` learned to read a :envvar:`CCMAKE_COLORS` + environment variable to customize colors. diff --git a/Source/CursesDialog/cmCursesColor.cxx b/Source/CursesDialog/cmCursesColor.cxx index 641d48ccec..c0b468b600 100644 --- a/Source/CursesDialog/cmCursesColor.cxx +++ b/Source/CursesDialog/cmCursesColor.cxx @@ -2,6 +2,12 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCursesColor.h" +#include +#include +#include +#include +#include + #include "cmCursesStandardIncludes.h" bool cmCursesColor::HasColors() @@ -19,11 +25,54 @@ void cmCursesColor::InitColors() if (HasColors()) { start_color(); use_default_colors(); - init_pair(cmCursesColor::BoolOff, COLOR_RED, -1); - init_pair(cmCursesColor::BoolOn, COLOR_GREEN, -1); - init_pair(cmCursesColor::String, COLOR_BLUE, -1); - init_pair(cmCursesColor::Path, COLOR_YELLOW, -1); - init_pair(cmCursesColor::Options, COLOR_MAGENTA, -1); + init_pair(BoolOff, GetColor('N', COLOR_RED), -1); + init_pair(BoolOn, GetColor('Y', COLOR_GREEN), -1); + init_pair(String, GetColor('S', COLOR_CYAN), -1); + init_pair(Path, GetColor('P', COLOR_YELLOW), -1); + init_pair(Choice, GetColor('C', COLOR_MAGENTA), -1); } #endif } + +short cmCursesColor::GetColor(char id, short fallback) +{ + static bool initialized = false; + static std::unordered_map env; + + if (!initialized) { + if (auto* v = getenv("CCMAKE_COLORS")) { + while (v[0] && v[1] && v[1] == '=') { + auto const n = std::toupper(*v); + + char buffer[12]; + memset(buffer, 0, sizeof(buffer)); + + if (auto* const e = strchr(v, ':')) { + if (static_cast(e - v) > sizeof(buffer)) { + break; + } + + strncpy(buffer, v + 2, static_cast(e - v - 2)); + v = e + 1; + } else { + auto const l = strlen(v); + if (l > sizeof(buffer)) { + break; + } + + strncpy(buffer, v + 2, l - 2); + v += l; + } + + auto const c = atoi(buffer); + if (c && c < COLORS) { + env.emplace(n, static_cast(c)); + } + } + } + initialized = true; + } + + auto const iter = env.find(id); + return (iter == env.end() ? fallback : iter->second); +} diff --git a/Source/CursesDialog/cmCursesColor.h b/Source/CursesDialog/cmCursesColor.h index 78ca52cbb1..f83265f05c 100644 --- a/Source/CursesDialog/cmCursesColor.h +++ b/Source/CursesDialog/cmCursesColor.h @@ -13,12 +13,15 @@ public: BoolOn, String, Path, - Options + Choice }; static bool HasColors(); static void InitColors(); + +protected: + static short GetColor(char id, short fallback); }; #endif // cmCursesColor_h diff --git a/Source/CursesDialog/cmCursesOptionsWidget.cxx b/Source/CursesDialog/cmCursesOptionsWidget.cxx index a15241faed..8df32e2f37 100644 --- a/Source/CursesDialog/cmCursesOptionsWidget.cxx +++ b/Source/CursesDialog/cmCursesOptionsWidget.cxx @@ -17,8 +17,8 @@ cmCursesOptionsWidget::cmCursesOptionsWidget(int width, int height, int left, // the widget into a string widget at some point. BOOL is safe for // now. if (cmCursesColor::HasColors()) { - set_field_fore(this->Field, COLOR_PAIR(cmCursesColor::Options)); - set_field_back(this->Field, COLOR_PAIR(cmCursesColor::Options)); + set_field_fore(this->Field, COLOR_PAIR(cmCursesColor::Choice)); + set_field_back(this->Field, COLOR_PAIR(cmCursesColor::Choice)); } else { set_field_fore(this->Field, A_NORMAL); set_field_back(this->Field, A_STANDOUT);