From 7624a70b67466ccb0769ea3e6b5f3dd209f4895c Mon Sep 17 00:00:00 2001 From: Tom Greenwood Date: Sat, 26 Jun 2021 06:01:25 +0000 Subject: [PATCH] Add Start at Login Option (Windows and Linux implemented, MacOS stubbed) This merge request adds the following: 1. A new class AutoStart, designed to add login startup entries for Linux (FreeDesktop autostart) and Windows (Shortcut File). 1. CLI options to enable, disable and check for autostart. (--autostart-enable, --autostart-disable and --autostart-check). e.g. OpenRGB.exe --autostart-enable "--startminimized --server --profile Blue" --nodetect --noautoconnect 1. UI options to enable "Start At Login" with several other options (see screenshots in Comments) Tested on KDE Neon and Windows 10 x64 (x64 build). Commits squashed and amended for code style by Adam Honse --- AutoStart/AutoStart-Linux.cpp | 212 +++++++++++++++++ AutoStart/AutoStart-Linux.h | 19 ++ AutoStart/AutoStart-MacOS.cpp | 181 +++++++++++++++ AutoStart/AutoStart-MacOS.h | 18 ++ AutoStart/AutoStart-Windows.cpp | 204 +++++++++++++++++ AutoStart/AutoStart-Windows.h | 19 ++ AutoStart/AutoStart.cpp | 16 ++ AutoStart/AutoStart.h | 40 ++++ OpenRGB.pro | 11 + cli.cpp | 85 ++++++- qt/OpenRGBSettingsPage.cpp | 388 +++++++++++++++++++++++++++++++- qt/OpenRGBSettingsPage.h | 20 ++ qt/OpenRGBSettingsPage.ui | 128 +++++++++-- 13 files changed, 1318 insertions(+), 23 deletions(-) create mode 100644 AutoStart/AutoStart-Linux.cpp create mode 100644 AutoStart/AutoStart-Linux.h create mode 100644 AutoStart/AutoStart-MacOS.cpp create mode 100644 AutoStart/AutoStart-MacOS.h create mode 100644 AutoStart/AutoStart-Windows.cpp create mode 100644 AutoStart/AutoStart-Windows.h create mode 100644 AutoStart/AutoStart.cpp create mode 100644 AutoStart/AutoStart.h diff --git a/AutoStart/AutoStart-Linux.cpp b/AutoStart/AutoStart-Linux.cpp new file mode 100644 index 00000000..f09f97e5 --- /dev/null +++ b/AutoStart/AutoStart-Linux.cpp @@ -0,0 +1,212 @@ +#include "AutoStart-Linux.h" +#include "LogManager.h" +#include "filesystem.h" + +#include +#include +#include +#include + +/*-----------------------------------------------------*\ +| Linux AutoStart Implementation | +| Public Methods | +\*-----------------------------------------------------*/ + +AutoStart::AutoStart(std::string name) +{ + InitAutoStart(name); +} + +bool AutoStart::DisableAutoStart() +{ + std::error_code autostart_file_remove_errcode; + bool success = false; + + /*-------------------------------------------------*\ + | Check if the filename is valid | + \*-------------------------------------------------*/ + if(autostart_file != "") + { + /*---------------------------------------------*\ + | If file doesn't exist, disable is successful | + \*---------------------------------------------*/ + if(!filesystem::exists(autostart_file)) + { + success = true; + } + /*---------------------------------------------*\ + | Otherwise, delete the file | + \*---------------------------------------------*/ + else + { + success = filesystem::remove(autostart_file, autostart_file_remove_errcode); + + if(!success) + { + LOG_ERROR("[AutoStart] An error occurred removing the auto start file."); + } + } + } + else + { + LOG_ERROR("Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info) +{ + bool success = false; + + /*-------------------------------------------------*\ + | Check if the filename is valid | + \*-------------------------------------------------*/ + if(autostart_file != "") + { + std::string desktop_file = GenerateDesktopFile(autostart_info); + std::ofstream autostart_file_stream(autostart_file, std::ios::out | std::ios::trunc); + + /*---------------------------------------------*\ + | Error out if the file could not be opened | + \*---------------------------------------------*/ + if(!autostart_file_stream) + { + LOG_ERROR("Could not open %s for writing.", autostart_file.c_str()); + success = false; + } + /*---------------------------------------------*\ + | Otherwise, write the file | + \*---------------------------------------------*/ + else + { + autostart_file_stream << desktop_file; + autostart_file_stream.close(); + success = !autostart_file_stream.fail(); + + if (!success) + { + LOG_ERROR("An error occurred writing the auto start file."); + } + } + } + else + { + LOG_ERROR("Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::IsAutoStartEnabled() +{ + /*-------------------------------------------------*\ + | Check if the filename is valid | + \*-------------------------------------------------*/ + if(autostart_file != "") + { + return(filesystem::exists(autostart_file)); + } + else + { + return(false); + } +} + +std::string AutoStart::GetExePath() +{ + /*-------------------------------------------------*\ + | Create the OpenRGB executable path | + \*-------------------------------------------------*/ + char exepath[ PATH_MAX ]; + + ssize_t count = readlink("/proc/self/exe", exepath, PATH_MAX); + + return(std::string(exepath, (count > 0) ? count : 0)); +} + +/*-----------------------------------------------------*\ +| Linux AutoStart Implementation | +| Private Methods | +\*-----------------------------------------------------*/ + +std::string AutoStart::GenerateDesktopFile(AutoStartInfo autostart_info) +{ + /*-------------------------------------------------*\ + | Generate a .desktop file from the AutoStart | + | parameters | + \*-------------------------------------------------*/ + std::stringstream fileContents; + + fileContents << "[Desktop Entry]" << std::endl; + fileContents << "Categories=" << autostart_info.category << std::endl; + fileContents << "Comment=" << autostart_info.desc << std::endl; + fileContents << "Icon=" << autostart_info.icon << std::endl; + fileContents << "Name=" << GetAutoStartName() << std::endl; + fileContents << "StartupNotify=true" << std::endl; + fileContents << "Terminal=false" << std::endl; + fileContents << "Type=Application" << std::endl; + + /*-------------------------------------------------*\ + | Add the executable path and arguments | + \*-------------------------------------------------*/ + fileContents << "Exec=" << autostart_info.path; + + if (autostart_info.args != "") + { + fileContents << " " << autostart_info.args; + } + + fileContents << std::endl; + + return(fileContents.str()); +} + +void AutoStart::InitAutoStart(std::string name) +{ + std::string autostart_dir; + + autostart_name = name; + + /*-------------------------------------------------*\ + | Get home and config paths | + \*-------------------------------------------------*/ + const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + const char *home = getenv("HOME"); + + /*-------------------------------------------------*\ + | Determine where the autostart .desktop files are | + | kept | + \*-------------------------------------------------*/ + if(xdg_config_home != NULL) + { + autostart_dir = xdg_config_home; + autostart_dir = autostart_dir + "/autostart/"; + } + else if(home != NULL) + { + autostart_dir = home; + autostart_dir = autostart_dir + "/.config/autostart/"; + } + + /*-------------------------------------------------*\ + | Check if the filename is valid | + \*-------------------------------------------------*/ + if(autostart_dir != "") + { + std::error_code ec; + + bool success = true; + + if(!filesystem::exists(autostart_dir)) + { + success = filesystem::create_directories(autostart_dir, ec); + } + + if(success) + { + autostart_file = autostart_dir + autostart_name + ".desktop"; + } + } +} + diff --git a/AutoStart/AutoStart-Linux.h b/AutoStart/AutoStart-Linux.h new file mode 100644 index 00000000..3ea43ec8 --- /dev/null +++ b/AutoStart/AutoStart-Linux.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "AutoStart.h" + +class AutoStart: public AutoStartInterface +{ +public: + AutoStart(std::string name); + + bool DisableAutoStart(); + bool EnableAutoStart(AutoStartInfo autostart_info); + bool IsAutoStartEnabled(); + std::string GetExePath(); + +private: + void InitAutoStart(std::string name); + std::string GenerateDesktopFile(AutoStartInfo autostart_info); +}; diff --git a/AutoStart/AutoStart-MacOS.cpp b/AutoStart/AutoStart-MacOS.cpp new file mode 100644 index 00000000..32bad731 --- /dev/null +++ b/AutoStart/AutoStart-MacOS.cpp @@ -0,0 +1,181 @@ +#include "AutoStart-MacOS.h" +#include "LogManager.h" +#include "filesystem.h" + +#include +#include +#include + +/*-----------------------------------------------------*\ +| MacOS AutoStart Implementation | +| Public Methods | +\*-----------------------------------------------------*/ + +AutoStart::AutoStart(std::string name) +{ + InitAutoStart(name); +} + +bool AutoStart::DisableAutoStart() +{ + std::error_code autostart_file_remove_errcode; + bool success = false; + + // /*-------------------------------------------------*\ + // | Check if the filename is valid | + // \*-------------------------------------------------*/ + // if(autostart_file != "") + // { + // /*---------------------------------------------*\ + // | If file doesn't exist, disable is successful | + // \*---------------------------------------------*/ + // if(!filesystem::exists(autostart_file)) + // { + // success = true; + // } + // /*---------------------------------------------*\ + // | Otherwise, delete the file | + // \*---------------------------------------------*/ + // else + // { + // success = filesystem::remove(autostart_file, autostart_file_remove_errcode); + + // if(!success) + // { + // LOG_ERROR("[AutoStart] An error occurred removing the auto start file."); + // } + // } + // } + // else + // { + // LOG_ERROR("Could not establish correct autostart file path."); + // } + + return(success); +} + +bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info) +{ + bool success = false; + + // /*-------------------------------------------------*\ + // | Check if the filename is valid | + // \*-------------------------------------------------*/ + // if(autostart_file != "") + // { + // std::string desktop_file = GenerateDesktopFile(autostart_info); + // std::ofstream autostart_file_stream(autostart_file, std::ios::out | std::ios::trunc); + + // /*---------------------------------------------*\ + // | Error out if the file could not be opened | + // \*---------------------------------------------*/ + // if(!autostart_file_stream) + // { + // LOG_ERROR("Could not open %s for writing.", autostart_file.c_str()); + // success = false; + // } + // /*---------------------------------------------*\ + // | Otherwise, write the file | + // \*---------------------------------------------*/ + // else + // { + // autostart_file_stream << desktop_file; + // autostart_file_stream.close(); + // success = !autostart_file_stream.fail(); + + // if (!success) + // { + // LOG_ERROR("An error occurred writing the auto start file."); + // } + // } + // } + // else + // { + // LOG_ERROR("Could not establish correct autostart file path."); + // } + + return(success); +} + +bool AutoStart::IsAutoStartEnabled() +{ + /*-------------------------------------------------*\ + | Check if the filename is valid | + \*-------------------------------------------------*/ + if(autostart_file != "") + { + return(filesystem::exists(autostart_file)); + } + else + { + return(false); + } +} + +std::string AutoStart::GetExePath() +{ + /*-------------------------------------------------*\ + | Create the OpenRGB executable path | + \*-------------------------------------------------*/ + // char exepath[ PATH_MAX ]; + + // ssize_t count = readlink("/proc/self/exe", exepath, PATH_MAX); + + // return(std::string(exepath, (count > 0) ? count : 0)); + + return(""); +} + +/*-----------------------------------------------------*\ +| MacOS AutoStart Implementation | +| Private Methods | +\*-----------------------------------------------------*/ + +void AutoStart::InitAutoStart(std::string name) +{ + std::string autostart_dir; + + autostart_name = name; + + // /*-------------------------------------------------*\ + // | Get home and config paths | + // \*-------------------------------------------------*/ + // const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + // const char *home = getenv("HOME"); + + // /*-------------------------------------------------*\ + // | Determine where the autostart .desktop files are | + // | kept | + // \*-------------------------------------------------*/ + // if(xdg_config_home != NULL) + // { + // autostart_dir = xdg_config_home; + // autostart_dir = autostart_dir + "/autostart/"; + // } + // else if(home != NULL) + // { + // autostart_dir = home; + // autostart_dir = autostart_dir + "/.config/autostart/"; + // } + + // /*-------------------------------------------------*\ + // | Check if the filename is valid | + // \*-------------------------------------------------*/ + // if(autostart_dir != "") + // { + // std::error_code ec; + + // bool success = true; + + // if(!filesystem::exists(autostart_dir)) + // { + // success = filesystem::create_directories(autostart_dir, ec); + // } + + // if(success) + // { + // autostart_file = autostart_dir + autostart_name + ".desktop"; + // } + // } +} + diff --git a/AutoStart/AutoStart-MacOS.h b/AutoStart/AutoStart-MacOS.h new file mode 100644 index 00000000..1c55e634 --- /dev/null +++ b/AutoStart/AutoStart-MacOS.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "AutoStart.h" + +class AutoStart: public AutoStartInterface +{ +public: + AutoStart(std::string name); + + bool DisableAutoStart(); + bool EnableAutoStart(AutoStartInfo autostart_info); + bool IsAutoStartEnabled(); + std::string GetExePath(); + +private: + void InitAutoStart(std::string name); +}; diff --git a/AutoStart/AutoStart-Windows.cpp b/AutoStart/AutoStart-Windows.cpp new file mode 100644 index 00000000..3f2625c5 --- /dev/null +++ b/AutoStart/AutoStart-Windows.cpp @@ -0,0 +1,204 @@ +#include "AutoStart-Windows.h" +#include "LogManager.h" +#include "filesystem.h" + +#include +#include + +#include "windows.h" +#include + +/*-----------------------------------------------------*\ +| Windows AutoStart Implementation | +| Public Methods | +\*-----------------------------------------------------*/ + +AutoStart::AutoStart(std::string name) +{ + InitAutoStart(name); +} + +bool AutoStart::DisableAutoStart() +{ + std::error_code autostart_file_remove_errcode; + bool success = false; + + /*-------------------------------------------------*\ + | Check if the filename is valid | + \*-------------------------------------------------*/ + if(autostart_file != "") + { + /*---------------------------------------------*\ + | If file doesn't exist, disable is successful | + \*---------------------------------------------*/ + if(!filesystem::exists(autostart_file)) + { + success = true; + } + /*---------------------------------------------*\ + | Otherwise, delete the file | + \*---------------------------------------------*/ + else + { + success = filesystem::remove(autostart_file, autostart_file_remove_errcode); + + if(!success) + { + LOG_ERROR("An error occurred removing the auto start file."); + } + } + } + else + { + LOG_ERROR("Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info) +{ + bool success = false; + + /*-------------------------------------------------*\ + | Check if the filename is valid | + \*-------------------------------------------------*/ + if(autostart_file != "") + { + bool weInitialised = false; + HRESULT result; + IShellLinkW* shellLink = NULL; + + std::wstring exepathw = utf8_decode(autostart_info.path); + std::wstring argumentsw = utf8_decode(autostart_info.args); + std::wstring startupfilepathw = utf8_decode(autostart_file); + std::wstring descriptionw = utf8_decode(autostart_info.desc); + std::wstring iconw = utf8_decode(autostart_info.path); + + result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink); + + /*---------------------------------------------*\ + | If not initialized, initialize | + \*---------------------------------------------*/ + if(result == CO_E_NOTINITIALIZED) + { + weInitialised = true; + CoInitializeEx(NULL, 2u); + result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink); + } + + /*---------------------------------------------*\ + | If successfully initialized, save a shortcut | + | from the AutoStart parameters | + \*---------------------------------------------*/ + if(SUCCEEDED(result)) + { + shellLink->SetPath(exepathw.c_str()); + shellLink->SetArguments(argumentsw.c_str()); + shellLink->SetDescription(descriptionw.c_str()); + shellLink->SetIconLocation(iconw.c_str(), 0); + + IPersistFile* persistFile; + + result = shellLink->QueryInterface(IID_IPersistFile, (void**)&persistFile); + + if(SUCCEEDED(result)) + { + result = persistFile->Save(startupfilepathw.c_str(), TRUE); + success = SUCCEEDED(result); + persistFile->Release(); + } + + shellLink->Release(); + } + + /*---------------------------------------------*\ + | Uninitialize when done | + \*---------------------------------------------*/ + if(weInitialised) + { + CoUninitialize(); + } + } + else + { + LOG_ERROR("Could not establish correct autostart file path."); + } + + return success; +} + +bool AutoStart::IsAutoStartEnabled() +{ + /*-------------------------------------------------*\ + | Check if the filename is valid | + \*-------------------------------------------------*/ + if(autostart_file != "") + { + return(filesystem::exists(autostart_file)); + } + else + { + return(false); + } +} + +std::string AutoStart::GetExePath() +{ + /*-------------------------------------------------*\ + | Create the OpenRGB executable path | + \*-------------------------------------------------*/ + char exepath[MAX_PATH] = ""; + + DWORD count = GetModuleFileNameA(NULL, exepath, MAX_PATH); + + return(std::string(exepath, (count > 0) ? count : 0)); +} + +/*-----------------------------------------------------*\ +| Windows AutoStart Implementation | +| Private Methods | +\*-----------------------------------------------------*/ + +void AutoStart::InitAutoStart(std::string name) +{ + char startMenuPath[MAX_PATH]; + + autostart_name = name; + + /*-------------------------------------------------*\ + | Get startup applications path | + \*-------------------------------------------------*/ + HRESULT result = SHGetFolderPathA(NULL, CSIDL_PROGRAMS, NULL, 0, startMenuPath); + + if(SUCCEEDED(result)) + { + autostart_file = std::string(startMenuPath); + + autostart_file += "\\Startup\\" + autostart_name + ".lnk"; + } + else + { + autostart_file.clear(); + } +} + +/*-----------------------------------------------------*\ +| Convert an UTF8 string to a wide Unicode String | +| (from wmi.cpp) | +\*-----------------------------------------------------*/ +std::wstring AutoStart::utf8_decode(const std::string& str) +{ + if(str.empty()) + { + return std::wstring(); + } + + int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int) str.size(), nullptr, 0); + + std::wstring wstrTo(size_needed, 0); + + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int) str.size(), &wstrTo[0], size_needed); + + return(wstrTo); +} diff --git a/AutoStart/AutoStart-Windows.h b/AutoStart/AutoStart-Windows.h new file mode 100644 index 00000000..017ec59a --- /dev/null +++ b/AutoStart/AutoStart-Windows.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "AutoStart.h" + +class AutoStart: public AutoStartInterface +{ +public: + AutoStart(std::string name); + + bool DisableAutoStart(); + bool EnableAutoStart(AutoStartInfo autostart_info); + bool IsAutoStartEnabled(); + std::string GetExePath(); + +private: + void InitAutoStart(std::string name); + std::wstring utf8_decode(const std::string& str); +}; diff --git a/AutoStart/AutoStart.cpp b/AutoStart/AutoStart.cpp new file mode 100644 index 00000000..a483e29d --- /dev/null +++ b/AutoStart/AutoStart.cpp @@ -0,0 +1,16 @@ +#include "AutoStart.h" + +/*-----------------------------------------------------*\ +| Common AutoStart Implementation | +| Public Methods | +\*-----------------------------------------------------*/ + +std::string AutoStartInterface::GetAutoStartFile() +{ + return(autostart_file); +} + +std::string AutoStartInterface::GetAutoStartName() +{ + return(autostart_name); +} diff --git a/AutoStart/AutoStart.h b/AutoStart/AutoStart.h new file mode 100644 index 00000000..f2dda1fb --- /dev/null +++ b/AutoStart/AutoStart.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +struct AutoStartInfo +{ + std::string path; + std::string args; + std::string desc; + std::string icon; + std::string category; +}; + +class AutoStartInterface +{ +public: + virtual bool DisableAutoStart() = 0; + virtual bool EnableAutoStart(AutoStartInfo autostart_info) = 0; + virtual bool IsAutoStartEnabled() = 0; + virtual std::string GetExePath() = 0; + + std::string GetAutoStartFile(); + std::string GetAutoStartName(); + +protected: + std::string autostart_file; + std::string autostart_name; +}; + +#ifdef _WIN32 +#include "AutoStart-Windows.h" +#endif + +#ifdef __linux__ +#include "AutoStart-Linux.h" +#endif + +#ifdef __APPLE__ +#include "AutoStart-MacOS.h" +#endif \ No newline at end of file diff --git a/OpenRGB.pro b/OpenRGB.pro index 4283ee71..c786b8d6 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -60,6 +60,7 @@ INCLUDEPATH += pci_ids/ \ serial_port/ \ super_io/ \ + AutoStart/ \ Controllers/AMDWraithPrismController/ \ Controllers/ASRockPolychromeSMBusController/ \ Controllers/ASRockPolychromeUSBController/ \ @@ -170,6 +171,7 @@ HEADERS += serial_port/find_usb_serial_port.h \ serial_port/serial_port.h \ super_io/super_io.h \ + AutoStart/AutoStart.h \ Controllers/AMDWraithPrismController/AMDWraithPrismController.h \ Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.h \ Controllers/AnnePro2Controller/AnnePro2Controller.h \ @@ -458,6 +460,7 @@ SOURCES += qt/hsv.cpp \ serial_port/serial_port.cpp \ super_io/super_io.cpp \ + AutoStart/AutoStart.cpp \ Controllers/AMDWraithPrismController/AMDWraithPrismController.cpp \ Controllers/AMDWraithPrismController/AMDWraithPrismControllerDetect.cpp \ Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.cpp \ @@ -875,6 +878,7 @@ win32:SOURCES += serial_port/find_usb_serial_port_win.cpp \ wmi/wmi.cpp \ wmi/acpiwmi.cpp \ + AutoStart/AutoStart-Windows.cpp \ Controllers/AsusTUFLaptopController/AsusTUFLaptopWMIDetect.cpp \ Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptopWMI.cpp \ Controllers/OpenRazerController/OpenRazerWindowsDetect.cpp \ @@ -892,6 +896,7 @@ win32:HEADERS += i2c_smbus/i2c_smbus_piix4.h \ wmi/wmi.h \ wmi/acpiwmi.h \ + AutoStart/AutoStart-Windows.h \ Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptopWMI.h \ Controllers/OpenRazerController/RGBController_OpenRazerWindows.h \ @@ -980,6 +985,7 @@ unix:!macx { HEADERS += \ i2c_smbus/i2c_smbus_linux.h \ + AutoStart/AutoStart-Linux.h \ Controllers/FaustusController/RGBController_Faustus.h \ Controllers/LinuxLEDController/LinuxLEDController.h \ Controllers/LinuxLEDController/RGBController_LinuxLED.h \ @@ -1019,6 +1025,7 @@ unix:!macx { dependencies/hueplusplus-1.0.0/src/LinHttpHandler.cpp \ i2c_smbus/i2c_smbus_linux.cpp \ serial_port/find_usb_serial_port_linux.cpp \ + AutoStart/AutoStart-Linux.cpp \ Controllers/FaustusController/RGBController_Faustus.cpp \ Controllers/LinuxLEDController/LinuxLEDController.cpp \ Controllers/LinuxLEDController/LinuxLEDControllerDetect.cpp \ @@ -1065,9 +1072,13 @@ macx { DEFINES += \ USE_HID_USAGE \ + HEADERS += \ + AutoStart/AutoStart-MacOS.h \ + SOURCES += \ dependencies/hueplusplus-1.0.0/src/LinHttpHandler.cpp \ serial_port/find_usb_serial_port_linux.cpp \ + AutoStart/AutoStart-MacOS.cpp \ LIBS += \ -lusb-1.0 \ diff --git a/cli.cpp b/cli.cpp index 1b43915e..9a4003f6 100644 --- a/cli.cpp +++ b/cli.cpp @@ -4,6 +4,7 @@ #include #include #include "OpenRGB.h" +#include "AutoStart.h" #include "ProfileManager.h" #include "ResourceManager.h" #include "RGBController.h" @@ -402,6 +403,9 @@ void OptionHelp() 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; } @@ -833,7 +837,9 @@ int ProcessOptions(int argc, char *argv[], Options *options, std::vectorGetSettingsManager()->GetSettings("UserInterface"); @@ -56,6 +57,68 @@ OpenRGBSettingsPage::OpenRGBSettingsPage(QWidget *parent) : ui->CheckboxSaveGeometry->setChecked(ui_settings["geometry"]["save_on_exit"]); } } + + /*---------------------------------------------------------*\ + | Load AutoStart settings | + \*---------------------------------------------------------*/ + ProfileManager* profile_manager = ResourceManager::get()->GetProfileManager(); + + /*---------------------------------------------------------*\ + | Load profiles into combo box | + \*---------------------------------------------------------*/ + if(profile_manager != NULL) + { + ui->ComboBoxAutoStartProfile->clear(); + + for(std::size_t profile_index = 0; profile_index < profile_manager->profile_list.size(); profile_index++) + { + ui->ComboBoxAutoStartProfile->addItem(profile_manager->profile_list[profile_index].c_str()); + } + } + + /*---------------------------------------------------------*\ + | Make sure autostart settings exist | + \*---------------------------------------------------------*/ + CreateAutoStartSettings(); + + /*---------------------------------------------------------*\ + | Initialise UI to current settings or defaults | + \*---------------------------------------------------------*/ + json autostart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("AutoStart"); + + RemediateAutoStartProfile(autostart_settings); + + /*---------------------------------------------------------*\ + | Text boxes | + \*---------------------------------------------------------*/ + ui->TextServerPort->setText(QString::fromStdString(autostart_settings["port"])); + ui->TextServerPort->setEnabled(autostart_settings["setserverport"]); + + ui->TextClientHost->setText(QString::fromStdString(autostart_settings["client"])); + ui->TextClientHost->setEnabled(autostart_settings["setclient"]); + + ui->TextCustomArgs->setText(QString::fromStdString(autostart_settings["custom"])); + ui->TextCustomArgs->setEnabled(autostart_settings["setcustom"]); + + /*---------------------------------------------------------*\ + | Checkboxes | + \*---------------------------------------------------------*/ + ui->CheckboxAutoStart->setChecked(autostart_settings["enabled"]); + SetAutoStartVisibility(autostart_settings["enabled"]); + + ui->CheckboxAutoStartMinimized->setChecked(autostart_settings["setminimized"]); + ui->CheckboxAutoStartClient->setChecked(autostart_settings["setclient"]); + ui->CheckboxAutoStartServer->setChecked(autostart_settings["setserver"]); + ui->CheckboxAutoStartSetServerPort->setChecked(autostart_settings["setserverport"]); + ui->CheckboxAutoStartCustom->setChecked(autostart_settings["setcustom"]); + + ui->AutoStartStatusLabel->hide(); + autostart_initialized = true; + +#ifdef __APPLE__ + SetAutoStartVisibility(false); + ui->CheckboxAutoStart->hide(); +#endif } OpenRGBSettingsPage::~OpenRGBSettingsPage() @@ -98,6 +161,329 @@ void Ui::OpenRGBSettingsPage::on_CheckboxSaveGeometry_clicked() SaveSettings(); } +void Ui::OpenRGBSettingsPage::on_CheckboxAutoStart_clicked() +{ + if(autostart_initialized) + { + json autostart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("AutoStart"); + autostart_settings["enabled"] = ui->CheckboxAutoStart->isChecked(); + + if(autostart_settings["enabled"]) + { + RemediateAutoStartProfile(autostart_settings); + } + + ResourceManager::get()->GetSettingsManager()->SetSettings("AutoStart", autostart_settings); + SaveSettings(); + + SetAutoStartVisibility(autostart_settings["enabled"]); + + ConfigureAutoStart(); + } +} + +void Ui::OpenRGBSettingsPage::on_CheckboxAutoStartMinimized_clicked() +{ + SaveAutoStartSetting("setminimized", ui->CheckboxAutoStartMinimized->isChecked()); +} + +void Ui::OpenRGBSettingsPage::on_CheckboxAutoStartServer_clicked() +{ + SaveAutoStartSetting("setserver", ui->CheckboxAutoStartServer->isChecked()); +} + +void Ui::OpenRGBSettingsPage::on_CheckboxAutoStartSetServerPort_clicked() +{ + SaveAutoStartSetting("setserverport", ui->CheckboxAutoStartSetServerPort->isChecked()); + ui->TextServerPort->setEnabled(ui->CheckboxAutoStartSetServerPort->isChecked()); +} + +void Ui::OpenRGBSettingsPage::on_CheckboxAutoStartClient_clicked() +{ + SaveAutoStartSetting("setclient", ui->CheckboxAutoStartClient->isChecked()); + ui->TextClientHost->setEnabled(ui->CheckboxAutoStartClient->isChecked()); +} + +void Ui::OpenRGBSettingsPage::on_CheckboxAutoStartProfile_clicked() +{ + SaveAutoStartSetting("setprofile", ui->CheckboxAutoStartProfile->isChecked()); + ui->ComboBoxAutoStartProfile->setEnabled(ui->CheckboxAutoStartProfile->isChecked()); +} + +void Ui::OpenRGBSettingsPage::on_CheckboxAutoStartCustom_clicked() +{ + SaveAutoStartSetting("setcustom", ui->CheckboxAutoStartCustom->isChecked()); + ui->TextCustomArgs->setEnabled(ui->CheckboxAutoStartCustom->isChecked()); +} + +void Ui::OpenRGBSettingsPage::on_TextServerPort_textChanged(QString port) +{ + SaveAutoStartSetting("port", port); +} + +void Ui::OpenRGBSettingsPage::on_TextClientHost_textChanged(QString client) +{ + SaveAutoStartSetting("client", client); +} + +void Ui::OpenRGBSettingsPage::on_TextCustomArgs_textChanged(QString custom) +{ + SaveAutoStartSetting("custom", custom); +} + +void Ui::OpenRGBSettingsPage::on_ComboBoxAutoStartProfile_currentTextChanged(const QString profile) +{ + SaveAutoStartSetting("profile", profile); +} + +void OpenRGBSettingsPage::SaveAutoStartSetting(std::string name, QString value) +{ + if(autostart_initialized) + { + json autostart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("AutoStart"); + autostart_settings[name] = value.toStdString(); + ResourceManager::get()->GetSettingsManager()->SetSettings("AutoStart", autostart_settings); + SaveSettings(); + + ConfigureAutoStart(); + } +} + +void OpenRGBSettingsPage::SaveAutoStartSetting(std::string name, bool value) +{ + if(autostart_initialized) + { + json autostart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("AutoStart"); + autostart_settings[name] = value; + ResourceManager::get()->GetSettingsManager()->SetSettings("AutoStart", autostart_settings); + SaveSettings(); + + ConfigureAutoStart(); + } +} + +void OpenRGBSettingsPage::SetAutoStartVisibility(bool visible) +{ + if (!visible) + { + ui->ComboBoxAutoStartProfile->hide(); + ui->CheckboxAutoStartClient->hide(); + ui->CheckboxAutoStartCustom->hide(); + ui->CheckboxAutoStartMinimized->hide(); + ui->CheckboxAutoStartProfile->hide(); + ui->CheckboxAutoStartServer->hide(); + ui->CheckboxAutoStartSetServerPort->hide(); + ui->TextCustomArgs->hide(); + ui->TextClientHost->hide(); + ui->TextServerPort->hide(); + ui->AutoStartLabel->hide(); + ui->AutoStartStatusLabel->hide(); + } + else + { + ui->ComboBoxAutoStartProfile->show(); + ui->CheckboxAutoStartClient->show(); + ui->CheckboxAutoStartCustom->show(); + ui->CheckboxAutoStartMinimized->show(); + ui->CheckboxAutoStartProfile->show(); + ui->CheckboxAutoStartServer->show(); + ui->CheckboxAutoStartSetServerPort->show(); + ui->TextCustomArgs->show(); + ui->TextClientHost->show(); + ui->TextServerPort->show(); + ui->AutoStartLabel->show(); + ui->AutoStartStatusLabel->show(); + } +} + +void OpenRGBSettingsPage::ConfigureAutoStart() +{ + std::map> autostart_map = { + {"setminimized", {"--startminimized","",false}}, + {"setserver", {"--server","",false}}, + {"setserverport", {"--server-port","port",false}}, + {"setclient", {"--client","client",false}}, + {"setprofile", {"--profile","profile",true}}, + {"setcustom", {"","custom",false}}, + }; + + json autostart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("AutoStart"); + + AutoStart auto_start("OpenRGB"); + + if (!autostart_settings["enabled"]) + { + if (!auto_start.DisableAutoStart()) + { + ui->AutoStartStatusLabel->setText("A problem occurred disabling Start At Login."); + ui->AutoStartStatusLabel->show(); + SetAutoStartVisibility(true); + } + else + { + ui->AutoStartStatusLabel->hide(); + } + } + else + { + std::string desc = "OpenRGB "; + desc += VERSION_STRING; + desc += ", for controlling RGB lighting."; + + std::string arguments = ""; + + for(std::pair>& x: autostart_map) + { + std::string argumentsetting = x.first; + std::string argument = std::get<0>(x.second); + std::string argumentvaluename = std::get<1>(x.second); + bool argumentquoted = std::get<2>(x.second); + + if (autostart_settings[x.first]) + { + if (argument != "") + { + if (arguments != "") + { + arguments += " "; + } + arguments += argument; + } + if (argumentvaluename != "") + { + std::string argumentvalue = autostart_settings[argumentvaluename]; + if (argumentvalue != "") + { + if (arguments != "") + { + arguments += " "; + } + if (argumentquoted) + { + arguments += "\"" + argumentvalue + "\""; + } + else + { + arguments += argumentvalue; + } + } + } + } + } + + AutoStartInfo auto_start_info; + + auto_start_info.args = arguments; + auto_start_info.category = "Utility;"; + auto_start_info.desc = desc; + auto_start_info.icon = "OpenRGB"; + auto_start_info.path = auto_start.GetExePath(); + + if (!auto_start.EnableAutoStart(auto_start_info)) + { + ui->AutoStartStatusLabel->setText("A problem occurred enabling Start At Login."); + ui->AutoStartStatusLabel->show(); + SetAutoStartVisibility(true); + } + else + { + ui->AutoStartStatusLabel->hide(); + } + } +} + +void OpenRGBSettingsPage::CreateAutoStartSettings() +{ + std::map autostart_default_map_string = { + {"custom", ""}, + {"port", "6742"}, + {"client","localhost:6742"}, + {"profile",ui->ComboBoxAutoStartProfile->count() > 0 ? ui->ComboBoxAutoStartProfile->itemText(0).toStdString(): ""} + }; + + std::map autostart_default_map_bool = { + {"enabled", false}, + {"setminimized", false}, + {"setclient", false}, + {"setserver", false}, + {"setserverport", false}, + {"setcustom", false}, + {"setprofile", false}, + }; + + json autostart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("AutoStart"); + + for(std::pair& x: autostart_default_map_bool) + { + if(!autostart_settings.contains(x.first)) + { + autostart_settings[x.first] = x.second; + } + } + + for(std::pair& x: autostart_default_map_string) + { + if(!autostart_settings.contains(x.first)) + { + autostart_settings[x.first] = x.second; + } + } + + ResourceManager::get()->GetSettingsManager()->SetSettings("AutoStart", autostart_settings); +} + +void OpenRGBSettingsPage::RemediateAutoStartProfile(json &autostart_settings) +{ + /*---------------------------------------------------------*\ + | If there are no profiles then we disable the UI for | + | profiles and if AutoStart is enabled then we force | + | disable setprofile | + \*---------------------------------------------------------*/ + if(ui->ComboBoxAutoStartProfile->count() == 0) + { + ui->CheckboxAutoStartProfile->setEnabled(false); + ui->ComboBoxAutoStartProfile->setEnabled(false); + + autostart_settings["profile"] = ""; + + if(autostart_settings["enabled"]) + { + autostart_settings["setprofile"] = false; + + ResourceManager::get()->GetSettingsManager()->SetSettings("AutoStart", autostart_settings); + + ConfigureAutoStart(); + SaveSettings(); + } + } + + /*---------------------------------------------------------*\ + | Else If the profile we want doesn't exist then we force | + | it to a profile which exists and if AutoStart is enabled | + | then we force disable setprofile | + \*---------------------------------------------------------*/ + else if(autostart_settings["profile"] == "" || + (autostart_settings["profile"] != "" && + ui->ComboBoxAutoStartProfile->findText(QString::fromStdString(autostart_settings["profile"])) == -1)) + { + autostart_settings["profile"] = ui->ComboBoxAutoStartProfile->itemText(0).toStdString(); + + if(autostart_settings["enabled"]) + { + autostart_settings["setprofile"] = false; + + ResourceManager::get()->GetSettingsManager()->SetSettings("AutoStart", autostart_settings); + + ConfigureAutoStart(); + SaveSettings(); + } + } + + ui->ComboBoxAutoStartProfile->setCurrentText(QString::fromStdString(autostart_settings["profile"])); + ui->ComboBoxAutoStartProfile->setEnabled(autostart_settings["setprofile"]); + ui->CheckboxAutoStartProfile->setChecked(autostart_settings["setprofile"]); +} + void OpenRGBSettingsPage::SaveSettings() { ResourceManager::get()->GetSettingsManager()->SaveSettings(); diff --git a/qt/OpenRGBSettingsPage.h b/qt/OpenRGBSettingsPage.h index 9705174a..ebd5ac07 100644 --- a/qt/OpenRGBSettingsPage.h +++ b/qt/OpenRGBSettingsPage.h @@ -3,6 +3,7 @@ #include "ui_OpenRGBSettingsPage.h" #include +#include "ResourceManager.h" namespace Ui { class OpenRGBSettingsPage; @@ -20,13 +21,32 @@ private: Ui::OpenRGBSettingsPageUi *ui; void SaveSettings(); + void CreateAutoStartSettings(); + void ConfigureAutoStart(); + void RemediateAutoStartProfile(json &autostart_settings); + void SetAutoStartVisibility(bool visible); + void SaveAutoStartSetting(std::string name, QString value); + void SaveAutoStartSetting(std::string name, bool value); + bool theme_initialized = false; + bool autostart_initialized = false; private slots: void on_ComboBoxTheme_currentTextChanged(const QString); void on_CheckboxMinimizeOnClose_clicked(); void on_CheckboxLoadGeometry_clicked(); void on_CheckboxSaveGeometry_clicked(); + void on_CheckboxAutoStart_clicked(); + void on_CheckboxAutoStartMinimized_clicked(); + void on_CheckboxAutoStartServer_clicked(); + void on_CheckboxAutoStartClient_clicked(); + void on_CheckboxAutoStartProfile_clicked(); + void on_TextServerPort_textChanged(const QString); + void on_TextClientHost_textChanged(const QString); + void on_TextCustomArgs_textChanged(const QString); + void on_ComboBoxAutoStartProfile_currentTextChanged(const QString); + void on_CheckboxAutoStartSetServerPort_clicked(); + void on_CheckboxAutoStartCustom_clicked(); }; #endif // OPENRGBSETTINGSPAGE_H diff --git a/qt/OpenRGBSettingsPage.ui b/qt/OpenRGBSettingsPage.ui index 67c30c74..707f7e24 100644 --- a/qt/OpenRGBSettingsPage.ui +++ b/qt/OpenRGBSettingsPage.ui @@ -7,18 +7,28 @@ 0 0 432 - 144 + 500 Form - + + + + Set Server Port + + + + Qt::Vertical + + QSizePolicy::MinimumExpanding + 20 @@ -27,37 +37,115 @@ - - - - Load Window Geometry - - - - - - - - - Minimize On Close - - - - Theme - + + + + + + + + + + Minimize On Close + + + + + + + Start Server + + + + + + + Start At Login + + + + + + + 90000 + + + + + + + + + + + + + Load Profile + + + + + + + Start Client + + + + + + + Start Minimized + + + + Save Geometry On Close + + + + Start at Login Status + + + + + + + Start At Login Settings: + + + + + + + + + + Load Window Geometry + + + + + + + Custom Arguments + + + + + +