mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2025-12-23 23:37:48 -05:00
1629 lines
69 KiB
C++
1629 lines
69 KiB
C++
/*---------------------------------------------------------*\
|
|
| cli.cpp |
|
|
| |
|
|
| OpenRGB command line interface |
|
|
| |
|
|
| This file is part of the OpenRGB project |
|
|
| SPDX-License-Identifier: GPL-2.0-only |
|
|
\*---------------------------------------------------------*/
|
|
|
|
#include <vector>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <iostream>
|
|
#include "AutoStart.h"
|
|
#include "filesystem.h"
|
|
#include "ProfileManager.h"
|
|
#include "ResourceManager.h"
|
|
#include "RGBController.h"
|
|
#include "i2c_smbus.h"
|
|
#include "NetworkClient.h"
|
|
#include "NetworkServer.h"
|
|
#include "LogManager.h"
|
|
#include "Colors.h"
|
|
|
|
/*-------------------------------------------------------------*\
|
|
| Quirk for MSVC; which doesn't support this case-insensitive |
|
|
| function |
|
|
\*-------------------------------------------------------------*/
|
|
#ifdef _WIN32
|
|
#include <shellapi.h>
|
|
#define strcasecmp _strcmpi
|
|
#endif
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
static std::string profile_save_filename = "";
|
|
const unsigned int brightness_percentage = 100;
|
|
const unsigned int speed_percentage = 100;
|
|
|
|
static int preserve_argc = 0;
|
|
static char **preserve_argv = nullptr;
|
|
|
|
enum {
|
|
RET_FLAG_PRINT_HELP = 1,
|
|
RET_FLAG_START_GUI = 2,
|
|
RET_FLAG_I2C_TOOLS = 4,
|
|
RET_FLAG_START_MINIMIZED = 8,
|
|
RET_FLAG_NO_DETECT = 16,
|
|
RET_FLAG_CLI_POST_DETECTION = 32,
|
|
RET_FLAG_START_SERVER = 64,
|
|
RET_FLAG_NO_AUTO_CONNECT = 128,
|
|
};
|
|
|
|
struct DeviceOptions
|
|
{
|
|
int device;
|
|
int zone = -1;
|
|
std::vector<std::tuple<unsigned char, unsigned char, unsigned char>> colors;
|
|
std::string mode;
|
|
unsigned int speed = 100;
|
|
unsigned int brightness = 100;
|
|
unsigned int size;
|
|
bool random_colors = false;
|
|
bool hasSize = false;
|
|
bool hasOption = false;
|
|
};
|
|
|
|
struct ServerOptions
|
|
{
|
|
bool start = false;
|
|
unsigned short port = OPENRGB_SDK_PORT;
|
|
};
|
|
|
|
struct Options
|
|
{
|
|
std::vector<DeviceOptions> devices;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If hasDevice is false, devices above is empty and |
|
|
| allDeviceOptions shall be applied to all available devices|
|
|
| except in the case that a profile was loaded. |
|
|
\*---------------------------------------------------------*/
|
|
bool hasDevice = false;
|
|
bool profile_loaded = false;
|
|
DeviceOptions allDeviceOptions;
|
|
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[] = {{COLOR_BLACK, "black"},
|
|
{COLOR_NAVY, "navy"},
|
|
{COLOR_DARKBLUE, "darkblue"},
|
|
{COLOR_MEDIUMBLUE, "mediumblue"},
|
|
{COLOR_BLUE, "blue"},
|
|
{COLOR_DARKGREEN, "darkgreen"},
|
|
{COLOR_GREEN, "green"},
|
|
{COLOR_TEAL, "teal"},
|
|
{COLOR_DARKCYAN, "darkcyan"},
|
|
{COLOR_DEEPSKYBLUE, "deepskyblue"},
|
|
{COLOR_DARKTURQUOISE, "darkturquoise"},
|
|
{COLOR_MEDIUMSPRINGGREEN, "mediumspringgreen"},
|
|
{COLOR_LIME, "lime"},
|
|
{COLOR_SPRINGGREEN, "springgreen"},
|
|
{COLOR_AQUA, "aqua"},
|
|
{COLOR_CYAN, "cyan"},
|
|
{COLOR_MIDNIGHTBLUE, "midnightblue"},
|
|
{COLOR_DODGERBLUE, "dodgerblue"},
|
|
{COLOR_LIGHTSEAGREEN, "lightseagreen"},
|
|
{COLOR_FORESTGREEN, "forestgreen"},
|
|
{COLOR_SEAGREEN, "seagreen"},
|
|
{COLOR_DARKSLATEGRAY, "darkslategray"},
|
|
{COLOR_DARKSLATEGREY, "darkslategrey"},
|
|
{COLOR_LIMEGREEN, "limegreen"},
|
|
{COLOR_MEDIUMSEAGREEN, "mediumseagreen"},
|
|
{COLOR_TURQUOISE, "turquoise"},
|
|
{COLOR_ROYALBLUE, "royalblue"},
|
|
{COLOR_STEELBLUE, "steelblue"},
|
|
{COLOR_DARKSLATEBLUE, "darkslateblue"},
|
|
{COLOR_MEDIUMTURQUOISE, "mediumturquoise"},
|
|
{COLOR_INDIGO, "indigo"},
|
|
{COLOR_DARKOLIVEGREEN, "darkolivegreen"},
|
|
{COLOR_CADETBLUE, "cadetblue"},
|
|
{COLOR_CORNFLOWERBLUE, "cornflowerblue"},
|
|
{COLOR_MEDIUMAQUAMARINE, "mediumaquamarine"},
|
|
{COLOR_DIMGRAY, "dimgray"},
|
|
{COLOR_DIMGREY, "dimgrey"},
|
|
{COLOR_SLATEBLUE, "slateblue"},
|
|
{COLOR_OLIVEDRAB, "olivedrab"},
|
|
{COLOR_SLATEGRAY, "slategray"},
|
|
{COLOR_SLATEGREY, "slategrey"},
|
|
{COLOR_LIGHTSLATEGRAY, "lightslategray"},
|
|
{COLOR_LIGHTSLATEGREY, "lightslategrey"},
|
|
{COLOR_MEDIUMSLATEBLUE, "mediumslateblue"},
|
|
{COLOR_LAWNGREEN, "lawngreen"},
|
|
{COLOR_CHARTREUSE, "chartreuse"},
|
|
{COLOR_AQUAMARINE, "aquamarine"},
|
|
{COLOR_MAROON, "maroon"},
|
|
{COLOR_PURPLE, "purple"},
|
|
{COLOR_ELECTRIC_ULTRAMARINE, "electricultramarine"},
|
|
{COLOR_OLIVE, "olive"},
|
|
{COLOR_GRAY, "gray"},
|
|
{COLOR_GREY, "grey"},
|
|
{COLOR_SKYBLUE, "skyblue"},
|
|
{COLOR_LIGHTSKYBLUE, "lightskyblue"},
|
|
{COLOR_BLUEVIOLET, "blueviolet"},
|
|
{COLOR_DARKRED, "darkred"},
|
|
{COLOR_DARKMAGENTA, "darkmagenta"},
|
|
{COLOR_SADDLEBROWN, "saddlebrown"},
|
|
{COLOR_DARKSEAGREEN, "darkseagreen"},
|
|
{COLOR_LIGHTGREEN, "lightgreen"},
|
|
{COLOR_MEDIUMPURPLE, "mediumpurple"},
|
|
{COLOR_DARKVIOLET, "darkviolet"},
|
|
{COLOR_PALEGREEN, "palegreen"},
|
|
{COLOR_DARKORCHID, "darkorchid"},
|
|
{COLOR_YELLOWGREEN, "yellowgreen"},
|
|
{COLOR_SIENNA, "sienna"},
|
|
{COLOR_BROWN, "brown"},
|
|
{COLOR_DARKGRAY, "darkgray"},
|
|
{COLOR_DARKGREY, "darkgrey"},
|
|
{COLOR_LIGHTBLUE, "lightblue"},
|
|
{COLOR_GREENYELLOW, "greenyellow"},
|
|
{COLOR_PALETURQUOISE, "paleturquoise"},
|
|
{COLOR_LIGHTSTEELBLUE, "lightsteelblue"},
|
|
{COLOR_POWDERBLUE, "powderblue"},
|
|
{COLOR_FIREBRICK, "firebrick"},
|
|
{COLOR_DARKGOLDENROD, "darkgoldenrod"},
|
|
{COLOR_MEDIUMORCHID, "mediumorchid"},
|
|
{COLOR_ROSYBROWN, "rosybrown"},
|
|
{COLOR_DARKKHAKI, "darkkhaki"},
|
|
{COLOR_SILVER, "silver"},
|
|
{COLOR_MEDIUMVIOLETRED, "mediumvioletred"},
|
|
{COLOR_INDIANRED, "indianred"},
|
|
{COLOR_PERU, "peru"},
|
|
{COLOR_CHOCOLATE, "chocolate"},
|
|
{COLOR_TAN, "tan"},
|
|
{COLOR_LIGHTGRAY, "lightgray"},
|
|
{COLOR_LIGHTGREY, "lightgrey"},
|
|
{COLOR_THISTLE, "thistle"},
|
|
{COLOR_ORCHID, "orchid"},
|
|
{COLOR_GOLDENROD, "goldenrod"},
|
|
{COLOR_PALEVIOLETRED, "palevioletred"},
|
|
{COLOR_CRIMSON, "crimson"},
|
|
{COLOR_GAINSBORO, "gainsboro"},
|
|
{COLOR_PLUM, "plum"},
|
|
{COLOR_BURLYWOOD, "burlywood"},
|
|
{COLOR_LIGHTCYAN, "lightcyan"},
|
|
{COLOR_LAVENDER, "lavender"},
|
|
{COLOR_DARKSALMON, "darksalmon"},
|
|
{COLOR_VIOLET, "violet"},
|
|
{COLOR_PALEGOLDENROD, "palegoldenrod"},
|
|
{COLOR_LIGHTCORAL, "lightcoral"},
|
|
{COLOR_KHAKI, "khaki"},
|
|
{COLOR_ALICEBLUE, "aliceblue"},
|
|
{COLOR_HONEYDEW, "honeydew"},
|
|
{COLOR_AZURE, "azure"},
|
|
{COLOR_SANDYBROWN, "sandybrown"},
|
|
{COLOR_WHEAT, "wheat"},
|
|
{COLOR_BEIGE, "beige"},
|
|
{COLOR_WHITESMOKE, "whitesmoke"},
|
|
{COLOR_MINTCREAM, "mintcream"},
|
|
{COLOR_GHOSTWHITE, "ghostwhite"},
|
|
{COLOR_SALMON, "salmon"},
|
|
{COLOR_ANTIQUEWHITE, "antiquewhite"},
|
|
{COLOR_LINEN, "linen"},
|
|
{COLOR_LIGHTGOLDENRODYELLOW, "lightgoldenrodyellow"},
|
|
{COLOR_OLDLACE, "oldlace"},
|
|
{COLOR_RED, "red"},
|
|
{COLOR_FUCHSIA, "fuchsia"},
|
|
{COLOR_MAGENTA, "magenta"},
|
|
{COLOR_DEEPPINK, "deeppink"},
|
|
{COLOR_ORANGERED, "orangered"},
|
|
{COLOR_TOMATO, "tomato"},
|
|
{COLOR_HOTPINK, "hotpink"},
|
|
{COLOR_CORAL, "coral"},
|
|
{COLOR_DARKORANGE, "darkorange"},
|
|
{COLOR_LIGHTSALMON, "lightsalmon"},
|
|
{COLOR_ORANGE, "orange"},
|
|
{COLOR_LIGHTPINK, "lightpink"},
|
|
{COLOR_PINK, "pink"},
|
|
{COLOR_GOLD, "gold"},
|
|
{COLOR_PEACHPUFF, "peachpuff"},
|
|
{COLOR_NAVAJOWHITE, "navajowhite"},
|
|
{COLOR_MOCCASIN, "moccasin"},
|
|
{COLOR_BISQUE, "bisque"},
|
|
{COLOR_MISTYROSE, "mistyrose"},
|
|
{COLOR_BLANCHEDALMOND, "blanchedalmond"},
|
|
{COLOR_PAPAYAWHIP, "papayawhip"},
|
|
{COLOR_LAVENDERBLUSH, "lavenderblush"},
|
|
{COLOR_SEASHELL, "seashell"},
|
|
{COLOR_CORNSILK, "cornsilk"},
|
|
{COLOR_LEMONCHIFFON, "lemonchiffon"},
|
|
{COLOR_FLORALWHITE, "floralwhite"},
|
|
{COLOR_SNOW, "snow"},
|
|
{COLOR_YELLOW, "yellow"},
|
|
{COLOR_LIGHTYELLOW, "lightyellow"},
|
|
{COLOR_IVORY, "ivory"},
|
|
{COLOR_WHITE, "white"},
|
|
{0, NULL}};
|
|
|
|
bool ParseColors(std::string colors_string, DeviceOptions *options)
|
|
{
|
|
while (colors_string.length() > 0) {
|
|
size_t rgb_end = colors_string.find_first_of(',');
|
|
std::string color = colors_string.substr(0, rgb_end);
|
|
int32_t rgb = 0;
|
|
|
|
bool parsed = false;
|
|
|
|
if (color.length() <= 0)
|
|
break;
|
|
|
|
/*-----------------------------------------------------------------*\
|
|
| This will set correct colour mode for modes with a |
|
|
| MODE_COLORS_RANDOM else generate a random colour from the |
|
|
| human_colors list above |
|
|
\*-----------------------------------------------------------------*/
|
|
if (color == "random") {
|
|
options->random_colors = true;
|
|
srand((unsigned int) time(NULL));
|
|
int index = rand() % (sizeof(human_colors) / sizeof(human_colors[0]))
|
|
+ 1; //Anything other than black
|
|
rgb = human_colors[index].rgb;
|
|
parsed = true;
|
|
} else {
|
|
/* 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) {
|
|
if (color.length() == 6) {
|
|
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 == std::string::npos)
|
|
break;
|
|
|
|
// Remove the current color and the next color's leading comma
|
|
colors_string = colors_string.substr(color.length() + 1);
|
|
}
|
|
|
|
return options->colors.size() > 0;
|
|
}
|
|
|
|
unsigned int ParseMode(DeviceOptions &options, std::vector<RGBController *> &rgb_controllers)
|
|
{
|
|
// no need to check if --mode wasn't passed
|
|
if (options.mode.size() == 0) {
|
|
return rgb_controllers[options.device]->active_mode;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Search through all of the device modes and see if there is|
|
|
| a match. If no match is found, print an error message. |
|
|
\*---------------------------------------------------------*/
|
|
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) {
|
|
return mode_idx;
|
|
}
|
|
}
|
|
|
|
std::cout << "Error: Mode '" + options.mode + "' not available for device '"
|
|
+ rgb_controllers[options.device]->name + "'"
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
DeviceOptions *GetDeviceOptionsForDevID(Options *opts, int device)
|
|
{
|
|
if (device == -1) {
|
|
return &opts->allDeviceOptions;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < opts->devices.size(); i++) {
|
|
if (opts->devices[i].device == device) {
|
|
return &opts->devices[i];
|
|
}
|
|
}
|
|
|
|
// should never happen
|
|
std::cout << "Internal error: Tried setting an option on a device that wasn't specified"
|
|
<< std::endl;
|
|
abort();
|
|
}
|
|
|
|
std::string QuoteIfNecessary(std::string str)
|
|
{
|
|
if (str.find(' ') == std::string::npos) {
|
|
return str;
|
|
} else {
|
|
return "'" + str + "'";
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------------------------------------*\
|
|
| Option processing functions |
|
|
\*---------------------------------------------------------------------------------------------------------*/
|
|
|
|
void OptionHelp()
|
|
{
|
|
std::string help_text;
|
|
help_text += "OpenRGB ";
|
|
help_text += VERSION_STRING;
|
|
help_text += ", for controlling RGB lighting.\n";
|
|
help_text += "Usage: OpenRGB (--device [--mode] [--color])...\n";
|
|
help_text += "\n";
|
|
help_text += "Options:\n";
|
|
help_text += "--gui Shows the GUI. GUI also appears when "
|
|
"not passing any parameters\n";
|
|
help_text += "--startminimized Starts the GUI minimized to tray. "
|
|
"Implies --gui, even if not specified\n";
|
|
help_text += "--client [IP]:[Port] Starts an SDK client on the given "
|
|
"IP:Port (assumes port 6742 if not specified)\n";
|
|
help_text += "--server Starts the SDK's server\n";
|
|
help_text += "--server-host Sets the SDK's server host. Default: "
|
|
"0.0.0.0 (all network interfaces)\n";
|
|
help_text += "--server-port Sets the SDK's server port. Default: "
|
|
"6742 (1024-65535)\n";
|
|
help_text += "-l, --list-devices Lists every compatible device with "
|
|
"their number\n";
|
|
help_text += "-d, --device [0-9 | \"name\"] Selects device to apply colors and/or "
|
|
"effect to, or applies to all devices if omitted\n";
|
|
help_text += " Basic string search is implemented 3 "
|
|
"characters or more\n";
|
|
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 [random | FFFFF,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 += "-b, --brightness [0-100] Sets the brightness as a percentage if "
|
|
"the mode supports brightness\n";
|
|
help_text += "-s, --speed [0-100] Sets the speed as a percentage if the "
|
|
"mode supports speed\n";
|
|
help_text += "-sz, --size [0-N] Sets the new size of the specified "
|
|
"device zone.\n";
|
|
help_text += " Must be specified after specifying a "
|
|
"zone.\n";
|
|
help_text
|
|
+= " If the specified size is out of range, or "
|
|
"the zone does not offer resizing capability, the size will not be changed\n";
|
|
help_text += "-V, --version Display version and software build "
|
|
"information\n";
|
|
help_text
|
|
+= "-p, --profile filename[.orp] Load the profile from filename/filename.orp\n";
|
|
help_text += "-sp, --save-profile filename.orp Save the given settings to profile "
|
|
"filename.orp\n";
|
|
help_text += "--i2c-tools Shows the I2C/SMBus Tools page in the "
|
|
"GUI. Implies --gui, even if not specified.\n";
|
|
help_text += " USE I2C TOOLS AT YOUR OWN RISK! Don't "
|
|
"use this option if you don't know what you're doing!\n";
|
|
help_text
|
|
+= " There is a risk of bricking your "
|
|
"motherboard, RGB controller, and RAM if you send invalid SMBus/I2C transactions.\n";
|
|
help_text += "--localconfig Use the current working directory "
|
|
"instead of the global configuration directory.\n";
|
|
help_text += "--config path Use a custom path instead of the global "
|
|
"configuration directory.\n";
|
|
help_text
|
|
+= "--nodetect Do not try to detect hardware at startup.\n";
|
|
help_text += "--noautoconnect Do not try to autoconnect to a local "
|
|
"server at startup.\n";
|
|
help_text
|
|
+= "--loglevel [0-6 | error | warning ...] Set the log level (0: fatal to 6: trace).\n";
|
|
help_text += "--print-source Print the source code file and line "
|
|
"number for each log entry.\n";
|
|
help_text += "-v, --verbose Print log messages to stdout.\n";
|
|
help_text += "-vv, --very-verbose Print debug messages and log messages "
|
|
"to stdout.\n";
|
|
help_text += "--autostart-check Check if OpenRGB starting at login is "
|
|
"enabled.\n";
|
|
help_text += "--autostart-disable Disable OpenRGB starting at login.\n";
|
|
help_text += "--autostart-enable arguments Enable OpenRGB to start at login. "
|
|
"Requires arguments to give to OpenRGB at login.\n";
|
|
|
|
std::cout << help_text << std::endl;
|
|
}
|
|
|
|
void OptionVersion()
|
|
{
|
|
std::string version_text;
|
|
version_text += "OpenRGB ";
|
|
version_text += VERSION_STRING;
|
|
version_text += ", for controlling RGB lighting.\n";
|
|
version_text += " Version:\t\t ";
|
|
version_text += VERSION_STRING;
|
|
version_text += "\n Build Date\t\t ";
|
|
version_text += BUILDDATE_STRING;
|
|
version_text += "\n Git Commit ID\t\t ";
|
|
version_text += GIT_COMMIT_ID;
|
|
version_text += "\n Git Commit Date\t ";
|
|
version_text += GIT_COMMIT_DATE;
|
|
version_text += "\n Git Branch\t\t ";
|
|
version_text += GIT_BRANCH;
|
|
version_text += "\n";
|
|
|
|
std::cout << version_text << std::endl;
|
|
}
|
|
|
|
void OptionListDevices(std::vector<RGBController *> &rgb_controllers)
|
|
{
|
|
ResourceManager::get()->WaitForDeviceDetection();
|
|
|
|
for (std::size_t controller_idx = 0; controller_idx < rgb_controllers.size(); controller_idx++) {
|
|
RGBController *controller = rgb_controllers[controller_idx];
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device name |
|
|
\*---------------------------------------------------------*/
|
|
std::cout << controller_idx << ": " << controller->name << std::endl;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device type |
|
|
\*---------------------------------------------------------*/
|
|
std::cout << " Type: " << device_type_to_str(controller->type) << std::endl;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device description |
|
|
\*---------------------------------------------------------*/
|
|
if (!controller->description.empty()) {
|
|
std::cout << " Description: " << controller->description << std::endl;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device version |
|
|
\*---------------------------------------------------------*/
|
|
if (!controller->version.empty()) {
|
|
std::cout << " Version: " << controller->version << std::endl;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device location |
|
|
\*---------------------------------------------------------*/
|
|
if (!controller->location.empty()) {
|
|
std::cout << " Location: " << controller->location << std::endl;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device serial |
|
|
\*---------------------------------------------------------*/
|
|
if (!controller->serial.empty()) {
|
|
std::cout << " Serial: " << controller->serial << std::endl;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device modes |
|
|
\*---------------------------------------------------------*/
|
|
if (!controller->modes.empty()) {
|
|
std::cout << " Modes:";
|
|
|
|
int current_mode = controller->GetMode();
|
|
for (std::size_t mode_idx = 0; mode_idx < controller->modes.size(); mode_idx++) {
|
|
std::string modeStr = QuoteIfNecessary(controller->modes[mode_idx].name);
|
|
|
|
if (current_mode == (int) mode_idx) {
|
|
modeStr = "[" + modeStr + "]";
|
|
}
|
|
std::cout << " " << modeStr;
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device zones |
|
|
\*---------------------------------------------------------*/
|
|
if (!controller->zones.empty()) {
|
|
std::cout << " Zones:";
|
|
|
|
for (std::size_t zone_idx = 0; zone_idx < controller->zones.size(); zone_idx++) {
|
|
std::cout << " " << QuoteIfNecessary(controller->zones[zone_idx].name);
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Print device LEDs |
|
|
\*---------------------------------------------------------*/
|
|
if (!controller->leds.empty()) {
|
|
std::cout << " LEDs:";
|
|
|
|
for (std::size_t led_idx = 0; led_idx < controller->leds.size(); led_idx++) {
|
|
std::cout << " " << QuoteIfNecessary(controller->leds[led_idx].name);
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
bool OptionDevice(std::vector<DeviceOptions> *current_devices,
|
|
std::string argument,
|
|
Options *options,
|
|
std::vector<RGBController *> &rgb_controllers)
|
|
{
|
|
bool found = false;
|
|
ResourceManager::get()->WaitForDeviceDetection();
|
|
|
|
try {
|
|
int current_device = std::stoi(argument);
|
|
|
|
if ((current_device >= static_cast<int>(rgb_controllers.size())) || (current_device < 0)) {
|
|
throw nullptr;
|
|
}
|
|
|
|
DeviceOptions newDev;
|
|
newDev.device = current_device;
|
|
|
|
if (!options->hasDevice) {
|
|
options->hasDevice = true;
|
|
}
|
|
|
|
current_devices->push_back(newDev);
|
|
|
|
found = true;
|
|
} catch (...) {
|
|
if (argument.length() > 1) {
|
|
for (unsigned int i = 0; i < rgb_controllers.size(); i++) {
|
|
/*---------------------------------------------------------*\
|
|
| If the argument is not a number then check all the |
|
|
| controllers names for a match |
|
|
\*---------------------------------------------------------*/
|
|
std::string name = rgb_controllers[i]->name;
|
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
|
std::transform(argument.begin(), argument.end(), argument.begin(), ::tolower);
|
|
|
|
if (name.find(argument) != std::string::npos) {
|
|
found = true;
|
|
|
|
DeviceOptions newDev;
|
|
newDev.device = i;
|
|
|
|
if (!options->hasDevice) {
|
|
options->hasDevice = true;
|
|
}
|
|
|
|
current_devices->push_back(newDev);
|
|
}
|
|
}
|
|
} else {
|
|
std::cout << "Error: Invalid device ID: " + argument << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool OptionZone(std::vector<DeviceOptions> *current_devices,
|
|
std::string argument,
|
|
Options * /*options*/,
|
|
std::vector<RGBController *> &rgb_controllers)
|
|
{
|
|
bool found = false;
|
|
ResourceManager::get()->WaitForDeviceDetection();
|
|
|
|
try {
|
|
int current_zone = std::stoi(argument);
|
|
|
|
for (size_t i = 0; i < current_devices->size(); i++) {
|
|
int current_device = current_devices->at(i).device;
|
|
|
|
if (current_zone >= static_cast<int>(rgb_controllers[current_device]->zones.size())
|
|
|| (current_zone < 0)) {
|
|
throw nullptr;
|
|
}
|
|
|
|
current_devices->at(i).zone = current_zone;
|
|
found = true;
|
|
}
|
|
} catch (...) {
|
|
std::cout << "Error: Invalid zone ID: " + argument << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool CheckColor(std::string argument, DeviceOptions *currentDevOpts)
|
|
{
|
|
if (ParseColors(argument, currentDevOpts)) {
|
|
currentDevOpts->hasOption = true;
|
|
return true;
|
|
} else {
|
|
std::cout << "Error: Invalid color value: " + argument << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool OptionColor(std::vector<DeviceOptions> *current_devices, std::string argument, Options *options)
|
|
{
|
|
/*---------------------------------------------------------*\
|
|
| If a device is not selected i.e. size() == 0 |
|
|
| then add color to allDeviceOptions |
|
|
\*---------------------------------------------------------*/
|
|
bool found = false;
|
|
DeviceOptions *currentDevOpts = &options->allDeviceOptions;
|
|
|
|
if (current_devices->size() == 0) {
|
|
found = CheckColor(argument, currentDevOpts);
|
|
} else {
|
|
for (size_t i = 0; i < current_devices->size(); i++) {
|
|
currentDevOpts = ¤t_devices->at(i);
|
|
|
|
found = CheckColor(argument, currentDevOpts);
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool OptionMode(std::vector<DeviceOptions> *current_devices, std::string argument, Options *options)
|
|
{
|
|
if (argument.size() == 0) {
|
|
std::cout << "Error: --mode passed with no argument" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If a device is not selected i.e. size() == 0 |
|
|
| then add mode to allDeviceOptions |
|
|
\*---------------------------------------------------------*/
|
|
bool found = false;
|
|
DeviceOptions *currentDevOpts = &options->allDeviceOptions;
|
|
|
|
if (current_devices->size() == 0) {
|
|
currentDevOpts->mode = argument;
|
|
currentDevOpts->hasOption = true;
|
|
found = true;
|
|
} else {
|
|
for (size_t i = 0; i < current_devices->size(); i++) {
|
|
currentDevOpts = ¤t_devices->at(i);
|
|
|
|
currentDevOpts->mode = argument;
|
|
currentDevOpts->hasOption = true;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool OptionSpeed(std::vector<DeviceOptions> *current_devices, std::string argument, Options *options)
|
|
{
|
|
if (argument.size() == 0) {
|
|
std::cout << "Error: --speed passed with no argument" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If a device is not selected i.e. size() == 0 |
|
|
| then add speed to allDeviceOptions |
|
|
\*---------------------------------------------------------*/
|
|
bool found = false;
|
|
DeviceOptions *currentDevOpts = &options->allDeviceOptions;
|
|
|
|
if (current_devices->size() == 0) {
|
|
currentDevOpts->speed = std::min(std::max(std::stoi(argument), 0), (int) speed_percentage);
|
|
currentDevOpts->hasOption = true;
|
|
found = true;
|
|
} else {
|
|
for (size_t i = 0; i < current_devices->size(); i++) {
|
|
DeviceOptions *currentDevOpts = ¤t_devices->at(i);
|
|
|
|
currentDevOpts->speed = std::min(std::max(std::stoi(argument), 0),
|
|
(int) speed_percentage);
|
|
currentDevOpts->hasOption = true;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool OptionBrightness(std::vector<DeviceOptions> *current_devices,
|
|
std::string argument,
|
|
Options *options)
|
|
{
|
|
if (argument.size() == 0) {
|
|
std::cout << "Error: --brightness passed with no argument" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If a device is not selected i.e. size() == 0 |
|
|
| then add brightness to allDeviceOptions |
|
|
\*---------------------------------------------------------*/
|
|
bool found = false;
|
|
DeviceOptions *currentDevOpts = &options->allDeviceOptions;
|
|
|
|
if (current_devices->size() == 0) {
|
|
currentDevOpts->brightness = std::min(std::max(std::stoi(argument), 0),
|
|
(int) brightness_percentage);
|
|
currentDevOpts->hasOption = true;
|
|
found = true;
|
|
} else {
|
|
for (size_t i = 0; i < current_devices->size(); i++) {
|
|
DeviceOptions *currentDevOpts = ¤t_devices->at(i);
|
|
|
|
currentDevOpts->brightness = std::min(std::max(std::stoi(argument), 0),
|
|
(int) brightness_percentage);
|
|
currentDevOpts->hasOption = true;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool OptionSize(std::vector<DeviceOptions> *current_devices,
|
|
std::string argument,
|
|
Options * /*options*/,
|
|
std::vector<RGBController *> &rgb_controllers)
|
|
{
|
|
const unsigned int new_size = std::stoi(argument);
|
|
|
|
ResourceManager::get()->WaitForDeviceDetection();
|
|
|
|
for (size_t i = 0; i < current_devices->size(); i++) {
|
|
int current_device = current_devices->at(i).device;
|
|
int current_zone = current_devices->at(i).zone;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Fail out if device, zone, or size are out of range |
|
|
\*---------------------------------------------------------*/
|
|
if ((current_device >= static_cast<int>(rgb_controllers.size())) || (current_device < 0)) {
|
|
std::cout << "Error: Device is out of range" << std::endl;
|
|
return false;
|
|
} else if ((current_zone >= static_cast<int>(rgb_controllers[current_device]->zones.size()))
|
|
|| (current_zone < 0)) {
|
|
std::cout << "Error: Zone is out of range" << std::endl;
|
|
return false;
|
|
} else if ((new_size < rgb_controllers[current_device]->zones[current_zone].leds_min)
|
|
|| (new_size > rgb_controllers[current_device]->zones[current_zone].leds_max)) {
|
|
std::cout << "Error: New size is out of range" << std::endl;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Resize the zone |
|
|
\*---------------------------------------------------------*/
|
|
rgb_controllers[current_device]->ResizeZone(current_zone, new_size);
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Save the profile |
|
|
\*---------------------------------------------------------*/
|
|
ResourceManager::get()->GetProfileManager()->SaveProfile("sizes", true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OptionProfile(std::string argument, std::vector<RGBController *> &rgb_controllers)
|
|
{
|
|
ResourceManager::get()->WaitForDeviceDetection();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Attempt to load profile |
|
|
\*---------------------------------------------------------*/
|
|
if (ResourceManager::get()->GetProfileManager()->LoadProfile(argument)) {
|
|
/*-----------------------------------------------------*\
|
|
| Change device mode if profile loading was successful |
|
|
\*-----------------------------------------------------*/
|
|
for (std::size_t controller_idx = 0; controller_idx < rgb_controllers.size();
|
|
controller_idx++) {
|
|
RGBController *device = rgb_controllers[controller_idx];
|
|
|
|
device->DeviceUpdateMode();
|
|
LOG_DEBUG("[CLI] Updating mode for %s to %i", device->name.c_str(), device->active_mode);
|
|
|
|
if (device->modes[device->active_mode].color_mode == MODE_COLORS_PER_LED) {
|
|
device->DeviceUpdateLEDs();
|
|
LOG_DEBUG("[CLI] Mode uses per-LED color, also updating LEDs");
|
|
}
|
|
}
|
|
|
|
std::cout << "Profile loaded successfully" << std::endl;
|
|
return true;
|
|
} else {
|
|
std::cout << "Profile failed to load" << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool OptionSaveProfile(std::string argument)
|
|
{
|
|
/*---------------------------------------------------------*\
|
|
| Set save profile filename |
|
|
\*---------------------------------------------------------*/
|
|
profile_save_filename = argument;
|
|
return (true);
|
|
}
|
|
|
|
int ProcessOptions(Options *options, std::vector<RGBController *> &rgb_controllers)
|
|
{
|
|
unsigned int ret_flags = 0;
|
|
int arg_index = 1;
|
|
std::vector<DeviceOptions> current_devices;
|
|
|
|
options->hasDevice = false;
|
|
options->profile_loaded = false;
|
|
|
|
#ifdef _WIN32
|
|
int fake_argc;
|
|
wchar_t **argvw = CommandLineToArgvW(GetCommandLineW(), &fake_argc);
|
|
#endif
|
|
|
|
while (arg_index < preserve_argc) {
|
|
std::string option = preserve_argv[arg_index];
|
|
std::string argument = "";
|
|
filesystem::path arg_path;
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Handle options that take an argument |
|
|
\*---------------------------------------------------------*/
|
|
if (arg_index + 1 < preserve_argc) {
|
|
argument = preserve_argv[arg_index + 1];
|
|
#ifdef _WIN32
|
|
arg_path = argvw[arg_index + 1];
|
|
#else
|
|
arg_path = argument;
|
|
#endif
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -l / --list-devices (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
if (option == "--list-devices" || option == "-l") {
|
|
OptionListDevices(rgb_controllers);
|
|
exit(0);
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -d / --device |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--device" || option == "-d") {
|
|
while (!current_devices.empty()) {
|
|
options->devices.push_back(current_devices.back());
|
|
current_devices.pop_back();
|
|
}
|
|
|
|
if (!OptionDevice(¤t_devices, argument, options, rgb_controllers)) {
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -z / --zone |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--zone" || option == "-z") {
|
|
if (!OptionZone(¤t_devices, argument, options, rgb_controllers)) {
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -c / --color |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--color" || option == "-c") {
|
|
if (!OptionColor(¤t_devices, argument, options)) {
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -m / --mode |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--mode" || option == "-m") {
|
|
if (!OptionMode(¤t_devices, argument, options)) {
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -b / --brightness |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--brightness" || option == "-b") {
|
|
if (!OptionBrightness(¤t_devices, argument, options)) {
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -s / --speed |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--speed" || option == "-s") {
|
|
if (!OptionSpeed(¤t_devices, argument, options)) {
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -sz / --size |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--size" || option == "-sz") {
|
|
if (!OptionSize(¤t_devices, argument, options, rgb_controllers)) {
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -p / --profile |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--profile" || option == "-p") {
|
|
options->profile_loaded = OptionProfile(arg_path.generic_u8string(), rgb_controllers);
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -sp / --save-profile |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--save-profile" || option == "-sp") {
|
|
OptionSaveProfile(arg_path.generic_u8string());
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Invalid option |
|
|
\*---------------------------------------------------------*/
|
|
else {
|
|
if ((option == "--localconfig") || (option == "--nodetect")
|
|
|| (option == "--noautoconnect") || (option == "--server") || (option == "--gui")
|
|
|| (option == "--i2c-tools" || option == "--yolo") || (option == "--startminimized")
|
|
|| (option == "--print-source") || (option == "--verbose" || option == "-v")
|
|
|| (option == "--very-verbose" || option == "-vv")
|
|
|| (option == "--help" || option == "-h")
|
|
|| (option == "--version" || option == "-V") || (option == "--autostart-check")
|
|
|| (option == "--autostart-disable")) {
|
|
/*-------------------------------------------------*\
|
|
| Do nothing, these are pre-detection arguments |
|
|
| and this parser should ignore them |
|
|
\*-------------------------------------------------*/
|
|
} else if ((option == "--server-port") || (option == "--server-host")
|
|
|| (option == "--loglevel") || (option == "--config")
|
|
|| (option == "--client") || (option == "--autostart-enable")) {
|
|
/*-------------------------------------------------*\
|
|
| Increment index for pre-detection arguments with |
|
|
| parameter |
|
|
\*-------------------------------------------------*/
|
|
arg_index++;
|
|
} else {
|
|
/*-------------------------------------------------*\
|
|
| If the argument is not a pre-detection argument, |
|
|
| throw an error and print help |
|
|
\*-------------------------------------------------*/
|
|
std::cout << "Error: Invalid option: " + option << std::endl;
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If a device was specified, check to verify that a |
|
|
| corresponding option was also specified |
|
|
\*---------------------------------------------------------*/
|
|
while (!current_devices.empty()) {
|
|
options->devices.push_back(current_devices.back());
|
|
current_devices.pop_back();
|
|
}
|
|
|
|
if (options->hasDevice) {
|
|
for (std::size_t option_idx = 0; option_idx < options->devices.size(); option_idx++) {
|
|
if (!options->devices[option_idx].hasOption) {
|
|
std::cout << "Error: Device " + std::to_string(option_idx)
|
|
+ " specified, but neither mode nor color given"
|
|
<< std::endl;
|
|
return RET_FLAG_PRINT_HELP;
|
|
}
|
|
}
|
|
return 0;
|
|
} else {
|
|
return ret_flags;
|
|
}
|
|
}
|
|
|
|
void ApplyOptions(DeviceOptions &options, std::vector<RGBController *> &rgb_controllers)
|
|
{
|
|
RGBController *device = rgb_controllers[options.device];
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Set mode first, in case it's 'direct' (which affects |
|
|
| SetLED below) |
|
|
\*---------------------------------------------------------*/
|
|
unsigned int mode = ParseMode(options, rgb_controllers);
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If the user has specified random colours and the device |
|
|
| supports that colour mode then swich to it before |
|
|
| evaluating if a colour needs to be set |
|
|
\*---------------------------------------------------------*/
|
|
if (options.random_colors && (device->modes[mode].flags & MODE_FLAG_HAS_RANDOM_COLOR)) {
|
|
device->modes[mode].color_mode = MODE_COLORS_RANDOM;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If the user has specified random colours and the device |
|
|
| supports that colour mode then swich to it before |
|
|
| evaluating if a colour needs to be set |
|
|
\*---------------------------------------------------------*/
|
|
if ((device->modes[mode].flags & MODE_FLAG_HAS_BRIGHTNESS)) {
|
|
unsigned int new_brightness = device->modes[mode].brightness_max
|
|
- device->modes[mode].brightness_min;
|
|
new_brightness *= options.brightness;
|
|
new_brightness /= brightness_percentage;
|
|
|
|
device->modes[mode].brightness = device->modes[mode].brightness_min + new_brightness;
|
|
}
|
|
|
|
if ((device->modes[mode].flags & MODE_FLAG_HAS_SPEED)) {
|
|
unsigned int new_speed = device->modes[mode].speed_max - device->modes[mode].speed_min;
|
|
new_speed *= options.speed;
|
|
new_speed /= speed_percentage;
|
|
|
|
device->modes[mode].speed = device->modes[mode].speed_min + new_speed;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Determine which color mode this mode uses and update |
|
|
| colors accordingly |
|
|
\*---------------------------------------------------------*/
|
|
switch (device->modes[mode].color_mode) {
|
|
case MODE_COLORS_NONE:
|
|
break;
|
|
|
|
case MODE_COLORS_RANDOM:
|
|
break;
|
|
|
|
case MODE_COLORS_PER_LED:
|
|
if (options.colors.size() != 0) {
|
|
std::size_t last_set_color = 0;
|
|
|
|
RGBColor *start_from;
|
|
unsigned int led_count;
|
|
if (options.zone < 0) {
|
|
start_from = &device->colors[0];
|
|
led_count = (unsigned int) device->leds.size();
|
|
} else {
|
|
start_from = device->zones[options.zone].colors;
|
|
led_count = device->zones[options.zone].leds_count;
|
|
}
|
|
|
|
for (std::size_t led_idx = 0; led_idx < led_count; led_idx++) {
|
|
if (led_idx < options.colors.size()) {
|
|
last_set_color = led_idx;
|
|
}
|
|
|
|
start_from[led_idx] = ToRGBColor(std::get<0>(options.colors[last_set_color]),
|
|
std::get<1>(options.colors[last_set_color]),
|
|
std::get<2>(options.colors[last_set_color]));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MODE_COLORS_MODE_SPECIFIC:
|
|
if (options.colors.size() >= device->modes[mode].colors_min
|
|
&& options.colors.size() <= device->modes[mode].colors_max) {
|
|
device->modes[mode].colors.resize(options.colors.size());
|
|
|
|
for (std::size_t color_idx = 0; color_idx < options.colors.size(); color_idx++) {
|
|
device->modes[mode].colors[color_idx]
|
|
= ToRGBColor(std::get<0>(options.colors[color_idx]),
|
|
std::get<1>(options.colors[color_idx]),
|
|
std::get<2>(options.colors[color_idx]));
|
|
}
|
|
} else {
|
|
std::cout << "Wrong number of colors specified for mode " + device->modes[mode].name
|
|
<< std::endl;
|
|
std::cout << "Please provide between " + std::to_string(device->modes[mode].colors_min)
|
|
+ " and " + std::to_string(device->modes[mode].colors_min) + " colors"
|
|
<< std::endl;
|
|
exit(0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Set device mode |
|
|
\*---------------------------------------------------------*/
|
|
device->active_mode = mode;
|
|
device->DeviceUpdateMode();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Set device per-LED colors if necessary |
|
|
\*---------------------------------------------------------*/
|
|
if (device->modes[mode].color_mode == MODE_COLORS_PER_LED) {
|
|
device->DeviceUpdateLEDs();
|
|
}
|
|
}
|
|
|
|
unsigned int cli_pre_detection(int argc, char *argv[])
|
|
{
|
|
/*---------------------------------------------------------*\
|
|
| Process only the arguments that should be performed prior |
|
|
| to detecting devices and/or starting clients |
|
|
\*---------------------------------------------------------*/
|
|
int arg_index = 1;
|
|
unsigned int cfg_args = 0;
|
|
unsigned int ret_flags = 0;
|
|
std::string server_host = OPENRGB_SDK_HOST;
|
|
unsigned short server_port = OPENRGB_SDK_PORT;
|
|
bool server_start = false;
|
|
bool print_help = false;
|
|
|
|
preserve_argc = argc;
|
|
preserve_argv = argv;
|
|
|
|
#ifdef _WIN32
|
|
int fake_argc;
|
|
wchar_t **argvw = CommandLineToArgvW(GetCommandLineW(), &fake_argc);
|
|
#endif
|
|
|
|
while (arg_index < argc) {
|
|
std::string option = argv[arg_index];
|
|
std::string argument = "";
|
|
|
|
LOG_DEBUG("[CLI] Parsing CLI option: %s", option.c_str());
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Handle options that take an argument |
|
|
\*---------------------------------------------------------*/
|
|
if (arg_index + 1 < argc) {
|
|
argument = argv[arg_index + 1];
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --localconfig |
|
|
\*---------------------------------------------------------*/
|
|
if (option == "--localconfig") {
|
|
ResourceManager::get()->SetConfigurationDirectory("./");
|
|
cfg_args++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --config |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--config") {
|
|
cfg_args += 2;
|
|
arg_index++;
|
|
#ifdef _WIN32
|
|
filesystem::path config_path(argvw[arg_index]);
|
|
#else
|
|
filesystem::path config_path(argument);
|
|
#endif
|
|
|
|
if (filesystem::is_directory(config_path)) {
|
|
ResourceManager::get()->SetConfigurationDirectory(config_path);
|
|
LOG_INFO("[CLI] Setting config directory to %s",
|
|
argument.c_str()); // TODO: Use config_path in logs somehow
|
|
} else {
|
|
LOG_ERROR("[CLI] '%s' is not a valid directory",
|
|
argument.c_str()); // TODO: Use config_path in logs somehow
|
|
print_help = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --nodetect |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--nodetect") {
|
|
ret_flags |= RET_FLAG_NO_DETECT;
|
|
cfg_args++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --noautoconnect |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--noautoconnect") {
|
|
ret_flags |= RET_FLAG_NO_AUTO_CONNECT;
|
|
cfg_args++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --client |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--client") {
|
|
NetworkClient *client = new NetworkClient(ResourceManager::get()->GetRGBControllers());
|
|
|
|
std::size_t pos = argument.find(":");
|
|
std::string ip = argument.substr(0, pos);
|
|
unsigned short port_val;
|
|
|
|
if (pos == argument.npos) {
|
|
port_val = OPENRGB_SDK_PORT;
|
|
} else {
|
|
std::string port = argument.substr(argument.find(":") + 1);
|
|
port_val = std::stoi(port);
|
|
}
|
|
|
|
std::string titleString = "OpenRGB ";
|
|
titleString.append(VERSION_STRING);
|
|
|
|
client->SetIP(ip.c_str());
|
|
client->SetName(titleString.c_str());
|
|
client->SetPort(port_val);
|
|
|
|
client->StartClient();
|
|
|
|
for (int timeout = 0; timeout < 100; timeout++) {
|
|
if (client->GetConnected()) {
|
|
break;
|
|
}
|
|
std::this_thread::sleep_for(10ms);
|
|
}
|
|
|
|
ResourceManager::get()->GetClients().push_back(client);
|
|
|
|
cfg_args++;
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --server (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--server") {
|
|
server_start = true;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --server-port |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--server-port") {
|
|
if (argument != "") {
|
|
try {
|
|
int port = std::stoi(argument);
|
|
if (port >= 1024 && port <= 65535) {
|
|
server_port = port;
|
|
server_start = true;
|
|
} else {
|
|
std::cout << "Error: Port out of range: " << port << " (1024-65535)"
|
|
<< std::endl;
|
|
print_help = true;
|
|
break;
|
|
}
|
|
} catch (std::invalid_argument & /*e*/) {
|
|
std::cout << "Error: Invalid data in --server-port argument (expected a number "
|
|
"in range 1024-65535)"
|
|
<< std::endl;
|
|
print_help = true;
|
|
break;
|
|
}
|
|
} else {
|
|
std::cout << "Error: Missing argument for --server-port" << std::endl;
|
|
print_help = true;
|
|
break;
|
|
}
|
|
cfg_args++;
|
|
arg_index++;
|
|
}
|
|
/*---------------------------------------------------------*\
|
|
| --server-host |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--server-host") {
|
|
if (argument != "") {
|
|
std::string host = argument;
|
|
|
|
server_host = host;
|
|
server_start = true;
|
|
} else {
|
|
std::cout << "Error: Missing argument for --server-host" << std::endl;
|
|
print_help = true;
|
|
break;
|
|
}
|
|
cfg_args++;
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --loglevel |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--loglevel") {
|
|
if (argument != "") {
|
|
try {
|
|
int level = std::stoi(argument);
|
|
if (level >= 0 && level <= LL_TRACE) {
|
|
LogManager::get()->setLoglevel(level);
|
|
} else {
|
|
LOG_ERROR("[CLI] Loglevel out of range: %d (0-6)", level);
|
|
print_help = true;
|
|
break;
|
|
}
|
|
} catch (std::invalid_argument & /*e*/) {
|
|
if (!strcasecmp(argument.c_str(), "fatal")) {
|
|
LogManager::get()->setLoglevel(LL_FATAL);
|
|
} else if (!strcasecmp(argument.c_str(), "error")) {
|
|
LogManager::get()->setLoglevel(LL_ERROR);
|
|
} else if (!strcasecmp(argument.c_str(), "warning")) {
|
|
LogManager::get()->setLoglevel(LL_WARNING);
|
|
} else if (!strcasecmp(argument.c_str(), "info")) {
|
|
LogManager::get()->setLoglevel(LL_INFO);
|
|
} else if (!strcasecmp(argument.c_str(), "verbose")) {
|
|
LogManager::get()->setLoglevel(LL_VERBOSE);
|
|
} else if (!strcasecmp(argument.c_str(), "debug")) {
|
|
LogManager::get()->setLoglevel(LL_DEBUG);
|
|
} else if (!strcasecmp(argument.c_str(), "trace")) {
|
|
LogManager::get()->setLoglevel(LL_TRACE);
|
|
} else {
|
|
LOG_ERROR("[CLI] Invalid loglevel");
|
|
print_help = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
LOG_ERROR("[CLI] Missing argument for --loglevel");
|
|
print_help = true;
|
|
break;
|
|
}
|
|
cfg_args += 2;
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --autostart-check (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--autostart-check") {
|
|
AutoStart auto_start("OpenRGB");
|
|
|
|
if (auto_start.IsAutoStartEnabled()) {
|
|
std::cout << "Autostart is enabled." << std::endl;
|
|
} else {
|
|
std::cout << "Autostart is disabled." << std::endl;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --autostart-disable (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--autostart-disable") {
|
|
AutoStart auto_start("OpenRGB");
|
|
|
|
if (auto_start.DisableAutoStart()) {
|
|
std::cout << "Autostart disabled." << std::endl;
|
|
} else {
|
|
std::cout << "Autostart failed to disable." << std::endl;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --autostart-enable |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--autostart-enable") {
|
|
if (argument != "") {
|
|
std::string desc = "OpenRGB ";
|
|
desc += VERSION_STRING;
|
|
desc += ", for controlling RGB lighting.";
|
|
|
|
AutoStart auto_start("OpenRGB");
|
|
AutoStartInfo auto_start_interface;
|
|
|
|
auto_start_interface.args = argument;
|
|
auto_start_interface.category = "Utility;";
|
|
auto_start_interface.desc = desc;
|
|
auto_start_interface.icon = "OpenRGB";
|
|
auto_start_interface.path = auto_start.GetExePath();
|
|
|
|
if (auto_start.EnableAutoStart(auto_start_interface)) {
|
|
std::cout << "Autostart enabled." << std::endl;
|
|
} else {
|
|
std::cout << "Autostart failed to enable." << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << "Error: Missing argument for --autostart-enable" << std::endl;
|
|
print_help = true;
|
|
break;
|
|
}
|
|
|
|
cfg_args++;
|
|
arg_index++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --gui (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--gui") {
|
|
ret_flags |= RET_FLAG_START_GUI;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --i2c-tools / --yolo (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--i2c-tools" || option == "--yolo") {
|
|
ret_flags |= RET_FLAG_START_GUI | RET_FLAG_I2C_TOOLS;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --startminimized (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--startminimized") {
|
|
ret_flags |= RET_FLAG_START_GUI | RET_FLAG_START_MINIMIZED;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -h / --help (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--help" || option == "-h") {
|
|
print_help = true;
|
|
break;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -V / --version (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--version" || option == "-V") {
|
|
OptionVersion();
|
|
exit(0);
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -v / --verbose (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--verbose" || option == "-v") {
|
|
LogManager::get()->setVerbosity(LL_VERBOSE);
|
|
cfg_args++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| -vv / --very-verbose (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--very-verbose" || option == "-vv") {
|
|
LogManager::get()->setVerbosity(LL_TRACE);
|
|
cfg_args++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| --print-source (no arguments) |
|
|
\*---------------------------------------------------------*/
|
|
else if (option == "--print-source") {
|
|
LogManager::get()->setPrintSource(true);
|
|
cfg_args++;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Any unrecognized arguments trigger the post-detection CLI |
|
|
\*---------------------------------------------------------*/
|
|
else {
|
|
ret_flags |= RET_FLAG_CLI_POST_DETECTION;
|
|
}
|
|
|
|
arg_index++;
|
|
}
|
|
|
|
if (print_help) {
|
|
OptionHelp();
|
|
exit(0);
|
|
}
|
|
|
|
if (server_start) {
|
|
NetworkServer *server = ResourceManager::get()->GetServer();
|
|
server->SetHost(server_host);
|
|
server->SetPort(server_port);
|
|
ret_flags |= RET_FLAG_START_SERVER;
|
|
}
|
|
|
|
if ((argc - cfg_args) <= 1) {
|
|
ret_flags |= RET_FLAG_START_GUI;
|
|
}
|
|
|
|
return (ret_flags);
|
|
}
|
|
|
|
unsigned int cli_post_detection()
|
|
{
|
|
/*---------------------------------------------------------*\
|
|
| Wait for device detection |
|
|
\*---------------------------------------------------------*/
|
|
ResourceManager::get()->WaitForDeviceDetection();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Get controller list from resource manager |
|
|
\*---------------------------------------------------------*/
|
|
std::vector<RGBController *> rgb_controllers = ResourceManager::get()->GetRGBControllers();
|
|
|
|
/*---------------------------------------------------------*\
|
|
| Process the argument options |
|
|
\*---------------------------------------------------------*/
|
|
Options options;
|
|
unsigned int ret_flags = ProcessOptions(&options, rgb_controllers);
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If the return flags are set, exit CLI mode without |
|
|
| processing device updates from CLI input. |
|
|
\*---------------------------------------------------------*/
|
|
switch (ret_flags) {
|
|
case 0:
|
|
break;
|
|
|
|
case RET_FLAG_PRINT_HELP:
|
|
OptionHelp();
|
|
exit(-1);
|
|
break;
|
|
|
|
default:
|
|
return ret_flags;
|
|
break;
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If the options has one or more specific devices, loop |
|
|
| through all of the specific devices and apply settings. |
|
|
| Otherwise, apply settings to all devices. |
|
|
\*---------------------------------------------------------*/
|
|
if (options.hasDevice) {
|
|
for (unsigned int device_idx = 0; device_idx < options.devices.size(); device_idx++) {
|
|
ApplyOptions(options.devices[device_idx], rgb_controllers);
|
|
}
|
|
} else if (!options.profile_loaded) {
|
|
for (unsigned int device_idx = 0; device_idx < rgb_controllers.size(); device_idx++) {
|
|
options.allDeviceOptions.device = device_idx;
|
|
ApplyOptions(options.allDeviceOptions, rgb_controllers);
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------*\
|
|
| If there is a save filename set, save the profile |
|
|
\*---------------------------------------------------------*/
|
|
if (profile_save_filename != "") {
|
|
if (ResourceManager::get()->GetProfileManager()->SaveProfile(profile_save_filename)) {
|
|
LOG_INFO("[CLI] Profile saved successfully");
|
|
} else {
|
|
LOG_ERROR("[CLI] Profile saving failed");
|
|
}
|
|
}
|
|
|
|
std::this_thread::sleep_for(1s);
|
|
|
|
return 0;
|
|
}
|