diff --git a/assets/webconfig/content/conf_leds.html b/assets/webconfig/content/conf_leds.html index 5acbd9d0..240b06fc 100755 --- a/assets/webconfig/content/conf_leds.html +++ b/assets/webconfig/content/conf_leds.html @@ -37,7 +37,13 @@
-

Information

In case your LEDs do not work, check here for errors:
+
+

Information

+
+ In case your LEDs do not work, check here for errors: + +
+
+ diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index b1a3ed1d..4a8d712f 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -57,6 +57,7 @@ "conf_leds_device_intro": "Hyperion supports a lot of controllers to transmit data to your target device. Select a LED controller out of the sorted list and configure it. We have chosen the best default settings for each device.", "conf_leds_error_hwled_gt_layout": "The hardware LED count ($1) is greater than LEDs configured via layout ($2),
$3 {{plural:$3|LED|LEDs}} will stay black if you continue.", "conf_leds_error_hwled_lt_layout": "The hardware LED count ($1) is less than LEDs configured via layout ($2).
The number of LEDs configured in the layout must not exceed the available LEDs", + "conf_leds_info_ws281x": "Hyperion must run with 'root' privileges for this controller type!", "conf_leds_layout_advanced": "Advanced Settings", "conf_leds_layout_blacklist_num_title": "Number of LEDs", "conf_leds_layout_blacklist_rule_title": "Blacklist rule", diff --git a/assets/webconfig/js/content_grabber.js b/assets/webconfig/js/content_grabber.js index 089837bb..75e149dd 100755 --- a/assets/webconfig/js/content_grabber.js +++ b/assets/webconfig/js/content_grabber.js @@ -755,7 +755,7 @@ $(document).ready(function () { updateJsonEditorSelection(conf_editor_video, 'root.grabberV4L2', 'available_devices', {}, enumVals, enumTitelVals, enumDefaultVal, addSelect, false); } - } + }; async function discoverInputSources(type, params) { const result = await requestInputSourcesDiscovery(type, params); diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index 814952c8..60dff310 100755 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -16,6 +16,8 @@ var devNET = ['atmoorb', 'cololight', 'fadecandy', 'philipshue', 'nanoleaf', 'ti var devSerial = ['adalight', 'dmx', 'atmo', 'sedu', 'tpm2', 'karate']; var devHID = ['hyperionusbasp', 'lightpack', 'paintpack', 'rawhid']; +var infoTextDefault = '' + $.i18n("conf_leds_device_info_log") + ' ' + $.i18n("main_menu_logging_token") + ''; + function round(number) { var factor = Math.pow(10, 4); var tempNumber = number * factor; @@ -664,6 +666,8 @@ $(document).ready(function () { conf_editor.getEditor("root.specificOptions").setValue(values_specific); }; + $("#info_container_text").html(infoTextDefault); + // change save button state based on validation result conf_editor.validate().length || window.readOnlyMode ? $('#btn_submit_controller').attr('disabled', true) : $('#btn_submit_controller').attr('disabled', false); @@ -727,6 +731,7 @@ $(document).ready(function () { case "sk9822": case "ws2812spi": case "piblaster": + case "ws281x": discover_device(ledType); hwLedCountDefault = 1; colorOrderDefault = "rgb"; @@ -1313,6 +1318,8 @@ var updateSelectList = function (ledType, discoveryInfo) { ledTypeGroup = "devRPiSPI"; } else if ($.inArray(ledType, devRPiGPIO) != -1) { ledTypeGroup = "devRPiGPIO"; + } else if ($.inArray(ledType, devRPiPWM) != -1) { + ledTypeGroup = "devRPiPWM"; } switch (ledTypeGroup) { @@ -1473,6 +1480,18 @@ var updateSelectList = function (ledType, discoveryInfo) { } } break; + case "devRPiPWM": + key = ledType; + + if (discoveryInfo.devices.length == 0) { + enumVals.push("NONE"); + enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none')); + $('#btn_submit_controller').attr('disabled', true); + showAllDeviceInputOptions(key, false); + + $("#info_container_text").html($.i18n("conf_leds_info_ws281x")); + } + break; default: } diff --git a/assets/webconfig/js/content_logging.js b/assets/webconfig/js/content_logging.js index 39876984..ce95e603 100644 --- a/assets/webconfig/js/content_logging.js +++ b/assets/webconfig/js/content_logging.js @@ -4,10 +4,11 @@ var isScroll = true; performTranslation(); requestLoggingStop(); -requestLoggingStart(); $(document).ready(function () { + requestLoggingStart(); + $('#conf_cont').append(createOptPanel('fa-reorder', $.i18n("edt_conf_log_heading_title"), 'editor_container', 'btn_submit')); if (window.showOptHelp) { $('#conf_cont').append(createHelpTable(window.schema.logger.properties, $.i18n("edt_conf_log_heading_title"))); diff --git a/assets/webconfig/js/ui_utils.js b/assets/webconfig/js/ui_utils.js index c1542af4..b6c72e4b 100644 --- a/assets/webconfig/js/ui_utils.js +++ b/assets/webconfig/js/ui_utils.js @@ -110,8 +110,16 @@ function loadContent(event, forceRefresh) { $("#page-content").off(); $("#page-content").load("/content/" + tag + ".html", function (response, status, xhr) { if (status == "error") { - $("#page-content").html('

' + $.i18n('info_404') + '

'); - removeOverlay(); + tag = 'dashboard'; + console.log("Could not find page:", prevTag, ", Redirecting to:", tag); + setStorage('lasthashtag', tag, true); + + $("#page-content").load("/content/" + tag + ".html", function (response, status, xhr) { + if (status == "error") { + $("#page-content").html('

' + encode_utf8(tag) + '
' + $.i18n('info_404') + '

'); + removeOverlay(); + } + }); } updateUiOnInstance(window.currentHyperionInstance); }); @@ -710,7 +718,11 @@ function hexToRgb(hex) { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) - } : null; + } : { + r: 0, + g: 0, + b: 0 + }; } /* @@ -1135,6 +1147,7 @@ function getSystemInfo() { info += '- UI Access: ' + storedAccess + '\n'; //info += '- Log lvl: ' + window.serverConfig.logger.level + '\n'; info += '- Avail Capt: ' + window.serverInfo.grabbers.available + '\n'; + info += '- Config path: ' + shy.rootPath + '\n'; info += '- Database: ' + (shy.readOnlyMode ? "ready-only" : "read/write") + '\n'; info += '\n'; @@ -1153,6 +1166,7 @@ function getSystemInfo() { info += '- CPU Hardware: ' + sys.cpuHardware + '\n'; info += '- Kernel: ' + sys.kernelType + ' (' + sys.kernelVersion + ' (WS: ' + sys.wordSize + '))\n'; + info += '- Root/Admin: ' + sys.isUserAdmin + '\n'; info += '- Qt Version: ' + sys.qtVersion + '\n'; info += '- Python Version: ' + sys.pyVersion + '\n'; info += '- Browser: ' + navigator.userAgent; diff --git a/include/hyperion/HyperionIManager.h b/include/hyperion/HyperionIManager.h index 85faecab..f24aee65 100644 --- a/include/hyperion/HyperionIManager.h +++ b/include/hyperion/HyperionIManager.h @@ -102,6 +102,8 @@ public slots: /// bool saveName(quint8 inst, const QString& name); + QString getRootPath() const { return _rootPath; } + signals: /// /// @brief Emits whenever the state of a instance changes according to enum instanceState diff --git a/include/utils/SysInfo.h b/include/utils/SysInfo.h index 22d80c27..38c3ae19 100644 --- a/include/utils/SysInfo.h +++ b/include/utils/SysInfo.h @@ -21,12 +21,16 @@ public: QString prettyName; QString hostName; QString domainName; + bool isUserAdmin; QString qtVersion; QString pyVersion; }; static HyperionSysInfo get(); + static bool isUserAdmin(); + static QString userName(); + private: SysInfo(); void getCPUInfo(); diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index f35533bd..c9496af9 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -328,6 +328,7 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject &, const QString &command, system["prettyName"] = data.prettyName; system["hostName"] = data.hostName; system["domainName"] = data.domainName; + system["isUserAdmin"] = data.isUserAdmin; system["qtVersion"] = data.qtVersion; system["pyVersion"] = data.pyVersion; info["system"] = system; @@ -338,6 +339,7 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject &, const QString &command, hyperion["gitremote"] = QString(HYPERION_GIT_REMOTE); hyperion["time"] = QString(__DATE__ " " __TIME__); hyperion["id"] = _authManager->getID(); + hyperion["rootPath"] = _instanceManager->getRootPath(); hyperion["readOnlyMode"] = _hyperion->getReadOnlyMode(); info["hyperion"] = hyperion; diff --git a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp index 2ba222f1..4276a6c4 100644 --- a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp @@ -1275,8 +1275,6 @@ QJsonArray V4L2Grabber::discover(const QJsonObject& params) device["device_name"] = _deviceProperties.value(it.key()).name; device["type"] = "v4l2"; - Debug( _log, "inputs size [%d], isEmpty [%d]", inputs.size(), inputs.isEmpty()); - for (auto input = inputs.begin(); input != inputs.end(); input++) { in["name"] = input.key(); diff --git a/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp b/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp index 6bff67f7..5bd407bd 100644 --- a/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp @@ -4,9 +4,15 @@ const ushort TPM2_DEFAULT_PORT = 65506; LedDeviceTpm2net::LedDeviceTpm2net(const QJsonObject &deviceConfig) : ProviderUdp(deviceConfig) + , _tpm2_buffer(nullptr) { } +LedDeviceTpm2net::~LedDeviceTpm2net() +{ + free (_tpm2_buffer); +} + LedDevice* LedDeviceTpm2net::construct(const QJsonObject &deviceConfig) { return new LedDeviceTpm2net(deviceConfig); @@ -25,6 +31,8 @@ bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig) _tpm2ByteCount = 3 * _ledCount; _tpm2TotalPackets = (_tpm2ByteCount / _tpm2_max) + ((_tpm2ByteCount % _tpm2_max) != 0); + _tpm2_buffer = (uint8_t*) malloc(_tpm2_max+7); + isInitOK = true; } return isInitOK; @@ -32,8 +40,6 @@ bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig) int LedDeviceTpm2net::write(const std::vector &ledValues) { - uint8_t * tpm2_buffer = (uint8_t*) malloc(_tpm2_max+7); - int retVal = 0; int _thisPacketBytes = 0; @@ -48,23 +54,22 @@ int LedDeviceTpm2net::write(const std::vector &ledValues) _thisPacketBytes = (_tpm2ByteCount - rawIdx < _tpm2_max) ? _tpm2ByteCount % _tpm2_max : _tpm2_max; // is this the last packet? ? ^^ last packet : ^^ earlier packets - tpm2_buffer[0] = 0x9c; // Packet start byte - tpm2_buffer[1] = 0xda; // Packet type Data frame - tpm2_buffer[2] = (_thisPacketBytes >> 8) & 0xff; // Frame size high - tpm2_buffer[3] = _thisPacketBytes & 0xff; // Frame size low - tpm2_buffer[4] = _tpm2ThisPacket++; // Packet Number - tpm2_buffer[5] = _tpm2TotalPackets; // Number of packets + _tpm2_buffer[0] = 0x9c; // Packet start byte + _tpm2_buffer[1] = 0xda; // Packet type Data frame + _tpm2_buffer[2] = (_thisPacketBytes >> 8) & 0xff; // Frame size high + _tpm2_buffer[3] = _thisPacketBytes & 0xff; // Frame size low + _tpm2_buffer[4] = _tpm2ThisPacket++; // Packet Number + _tpm2_buffer[5] = _tpm2TotalPackets; // Number of packets } - tpm2_buffer [6 + rawIdx%_tpm2_max] = rawdata[rawIdx]; + _tpm2_buffer [6 + rawIdx%_tpm2_max] = rawdata[rawIdx]; // is this the last byte of last packet || last byte of other packets if ( (rawIdx == _tpm2ByteCount-1) || (rawIdx %_tpm2_max == _tpm2_max-1) ) { - tpm2_buffer [6 + rawIdx%_tpm2_max +1] = 0x36; // Packet end byte - retVal &= writeBytes(_thisPacketBytes+7, tpm2_buffer); + _tpm2_buffer [6 + rawIdx%_tpm2_max +1] = 0x36; // Packet end byte + retVal &= writeBytes(_thisPacketBytes+7, _tpm2_buffer); } } - return retVal; } diff --git a/libsrc/leddevice/dev_net/LedDeviceTpm2net.h b/libsrc/leddevice/dev_net/LedDeviceTpm2net.h index 3d07ee17..299e7871 100644 --- a/libsrc/leddevice/dev_net/LedDeviceTpm2net.h +++ b/libsrc/leddevice/dev_net/LedDeviceTpm2net.h @@ -18,6 +18,11 @@ public: /// explicit LedDeviceTpm2net(const QJsonObject &deviceConfig); + /// + /// @brief Destructor of the TPM2 LED-device + /// + ~LedDeviceTpm2net() override; + /// /// @brief Constructs the LED-device /// @@ -48,6 +53,8 @@ private: int _tpm2ByteCount; int _tpm2TotalPackets; int _tpm2ThisPacket; + + uint8_t * _tpm2_buffer; }; #endif // LEDEVICETPM2NET_H diff --git a/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp b/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp index 35275f86..2b67173c 100644 --- a/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp +++ b/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.cpp @@ -1,4 +1,10 @@ #include "LedDeviceWS281x.h" +#include + +// Constants +namespace { +const bool verbose = false; +} //End of constants LedDeviceWS281x::LedDeviceWS281x(const QJsonObject &deviceConfig) : LedDevice(deviceConfig) @@ -75,19 +81,26 @@ int LedDeviceWS281x::open() int retval = -1; _isDeviceReady = false; - // Try to open the LedDevice - - ws2811_return_t rc = ws2811_init(&_led_string); - if ( rc != WS2811_SUCCESS ) + if (!SysInfo::isUserAdmin()) { - QString errortext = QString ("Failed to open. Error message: %1").arg( ws2811_get_return_t_str(rc) ); + QString errortext = QString ("Hyperion must run with \"root\" privileges for this device. Current user is: \"%1\"").arg(SysInfo::userName()); this->setInError( errortext ); } else { - // Everything is OK, device is ready - _isDeviceReady = true; - retval = 0; + // Try to open the LedDevice + ws2811_return_t rc = ws2811_init(&_led_string); + if ( rc != WS2811_SUCCESS ) + { + QString errortext = QString ("Failed to open. Error message: %1").arg( ws2811_get_return_t_str(rc) ); + this->setInError( errortext ); + } + else + { + // Everything is OK, device is ready + _isDeviceReady = true; + retval = 0; + } } return retval; } @@ -138,3 +151,22 @@ int LedDeviceWS281x::write(const std::vector &ledValues) return ws2811_render(&_led_string) ? -1 : 0; } + +QJsonObject LedDeviceWS281x::discover(const QJsonObject& /*params*/) +{ + QJsonObject devicesDiscovered; + devicesDiscovered.insert("ledDeviceType", _activeDeviceType); + + QJsonArray deviceList; + + if (SysInfo::isUserAdmin()) + { + //Indicate the general availability of the device, if hyperion is run under root + deviceList << QJsonObject ({{"found",true}}); + devicesDiscovered.insert("devices", deviceList); + } + + DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); + + return devicesDiscovered; +} diff --git a/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.h b/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.h index 033005d8..d5d4f123 100644 --- a/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.h +++ b/libsrc/leddevice/dev_rpi_pwm/LedDeviceWS281x.h @@ -29,6 +29,15 @@ public: /// static LedDevice* construct(const QJsonObject &deviceConfig); + /// + /// @brief Discover WS281x devices available (for configuration). + /// + /// @param[in] params Parameters used to overwrite discovery default behaviour + /// + /// @return A JSON structure holding a list of devices found + /// + QJsonObject discover(const QJsonObject& params) override; + protected: /// diff --git a/libsrc/leddevice/dev_spi/ProviderSpi.cpp b/libsrc/leddevice/dev_spi/ProviderSpi.cpp index 2cc46d50..f57c54f9 100644 --- a/libsrc/leddevice/dev_spi/ProviderSpi.cpp +++ b/libsrc/leddevice/dev_spi/ProviderSpi.cpp @@ -144,12 +144,14 @@ int ProviderSpi::writeBytes(unsigned size, const uint8_t * data) return -1; } + uint8_t * newdata {nullptr}; + _spi.tx_buf = __u64(data); _spi.len = __u32(size); if (_spiDataInvert) { - uint8_t * newdata = (uint8_t *)malloc(size); + newdata = static_cast(malloc(size)); for (unsigned i = 0; i #endif -static const QString SSDP_HYPERION_ST("urn:hyperion-project.org:device:basic:1"); +static const QString SSDP_IDENTIFIER("urn:hyperion-project.org:device:basic:1"); SSDPHandler::SSDPHandler(WebServer* webserver, quint16 flatBufPort, quint16 protoBufPort, quint16 jsonServerPort, quint16 sslPort, const QString& name, QObject * parent) : SSDPServer(parent) @@ -41,7 +41,7 @@ void SSDPHandler::initServer() // announce targets _deviceList.push_back("upnp:rootdevice"); _deviceList.push_back("uuid:"+_uuid); - _deviceList.push_back(SSDP_HYPERION_ST); + _deviceList.push_back(SSDP_IDENTIFIER); // prep server SSDPServer::initServer(); @@ -174,8 +174,8 @@ void SSDPHandler::handleMSearchRequest(const QString& target, const QString& mx, const auto respond = [=] () { // when searched for all devices / root devices / basic device if(target == "ssdp:all") - sendMSearchResponse(SSDP_HYPERION_ST, address, port); - else if(target == "upnp:rootdevice" || target == "urn:schemas-upnp-org:device:basic:1" || target == SSDP_HYPERION_ST) + sendMSearchResponse(SSDP_IDENTIFIER, address, port); + else if(target == "upnp:rootdevice" || target == "urn:schemas-upnp-org:device:basic:1" || target == SSDP_IDENTIFIER) sendMSearchResponse(target, address, port); }; @@ -213,8 +213,8 @@ QString SSDPHandler::getBaseAddress() const QString SSDPHandler::buildDesc() const { - /// %1 base url http://192.168.0.177:80/ - /// %2 friendly name Hyperion 2.0.0 (192.168.0.177) + /// %1 base url http://192.168.0.177:8090/ + /// %2 friendly name Hyperion (192.168.0.177) /// %3 modelNumber 2.0.0 /// %4 serialNumber / UDN (H ID) Fjsa723dD0.... /// %5 json port 19444 diff --git a/libsrc/utils/SysInfo.cpp b/libsrc/utils/SysInfo.cpp index 3136899d..4eebc02b 100644 --- a/libsrc/utils/SysInfo.cpp +++ b/libsrc/utils/SysInfo.cpp @@ -11,23 +11,28 @@ #include +#ifdef _WIN32 +#include +#endif + SysInfo* SysInfo::_instance = nullptr; SysInfo::SysInfo() : QObject() { - _sysinfo.kernelType = QSysInfo::kernelType(); - _sysinfo.kernelVersion = QSysInfo::kernelVersion(); - _sysinfo.architecture = QSysInfo::currentCpuArchitecture(); - _sysinfo.wordSize = QString::number(QSysInfo::WordSize); - _sysinfo.productType = QSysInfo::productType(); + _sysinfo.kernelType = QSysInfo::kernelType(); + _sysinfo.kernelVersion = QSysInfo::kernelVersion(); + _sysinfo.architecture = QSysInfo::currentCpuArchitecture(); + _sysinfo.wordSize = QString::number(QSysInfo::WordSize); + _sysinfo.productType = QSysInfo::productType(); _sysinfo.productVersion = QSysInfo::productVersion(); - _sysinfo.prettyName = QSysInfo::prettyProductName(); - _sysinfo.hostName = QHostInfo::localHostName(); - _sysinfo.domainName = QHostInfo::localDomainName(); + _sysinfo.prettyName = QSysInfo::prettyProductName(); + _sysinfo.hostName = QHostInfo::localHostName(); + _sysinfo.domainName = QHostInfo::localDomainName(); + _sysinfo.isUserAdmin = isUserAdmin(); getCPUInfo(); - _sysinfo.qtVersion = QT_VERSION_STR; - _sysinfo.pyVersion = PY_VERSION; + _sysinfo.qtVersion = QT_VERSION_STR; + _sysinfo.pyVersion = PY_VERSION; } SysInfo::HyperionSysInfo SysInfo::get() @@ -41,44 +46,95 @@ SysInfo::HyperionSysInfo SysInfo::get() void SysInfo::getCPUInfo() { QString cpuInfo; - if( FileUtils::readFile("/proc/cpuinfo", cpuInfo, Logger::getInstance("DAEMON"), true) ) + if (FileUtils::readFile("/proc/cpuinfo", cpuInfo, Logger::getInstance("DAEMON"), true)) { - QRegularExpression regEx ("^model\\s*:\\s(.*)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::MultilineOption); + QRegularExpression regEx("^model\\s*:\\s(.*)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::MultilineOption); QRegularExpressionMatch match; match = regEx.match(cpuInfo); - if ( match.hasMatch() ) + if (match.hasMatch()) { _sysinfo.cpuModelType = match.captured(1); } regEx.setPattern("^model name\\s*:\\s(.*)"); match = regEx.match(cpuInfo); - if ( match.hasMatch() ) + if (match.hasMatch()) { _sysinfo.cpuModelName = match.captured(1); } regEx.setPattern("^hardware\\s*:\\s(.*)"); match = regEx.match(cpuInfo); - if ( match.hasMatch() ) + if (match.hasMatch()) { _sysinfo.cpuHardware = match.captured(1); } regEx.setPattern("^revision\\s*:\\s(.*)"); match = regEx.match(cpuInfo); - if ( match.hasMatch() ) + if (match.hasMatch()) { _sysinfo.cpuRevision = match.captured(1); } regEx.setPattern("^revision\\s*:\\s(.*)"); match = regEx.match(cpuInfo); - if ( match.hasMatch() ) + if (match.hasMatch()) { _sysinfo.cpuRevision = match.captured(1); } } } +QString SysInfo::userName() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + #if defined (WIN32) + return qEnvironmentVariable("USERNAME"); + #else + return qEnvironmentVariable("USER"); + #endif +#else + #if defined (WIN32) + return getenv("USERNAME"); + #else + return getenv("USER"); + #endif +#endif +} + +bool SysInfo::isUserAdmin() +{ + bool isAdmin{ false }; + +#ifdef _WIN32 + BOOL b; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup; + b = AllocateAndInitializeSid( + &NtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &AdministratorsGroup); + if (b) + { + if (!CheckTokenMembership(NULL, AdministratorsGroup, &b)) + { + b = false; + } + FreeSid(AdministratorsGroup); + } + // TRUE - User has Administrators local group. + // FALSE - User does not have Administrators local group. + isAdmin = b; +#else + if (getuid() == 0U) + { + isAdmin = true; + } +#endif + return isAdmin; +}