Implement human color parsing in the command-line by using the standardized CSS 3 subset, well known for styling. Interpret first as text to avoid problems like 'darkgreen' being picked up as 0xDA.
- Also, and make case insensitive by using the strcasecmp() function. Both for colors and modes. The only improvement I can think of is stripping whitespace beforehand. - Also, improve digit parsing by using `strtoul()`, previously only FF00FF worked, whereas now we can type things like 0xFF00ff or 0xff00ff. For example; something like this should work: `OpenRGB.exe -c darkgreen,ff00ff,0xff0000,red,REd,teal,pink,gOLd,AAgoldo,magenta -m breathing` (`AAgoldo` will throw a `Error: Unknown color: 'AAgoldo', skipping.`; and pick the next color instead, this is intended behavior)
This commit is contained in:
parent
d2dfd03551
commit
cb9b22804a
1 changed files with 208 additions and 16 deletions
224
cli.cpp
224
cli.cpp
|
|
@ -33,6 +33,9 @@ static void Sleep(unsigned int milliseconds)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32 /* swy: quirk for MSVC; which doesn't support this case-insensitive function */
|
||||
#define strcasecmp strcmpi
|
||||
#endif
|
||||
|
||||
static std::vector<RGBController*> rgb_controllers;
|
||||
static ProfileManager* profile_manager;
|
||||
|
|
@ -68,30 +71,218 @@ struct Options
|
|||
ServerOptions servOpts;
|
||||
};
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------------*\
|
||||
| Support a common subset of human colors; for easier typing: https://www.w3.org/TR/css-color-3/#svg-color |
|
||||
\*---------------------------------------------------------------------------------------------------------*/
|
||||
struct HumanColors { uint32_t rgb; const char* keyword; } static const human_colors[] =
|
||||
{
|
||||
{ 0x000000, "black" },
|
||||
{ 0x000080, "navy" },
|
||||
{ 0x00008b, "darkblue" },
|
||||
{ 0x0000cd, "mediumblue" },
|
||||
{ 0x0000ff, "blue" },
|
||||
{ 0x006400, "darkgreen" },
|
||||
{ 0x008000, "green" },
|
||||
{ 0x008080, "teal" },
|
||||
{ 0x008b8b, "darkcyan" },
|
||||
{ 0x00bfff, "deepskyblue" },
|
||||
{ 0x00ced1, "darkturquoise" },
|
||||
{ 0x00fa9a, "mediumspringgreen" },
|
||||
{ 0x00ff00, "lime" },
|
||||
{ 0x00ff7f, "springgreen" },
|
||||
{ 0x00ffff, "aqua" },
|
||||
{ 0x00ffff, "cyan" },
|
||||
{ 0x191970, "midnightblue" },
|
||||
{ 0x1e90ff, "dodgerblue" },
|
||||
{ 0x20b2aa, "lightseagreen" },
|
||||
{ 0x228b22, "forestgreen" },
|
||||
{ 0x2e8b57, "seagreen" },
|
||||
{ 0x2f4f4f, "darkslategray" },
|
||||
{ 0x2f4f4f, "darkslategrey" },
|
||||
{ 0x32cd32, "limegreen" },
|
||||
{ 0x3cb371, "mediumseagreen" },
|
||||
{ 0x40e0d0, "turquoise" },
|
||||
{ 0x4169e1, "royalblue" },
|
||||
{ 0x4682b4, "steelblue" },
|
||||
{ 0x483d8b, "darkslateblue" },
|
||||
{ 0x48d1cc, "mediumturquoise" },
|
||||
{ 0x4b0082, "indigo" },
|
||||
{ 0x556b2f, "darkolivegreen" },
|
||||
{ 0x5f9ea0, "cadetblue" },
|
||||
{ 0x6495ed, "cornflowerblue" },
|
||||
{ 0x66cdaa, "mediumaquamarine" },
|
||||
{ 0x696969, "dimgray" },
|
||||
{ 0x696969, "dimgrey" },
|
||||
{ 0x6a5acd, "slateblue" },
|
||||
{ 0x6b8e23, "olivedrab" },
|
||||
{ 0x708090, "slategray" },
|
||||
{ 0x708090, "slategrey" },
|
||||
{ 0x778899, "lightslategray" },
|
||||
{ 0x778899, "lightslategrey" },
|
||||
{ 0x7b68ee, "mediumslateblue" },
|
||||
{ 0x7cfc00, "lawngreen" },
|
||||
{ 0x7fff00, "chartreuse" },
|
||||
{ 0x7fffd4, "aquamarine" },
|
||||
{ 0x800000, "maroon" },
|
||||
{ 0x800080, "purple" },
|
||||
{ 0x808000, "olive" },
|
||||
{ 0x808080, "gray" },
|
||||
{ 0x808080, "grey" },
|
||||
{ 0x87ceeb, "skyblue" },
|
||||
{ 0x87cefa, "lightskyblue" },
|
||||
{ 0x8a2be2, "blueviolet" },
|
||||
{ 0x8b0000, "darkred" },
|
||||
{ 0x8b008b, "darkmagenta" },
|
||||
{ 0x8b4513, "saddlebrown" },
|
||||
{ 0x8fbc8f, "darkseagreen" },
|
||||
{ 0x90ee90, "lightgreen" },
|
||||
{ 0x9370db, "mediumpurple" },
|
||||
{ 0x9400d3, "darkviolet" },
|
||||
{ 0x98fb98, "palegreen" },
|
||||
{ 0x9932cc, "darkorchid" },
|
||||
{ 0x9acd32, "yellowgreen" },
|
||||
{ 0xa0522d, "sienna" },
|
||||
{ 0xa52a2a, "brown" },
|
||||
{ 0xa9a9a9, "darkgray" },
|
||||
{ 0xa9a9a9, "darkgrey" },
|
||||
{ 0xadd8e6, "lightblue" },
|
||||
{ 0xadff2f, "greenyellow" },
|
||||
{ 0xafeeee, "paleturquoise" },
|
||||
{ 0xb0c4de, "lightsteelblue" },
|
||||
{ 0xb0e0e6, "powderblue" },
|
||||
{ 0xb22222, "firebrick" },
|
||||
{ 0xb8860b, "darkgoldenrod" },
|
||||
{ 0xba55d3, "mediumorchid" },
|
||||
{ 0xbc8f8f, "rosybrown" },
|
||||
{ 0xbdb76b, "darkkhaki" },
|
||||
{ 0xc0c0c0, "silver" },
|
||||
{ 0xc71585, "mediumvioletred" },
|
||||
{ 0xcd5c5c, "indianred" },
|
||||
{ 0xcd853f, "peru" },
|
||||
{ 0xd2691e, "chocolate" },
|
||||
{ 0xd2b48c, "tan" },
|
||||
{ 0xd3d3d3, "lightgray" },
|
||||
{ 0xd3d3d3, "lightgrey" },
|
||||
{ 0xd8bfd8, "thistle" },
|
||||
{ 0xda70d6, "orchid" },
|
||||
{ 0xdaa520, "goldenrod" },
|
||||
{ 0xdb7093, "palevioletred" },
|
||||
{ 0xdc143c, "crimson" },
|
||||
{ 0xdcdcdc, "gainsboro" },
|
||||
{ 0xdda0dd, "plum" },
|
||||
{ 0xdeb887, "burlywood" },
|
||||
{ 0xe0ffff, "lightcyan" },
|
||||
{ 0xe6e6fa, "lavender" },
|
||||
{ 0xe9967a, "darksalmon" },
|
||||
{ 0xee82ee, "violet" },
|
||||
{ 0xeee8aa, "palegoldenrod" },
|
||||
{ 0xf08080, "lightcoral" },
|
||||
{ 0xf0e68c, "khaki" },
|
||||
{ 0xf0f8ff, "aliceblue" },
|
||||
{ 0xf0fff0, "honeydew" },
|
||||
{ 0xf0ffff, "azure" },
|
||||
{ 0xf4a460, "sandybrown" },
|
||||
{ 0xf5deb3, "wheat" },
|
||||
{ 0xf5f5dc, "beige" },
|
||||
{ 0xf5f5f5, "whitesmoke" },
|
||||
{ 0xf5fffa, "mintcream" },
|
||||
{ 0xf8f8ff, "ghostwhite" },
|
||||
{ 0xfa8072, "salmon" },
|
||||
{ 0xfaebd7, "antiquewhite" },
|
||||
{ 0xfaf0e6, "linen" },
|
||||
{ 0xfafad2, "lightgoldenrodyellow" },
|
||||
{ 0xfdf5e6, "oldlace" },
|
||||
{ 0xff0000, "red" },
|
||||
{ 0xff00ff, "fuchsia" },
|
||||
{ 0xff00ff, "magenta" },
|
||||
{ 0xff1493, "deeppink" },
|
||||
{ 0xff4500, "orangered" },
|
||||
{ 0xff6347, "tomato" },
|
||||
{ 0xff69b4, "hotpink" },
|
||||
{ 0xff7f50, "coral" },
|
||||
{ 0xff8c00, "darkorange" },
|
||||
{ 0xffa07a, "lightsalmon" },
|
||||
{ 0xffa500, "orange" },
|
||||
{ 0xffb6c1, "lightpink" },
|
||||
{ 0xffc0cb, "pink" },
|
||||
{ 0xffd700, "gold" },
|
||||
{ 0xffdab9, "peachpuff" },
|
||||
{ 0xffdead, "navajowhite" },
|
||||
{ 0xffe4b5, "moccasin" },
|
||||
{ 0xffe4c4, "bisque" },
|
||||
{ 0xffe4e1, "mistyrose" },
|
||||
{ 0xffebcd, "blanchedalmond" },
|
||||
{ 0xffefd5, "papayawhip" },
|
||||
{ 0xfff0f5, "lavenderblush" },
|
||||
{ 0xfff5ee, "seashell" },
|
||||
{ 0xfff8dc, "cornsilk" },
|
||||
{ 0xfffacd, "lemonchiffon" },
|
||||
{ 0xfffaf0, "floralwhite" },
|
||||
{ 0xfffafa, "snow" },
|
||||
{ 0xffff00, "yellow" },
|
||||
{ 0xffffe0, "lightyellow" },
|
||||
{ 0xfffff0, "ivory" },
|
||||
{ 0xffffff, "white" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
bool ParseColors(std::string colors_string, DeviceOptions *options)
|
||||
{
|
||||
while (colors_string.length() >= 6)
|
||||
while (colors_string.length() > 0)
|
||||
{
|
||||
int rgb_end = colors_string.find_first_of(',');
|
||||
size_t rgb_end = colors_string.find_first_of(',');
|
||||
std::string color = colors_string.substr(0, rgb_end);
|
||||
if (color.length() != 6)
|
||||
int32_t rgb = 0;
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
if (color.length() <= 0)
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
unsigned char r = std::stoi(color.substr(0, 2), nullptr, 16);
|
||||
unsigned char g = std::stoi(color.substr(2, 2), nullptr, 16);
|
||||
unsigned char b = std::stoi(color.substr(4, 2), nullptr, 16);
|
||||
options->colors.push_back(std::make_tuple(r, g, b));
|
||||
}
|
||||
catch (...)
|
||||
/* swy: (A) try interpreting it as text; as human keywords, otherwise strtoul() will pick up 'darkgreen' as 0xDA */
|
||||
for (const struct HumanColors *hc = human_colors; hc->keyword != NULL; hc++)
|
||||
{
|
||||
if (strcasecmp(hc->keyword, color.c_str()) != 0)
|
||||
continue;
|
||||
|
||||
rgb = hc->rgb; parsed = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* swy: (B) no luck, try interpreting it as an hexadecimal number instead */
|
||||
if (!parsed)
|
||||
{
|
||||
const char *colorptr = color.c_str(); char *endptr = NULL;
|
||||
|
||||
rgb = strtoul(colorptr, &endptr, 16);
|
||||
|
||||
/* swy: check that strtoul() has advanced the read pointer until the end (NULL terminator);
|
||||
that means it has read the whole thing */
|
||||
if (colorptr != endptr && endptr && *endptr == '\0')
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
/* swy: we got it, save the 32-bit integer as a tuple of three RGB bytes */
|
||||
if (parsed)
|
||||
{
|
||||
options->colors.push_back(std::make_tuple(
|
||||
(rgb >> (8 * 2)) & 0xFF, /* RR.... */
|
||||
(rgb >> (8 * 1)) & 0xFF, /* ..GG.. */
|
||||
(rgb >> (8 * 0)) & 0xFF /* ....BB */
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: Unknown color: '" + color + "', skipping." << std::endl;
|
||||
}
|
||||
|
||||
// If there are no more colors
|
||||
if (rgb_end == static_cast<int>(std::string::npos))
|
||||
if (rgb_end == std::string::npos)
|
||||
break;
|
||||
|
||||
// Remove the current color and the next color's leading comma
|
||||
colors_string = colors_string.substr(color.length() + 1);
|
||||
}
|
||||
|
|
@ -111,7 +302,7 @@ unsigned int ParseMode(DeviceOptions& options)
|
|||
| Search through all of the device modes and see if there is|
|
||||
| a match. If no match is found, print an error message. |
|
||||
\*---------------------------------------------------------*/
|
||||
for(std::size_t mode_idx = 0; mode_idx < rgb_controllers[options.device]->modes.size(); mode_idx++)
|
||||
for(unsigned int mode_idx = 0; mode_idx < rgb_controllers[options.device]->modes.size(); mode_idx++)
|
||||
{
|
||||
if (strcasecmp(rgb_controllers[options.device]->modes[mode_idx].name.c_str(), options.mode.c_str()) == 0)
|
||||
{
|
||||
|
|
@ -176,7 +367,7 @@ void OptionHelp()
|
|||
help_text += " Can be specified multiple times with different modes and colors\n";
|
||||
help_text += "-z, --zone [0-9] Selects zone to apply colors and/or sizes to, or applies to all zones in device if omitted\n";
|
||||
help_text += " Must be specified after specifying a device\n";
|
||||
help_text += "-c, --color \"FFFFFF,00AAFF...\" Sets colors on each device directly if no effect is specified, and sets the effect color if an effect is specified\n";
|
||||
help_text += "-c, --color FFFFFF,00AAFF... Sets colors on each device directly if no effect is specified, and sets the effect color if an effect is specified\n";
|
||||
help_text += " If there are more LEDs than colors given, the last color will be applied to the remaining LEDs\n";
|
||||
help_text += "-m, --mode [breathing | static | ...] Sets the mode to be applied, check --list-devices to see which modes are supported on your device\n";
|
||||
help_text += "-s, --size [0-N] Sets the new size of the specified device zone.\n";
|
||||
|
|
@ -387,6 +578,7 @@ bool OptionMode(int *currentDev, std::string argument, Options *options)
|
|||
std::cout << "Error: --mode passed with no argument" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceOptions* currentDevOpts = GetDeviceOptionsForDevID(options, *currentDev);
|
||||
currentDevOpts->mode = argument;
|
||||
currentDevOpts->hasOption = true;
|
||||
|
|
@ -793,14 +985,14 @@ unsigned int cli_main(int argc, char *argv[], std::vector<RGBController *> rgb_c
|
|||
\*---------------------------------------------------------*/
|
||||
if (options.hasDevice)
|
||||
{
|
||||
for(std::size_t device_idx = 0; device_idx < options.devices.size(); device_idx++)
|
||||
for(unsigned int device_idx = 0; device_idx < options.devices.size(); device_idx++)
|
||||
{
|
||||
ApplyOptions(options.devices[device_idx]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::size_t device_idx = 0; device_idx < rgb_controllers.size(); device_idx++)
|
||||
for (unsigned int device_idx = 0; device_idx < rgb_controllers.size(); device_idx++)
|
||||
{
|
||||
options.allDeviceOptions.device = device_idx;
|
||||
ApplyOptions(options.allDeviceOptions);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue