mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2025-12-23 23:37:48 -05:00
Support starting OpenRGB as a Windows service
Commits squashed, code style cleaned up, and manually rebased by Adam Honse <calcprogrammer1@gmail.com>
This commit is contained in:
@@ -206,6 +206,7 @@ HEADERS +=
|
||||
RGBController/RGBController_Dummy.h \
|
||||
RGBController/RGBControllerKeyNames.h \
|
||||
RGBController/RGBController_Network.h \
|
||||
startup/startup.h \
|
||||
|
||||
SOURCES += \
|
||||
$$GUI_CPP \
|
||||
@@ -240,7 +241,7 @@ SOURCES +=
|
||||
dependencies/hueplusplus-1.2.0/src/UPnP.cpp \
|
||||
dependencies/hueplusplus-1.2.0/src/Utils.cpp \
|
||||
dependencies/hueplusplus-1.2.0/src/ZLLSensors.cpp \
|
||||
main.cpp \
|
||||
startup/startup.cpp \
|
||||
cli.cpp \
|
||||
dmiinfo/dmiinfo.cpp \
|
||||
LogManager.cpp \
|
||||
@@ -349,6 +350,7 @@ win32:SOURCES +=
|
||||
SuspendResume/SuspendResume_Windows.cpp \
|
||||
wmi/wmi.cpp \
|
||||
AutoStart/AutoStart-Windows.cpp \
|
||||
startup/main_Windows.cpp \
|
||||
|
||||
win32:HEADERS += $$CONTROLLER_H_WINDOWS
|
||||
|
||||
@@ -520,6 +522,7 @@ contains(QMAKE_PLATFORM, linux) {
|
||||
SPDAccessor/EE1004Accessor_Linux.cpp \
|
||||
SPDAccessor/SPD5118Accessor_Linux.cpp \
|
||||
SuspendResume/SuspendResume_Linux_FreeBSD.cpp \
|
||||
startup/main_Linux_MacOS.cpp \
|
||||
|
||||
#-------------------------------------------------------------------------------------------#
|
||||
# Set up install paths #
|
||||
@@ -710,6 +713,7 @@ macx {
|
||||
AutoStart/AutoStart-MacOS.cpp \
|
||||
qt/macutils.mm \
|
||||
SuspendResume/SuspendResume_MacOS.cpp \
|
||||
startup/main_Linux_MacOS.cpp \
|
||||
|
||||
SOURCES += $$CONTROLLER_CPP_MACOS
|
||||
|
||||
|
||||
345
main.cpp
345
main.cpp
@@ -1,345 +0,0 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| main.cpp |
|
||||
| |
|
||||
| Entry point for the OpenRGB application |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "ResourceManager.h"
|
||||
#include "NetworkServer.h"
|
||||
#include "LogManager.h"
|
||||
#include "cli.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _MACOSX_X86_X64
|
||||
#include "macUSPCIOAccess.h"
|
||||
io_connect_t macUSPCIO_driver_connection;
|
||||
#endif
|
||||
|
||||
#include "OpenRGBDialog.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "macutils.h"
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <csignal>
|
||||
#endif
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* InitializeTimerResolutionThreadFunction (Win32) *
|
||||
* *
|
||||
* On Windows, the default timer resolution is 15.6ms. For higher accuracy delays, *
|
||||
* the timer resolution should be set to a shorter interval. The shortest interval *
|
||||
* that can be set is 0.5ms. *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
#ifdef _WIN32
|
||||
void InitializeTimerResolutionThreadFunction()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| NtSetTimerResolution function pointer type |
|
||||
\*-----------------------------------------------------*/
|
||||
typedef unsigned int NTSTATUS;
|
||||
typedef NTSTATUS (*NTSETTIMERRESOLUTION)(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION = 4, |
|
||||
| isn't defined in Win10 headers |
|
||||
\*-----------------------------------------------------*/
|
||||
PROCESS_POWER_THROTTLING_STATE PowerThrottlingState { PROCESS_POWER_THROTTLING_CURRENT_VERSION, 4, 0 };
|
||||
|
||||
ULONG CurrentResolution;
|
||||
HMODULE NtDllHandle;
|
||||
NTSETTIMERRESOLUTION NtSetTimerResolution;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Load ntdll.dll and get pointer to NtSetTimerResolution|
|
||||
\*-----------------------------------------------------*/
|
||||
NtDllHandle = LoadLibrary("ntdll.dll");
|
||||
NtSetTimerResolution = (NTSETTIMERRESOLUTION)GetProcAddress(NtDllHandle, "NtSetTimerResolution");
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Windows 11 requires |
|
||||
| PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION |
|
||||
\*-----------------------------------------------------*/
|
||||
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottlingState, sizeof(PowerThrottlingState));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Call NtSetTimerResolution to set timer resolution to |
|
||||
| 0.5ms every 500ms |
|
||||
\*-----------------------------------------------------*/
|
||||
while(1)
|
||||
{
|
||||
NtSetTimerResolution(5000, TRUE, &CurrentResolution);
|
||||
|
||||
std::this_thread::sleep_for(500ms);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void WaitWhileServerOnline(NetworkServer* srv)
|
||||
{
|
||||
while (srv->GetOnline())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* Install SMBus Driver WinRing0, If not already installed (Win32) *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
#ifdef _WIN32
|
||||
void InstallWinRing0()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Driver final location usually |
|
||||
| C:\windows\system32\drivers\WinRing0x64.sys |
|
||||
\*-----------------------------------------------------*/
|
||||
TCHAR winring0_install_location[MAX_PATH];
|
||||
uint system_path_length = GetSystemDirectory(winring0_install_location, MAX_PATH);
|
||||
std::string winring0_filename = "WinRing0.sys";
|
||||
BOOL bIsWow64 = false;
|
||||
#if _WIN64
|
||||
winring0_filename = "WinRing0x64.sys";
|
||||
#else
|
||||
BOOL (*fnIsWow64Process)(HANDLE, PBOOL) = (BOOL (__cdecl *)(HANDLE, PBOOL))GetProcAddress(GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
|
||||
if (fnIsWow64Process)
|
||||
{
|
||||
fnIsWow64Process(GetCurrentProcess(),&bIsWow64);
|
||||
}
|
||||
if(bIsWow64)
|
||||
{
|
||||
winring0_filename = "WinRing0x64.sys";
|
||||
}
|
||||
#endif
|
||||
std::strncat(winring0_install_location, "\\drivers\\", MAX_PATH - system_path_length - 1);
|
||||
std::strncat(winring0_install_location, winring0_filename.c_str(), MAX_PATH - system_path_length - 10);
|
||||
|
||||
std::string driver_name = winring0_filename.substr(0, winring0_filename.size() - 4); // driver name: WinRing0 or WinRing0x64
|
||||
SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||
if (manager)
|
||||
{
|
||||
PVOID wow64_fsredirection_OldValue = NULL;
|
||||
if(bIsWow64)
|
||||
{
|
||||
Wow64DisableWow64FsRedirection(&wow64_fsredirection_OldValue);
|
||||
}
|
||||
if(INVALID_FILE_ATTRIBUTES == GetFileAttributes(winring0_install_location) && GetLastError()==ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
char module_path_buffer[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, module_path_buffer, MAX_PATH);
|
||||
std::string::size_type exe_loc = std::string(module_path_buffer).find_last_of("\\/");
|
||||
std::string driver_source_path = std::string(module_path_buffer).substr(0, exe_loc + 1) + winring0_filename;
|
||||
CopyFile(driver_source_path.c_str(), winring0_install_location, true);
|
||||
}
|
||||
if(bIsWow64)
|
||||
{
|
||||
Wow64RevertWow64FsRedirection(wow64_fsredirection_OldValue);
|
||||
}
|
||||
|
||||
SC_HANDLE service = OpenService(manager, driver_name.c_str(), SERVICE_ALL_ACCESS);
|
||||
if(!service)
|
||||
{
|
||||
std::string service_sys_path = "System32\\Drivers\\" + winring0_filename;
|
||||
service = CreateService(manager,
|
||||
driver_name.c_str(),
|
||||
driver_name.c_str(),
|
||||
SERVICE_ALL_ACCESS,
|
||||
SERVICE_KERNEL_DRIVER,
|
||||
SERVICE_AUTO_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
service_sys_path.c_str(),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(manager);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* Linux signal handler *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
#ifdef __linux__
|
||||
void sigHandler(int s)
|
||||
{
|
||||
std::signal(s, SIG_DFL);
|
||||
qApp->quit();
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* main *
|
||||
* *
|
||||
* Main function. Detects busses and Aura controllers, then opens the main window *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int exitval = EXIT_SUCCESS;
|
||||
#ifdef _WIN32
|
||||
/*-----------------------------------------------------*\
|
||||
| Windows only - Attach console output |
|
||||
\*-----------------------------------------------------*/
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| We are running under some terminal context; |
|
||||
| otherwise leave the GUI and CRT alone |
|
||||
\*-------------------------------------------------*/
|
||||
freopen("CONIN$", "r", stdin);
|
||||
freopen("CONOUT$", "w", stdout);
|
||||
freopen("CONOUT$", "w", stderr);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Windows only - Start timer resolution correction |
|
||||
| thread |
|
||||
\*-----------------------------------------------------*/
|
||||
std::thread * InitializeTimerResolutionThread;
|
||||
InitializeTimerResolutionThread = new std::thread(InitializeTimerResolutionThreadFunction);
|
||||
InitializeTimerResolutionThread->detach();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Windows only - Install SMBus Driver WinRing0 |
|
||||
\*-----------------------------------------------------*/
|
||||
InstallWinRing0();
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Mac x86/x64 only - Install SMBus Driver macUSPCIO |
|
||||
\*-----------------------------------------------------*/
|
||||
#ifdef _MACOSX_X86_X64
|
||||
InitMacUSPCIODriver();
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Process command line arguments before detection |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned int ret_flags = cli_pre_detection(argc, argv);
|
||||
|
||||
ResourceManager::get()->Initialize(
|
||||
!(ret_flags & RET_FLAG_NO_AUTO_CONNECT),
|
||||
!(ret_flags & RET_FLAG_NO_DETECT),
|
||||
ret_flags & RET_FLAG_START_SERVER,
|
||||
ret_flags & RET_FLAG_CLI_POST_DETECTION);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If the command line parser indicates that the GUI |
|
||||
| should run, or if there were no command line |
|
||||
| arguments, start the GUI. |
|
||||
\*-----------------------------------------------------*/
|
||||
if(ret_flags & RET_FLAG_START_GUI)
|
||||
{
|
||||
LOG_TRACE("[main] initializing GUI");
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Enable high DPI scaling support |
|
||||
\*-------------------------------------------------*/
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Enable high DPI fractional scaling support on |
|
||||
| Windows |
|
||||
\*-------------------------------------------------*/
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && defined(Q_OS_WIN)
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
|
||||
QApplication a(argc, argv);
|
||||
QGuiApplication::setDesktopFileName("org.openrgb.OpenRGB");
|
||||
LOG_TRACE("[main] QApplication created");
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Main UI widget |
|
||||
\*-------------------------------------------------*/
|
||||
OpenRGBDialog dlg;
|
||||
LOG_TRACE("[main] Dialog created");
|
||||
|
||||
if(ret_flags & RET_FLAG_I2C_TOOLS)
|
||||
{
|
||||
dlg.AddI2CToolsPage();
|
||||
}
|
||||
|
||||
if(ret_flags & RET_FLAG_START_MINIMIZED)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/*---------------------------------------------*\
|
||||
| Show the window always, even if it will |
|
||||
| immediately be hidden. On Windows, events |
|
||||
| are not delivered to nativeEventFilter (for |
|
||||
| SuspendResume) until the window has been |
|
||||
| shown once. |
|
||||
\*---------------------------------------------*/
|
||||
dlg.showMinimized();
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
MacUtils::ToggleApplicationDocklessState(false);
|
||||
#endif
|
||||
dlg.hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
dlg.show();
|
||||
}
|
||||
|
||||
LOG_TRACE("[main] Ready to exec() the dialog");
|
||||
#ifdef __linux__
|
||||
std::signal(SIGINT, sigHandler);
|
||||
std::signal(SIGTERM, sigHandler);
|
||||
#endif
|
||||
exitval = a.exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| If no GUI is needed, we let the background |
|
||||
| threads run as long as they need, but we need to |
|
||||
| AT LEAST wait for initialization to finish |
|
||||
\*-------------------------------------------------*/
|
||||
ResourceManager::get()->WaitForInitialization();
|
||||
|
||||
if(ret_flags & RET_FLAG_START_SERVER)
|
||||
{
|
||||
NetworkServer* server = ResourceManager::get()->GetServer();
|
||||
|
||||
if(!server->GetOnline())
|
||||
{
|
||||
exitval = EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
WaitWhileServerOnline(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
ResourceManager::get()->Cleanup();
|
||||
#ifdef _MACOSX_X86_X64
|
||||
CloseMacUSPCIODriver();
|
||||
#endif
|
||||
LOG_TRACE("OpenRGB finishing with exit code %d", exitval);
|
||||
return exitval;
|
||||
}
|
||||
@@ -102,15 +102,9 @@ XML_FILE+=" InstallerVersion='200' Languages='1033' Compressed='yes'
|
||||
XML_FILE+=" <Media Id='1' Cabinet='${PRODUCTNAME,,}.cab' EmbedCab='yes'/>\r\n"
|
||||
XML_FILE+=" <Condition Message='This package supports Windows 64bit Only'>VersionNT64</Condition>\r\n"
|
||||
XML_FILE+=" <MajorUpgrade Schedule='afterInstallInitialize' AllowDowngrades='yes'/>\r\n"
|
||||
XML_FILE+=" <CustomAction Id='LaunchOpenRGBAdmin' FileKey='${EXE_ID}' ExeCommand='${EXE_FILE} --loglevel 1 --nodetect' Execute='deferred' Return='asyncNoWait' Impersonate='no'/>\r\n"
|
||||
XML_FILE+=" <CustomAction Id='LaunchOpenRGB' FileKey='${EXE_ID}' ExeCommand='--gui --loglevel 6' Execute='immediate' Return='asyncNoWait' Impersonate='yes'/>\r\n"
|
||||
XML_FILE+=" <Icon Id='OpenRGBIcon' SourceFile='${ICONFILE}'/>\r\n"
|
||||
XML_FILE+=" <Property Id='ARPPRODUCTICON' Value='OpenRGBIcon'/>\r\n"
|
||||
XML_FILE+=" <Property Id='ARPURLINFOABOUT' Value='https://www.openrgb.org'/>\r\n"
|
||||
XML_FILE+=" <InstallExecuteSequence>\r\n"
|
||||
XML_FILE+=" <Custom Action='LaunchOpenRGBAdmin' After='InstallFiles'>NOT Installed</Custom>\r\n"
|
||||
XML_FILE+=" <Custom Action='LaunchOpenRGB' After='InstallFinalize'>NOT Installed</Custom>\r\n"
|
||||
XML_FILE+=" </InstallExecuteSequence>\r\n"
|
||||
XML_FILE+=" <Property Id='WIXUI_INSTALLDIR' Value='INSTALLDIR'/>\r\n"
|
||||
XML_FILE+=" <UIRef Id='WixUI_InstallDir'/>\r\n"
|
||||
XML_FILE+=" <UIRef Id='WixUI_ErrorProgressText'/>\r\n"
|
||||
@@ -123,6 +117,8 @@ XML_FILE+=" <Directory Id='ProgramFiles64Folder'>\r\n"
|
||||
XML_FILE+=" <Directory Id='INSTALLDIR' Name='${PRODUCTNAME}'>\r\n"
|
||||
XML_FILE+=" <Component Id='${PRODUCTNAME}Files' Guid='"$(uuidgen -t | awk '{ print toupper($0) }')"'>\r\n"
|
||||
XML_FILE+="${FILES}\r\n"
|
||||
XML_FILE+=" <ServiceInstall Id='ServiceInstaller' Type='ownProcess' Name='OpenRGB' DisplayName='OpenRGB' Description='OpenRGB SDK Server' Start='auto' Account='[SERVICEACCOUNT]' Password='[SERVICEPASSWORD]' ErrorControl='normal'/>\r\n"
|
||||
XML_FILE+=" <ServiceControl Id='StartService' Start='install' Stop='both' Remove='uninstall' Name='OpenRGB' Wait='yes'/>\r\n"
|
||||
XML_FILE+=" </Component>\r\n"
|
||||
XML_FILE+="${DIRECTORIES}\r\n"
|
||||
XML_FILE+=" </Directory>\r\n"
|
||||
|
||||
105
startup/main_Linux_MacOS.cpp
Normal file
105
startup/main_Linux_MacOS.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| main_Linux_MacOS.cpp |
|
||||
| |
|
||||
| Entry point for the OpenRGB application |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "cli.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "NetworkServer.h"
|
||||
#include "LogManager.h"
|
||||
#include "startup.h"
|
||||
|
||||
#ifdef _MACOSX_X86_X64
|
||||
#include "macUSPCIOAccess.h"
|
||||
io_connect_t macUSPCIO_driver_connection;
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "macutils.h"
|
||||
#endif
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| WaitWhileServerOnline |
|
||||
| |
|
||||
| Wait while NetworkServer is online and return only when |
|
||||
| it has shut down |
|
||||
\*---------------------------------------------------------*/
|
||||
void WaitWhileServerOnline(NetworkServer* srv)
|
||||
{
|
||||
while(srv->GetOnline())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
};
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| main |
|
||||
| |
|
||||
| Entry point, calls the startup processing |
|
||||
\*---------------------------------------------------------*/
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Mac x86/x64 only - Install SMBus Driver macUSPCIO |
|
||||
\*-----------------------------------------------------*/
|
||||
#ifdef _MACOSX_X86_X64
|
||||
InitMacUSPCIODriver();
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Perform CLI pre-detection processing to get return |
|
||||
| flags |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned int ret_flags = cli_pre_detection(argc, argv);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize ResourceManager |
|
||||
\*-----------------------------------------------------*/
|
||||
ResourceManager::get()->Initialize(
|
||||
!(ret_flags & RET_FLAG_NO_AUTO_CONNECT),
|
||||
!(ret_flags & RET_FLAG_NO_DETECT),
|
||||
ret_flags & RET_FLAG_START_SERVER,
|
||||
ret_flags & RET_FLAG_CLI_POST_DETECTION);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Perform application startup and run the application. |
|
||||
| This call returns only when the GUI application is |
|
||||
| closing or if not running the GUI. |
|
||||
\*-----------------------------------------------------*/
|
||||
int exitval = startup(argc, argv, ret_flags);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If started in headless server mode, wait until server |
|
||||
| shuts down before closing application. |
|
||||
\*-----------------------------------------------------*/
|
||||
if((ret_flags & RET_FLAG_START_SERVER) && !(ret_flags & RET_FLAG_START_GUI))
|
||||
{
|
||||
NetworkServer* server = ResourceManager::get()->GetServer();
|
||||
if(server)
|
||||
{
|
||||
WaitWhileServerOnline(server);
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Perform ResourceManager cleanup before exiting |
|
||||
\*-----------------------------------------------------*/
|
||||
ResourceManager::get()->Cleanup();
|
||||
|
||||
LOG_TRACE("OpenRGB finishing with exit code %d", exitval);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Mac x86/x64 only - Uninstall SMBus Driver macUSPCIO |
|
||||
\*-----------------------------------------------------*/
|
||||
#ifdef _MACOSX_X86_X64
|
||||
CloseMacUSPCIODriver();
|
||||
#endif
|
||||
|
||||
return exitval;
|
||||
}
|
||||
699
startup/main_Windows.cpp
Normal file
699
startup/main_Windows.cpp
Normal file
@@ -0,0 +1,699 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| main_Windows.cpp |
|
||||
| |
|
||||
| Entry point for the OpenRGB application on Windows |
|
||||
| Differentiate between service and application startup |
|
||||
| Housekeeping for Windows service management |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#include <thread>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include "cli.h"
|
||||
#include "startup.h"
|
||||
#include "LogManager.h"
|
||||
#include "NetworkServer.h"
|
||||
#include "ResourceManager.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static int common_main(int argc, char* argv[]);
|
||||
|
||||
static void WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv);
|
||||
static void ReportServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
|
||||
|
||||
static char service_name[] = "OpenRGB";
|
||||
static SERVICE_TABLE_ENTRY service_dispatch_table[] = { { service_name, ServiceMain }, { NULL, NULL } };
|
||||
static DWORD service_checkpoint = 1;
|
||||
static SERVICE_STATUS_HANDLE service_status_handle;
|
||||
static SERVICE_STATUS service_status;
|
||||
|
||||
static bool started_as_service;
|
||||
static volatile bool service_stop_requested;
|
||||
static bool have_console;
|
||||
|
||||
static std::mutex service_status_mutex;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| Detection progress goes from 0 to 100 twice, passing an |
|
||||
| estimate based on that confuses the service manager |
|
||||
\*---------------------------------------------------------*/
|
||||
static int detection_pass;
|
||||
static unsigned int lastpercent = 101;
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| ServiceStartupProgress |
|
||||
| |
|
||||
| Report detection progress when running as a service |
|
||||
\*---------------------------------------------------------*/
|
||||
static void ServiceStartupProgress(void*)
|
||||
{
|
||||
unsigned int percent = ResourceManager::get()->GetDetectionPercent();
|
||||
unsigned int estimate;
|
||||
|
||||
percent = std::clamp(percent, 0u, 100u);
|
||||
|
||||
if(lastpercent > percent)
|
||||
{
|
||||
detection_pass += 1;
|
||||
}
|
||||
|
||||
lastpercent = percent;
|
||||
|
||||
switch(detection_pass)
|
||||
{
|
||||
case 0:
|
||||
percent = 0;
|
||||
break;
|
||||
case 1:
|
||||
percent = percent * 4 / 5;
|
||||
break;
|
||||
case 2:
|
||||
percent = percent / 5 + 80;
|
||||
break;
|
||||
default:
|
||||
percent = 100;
|
||||
break;
|
||||
}
|
||||
|
||||
estimate = (100 - percent) / 5 + 10;
|
||||
|
||||
ReportServiceStatus(SERVICE_START_PENDING, NO_ERROR, estimate * 1000);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| ReportServiceStatus |
|
||||
| |
|
||||
| As of writing this (24H2) there are 7 possible service |
|
||||
| states: |
|
||||
| SERVICE_START_PENDING, SERVICE_RUNNING, |
|
||||
| SERVICE_STOP_PENDING, SERVICE_STOPPED, |
|
||||
| SERVICE_PAUSE_PENDING, SERVICE_PAUSED, |
|
||||
| SERVICE_CONTINUE_PENDING |
|
||||
| |
|
||||
| We don't accept pause requests, so only start_pending |
|
||||
| through stopped are possible. |
|
||||
| |
|
||||
| Control requests can arrive asynchronously so we take |
|
||||
| some precautions here not to report an out of order |
|
||||
| status. |
|
||||
\*---------------------------------------------------------*/
|
||||
static void ReportServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
|
||||
{
|
||||
static DWORD last_state = SERVICE_START_PENDING;
|
||||
|
||||
LOG_TRACE("ReportServiceStatus(%lu, %lu, %lu), PID %lu Thread %lu", dwCurrentState, dwWin32ExitCode, dwWaitHint, GetCurrentProcessId(), GetCurrentThreadId());
|
||||
|
||||
service_status_mutex.lock();
|
||||
|
||||
switch(last_state)
|
||||
{
|
||||
case SERVICE_RUNNING:
|
||||
/*---------------------------------------------*\
|
||||
| Don't go back to SERVICE_START_PENDING |
|
||||
\*---------------------------------------------*/
|
||||
if(dwCurrentState == SERVICE_START_PENDING)
|
||||
{
|
||||
service_status_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case SERVICE_STOP_PENDING:
|
||||
/*---------------------------------------------*\
|
||||
| Don't go back to starting or running |
|
||||
\*---------------------------------------------*/
|
||||
if((dwCurrentState != SERVICE_STOP_PENDING) && (dwCurrentState != SERVICE_STOPPED))
|
||||
{
|
||||
service_status_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case SERVICE_STOPPED:
|
||||
/*---------------------------------------------*\
|
||||
| Don't call SetServiceStatus anymore after |
|
||||
| SERVICE_STOPPED has been reported |
|
||||
\*---------------------------------------------*/
|
||||
service_status_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(dwCurrentState)
|
||||
{
|
||||
case SERVICE_START_PENDING:
|
||||
case SERVICE_STOP_PENDING:
|
||||
service_status.dwControlsAccepted = 0;
|
||||
service_status.dwCheckPoint = service_checkpoint;
|
||||
service_checkpoint += 1;
|
||||
break;
|
||||
|
||||
case SERVICE_RUNNING:
|
||||
case SERVICE_STOPPED:
|
||||
service_status.dwCheckPoint = 0;
|
||||
service_status.dwControlsAccepted =
|
||||
// SERVICE_ACCEPT_POWEREVENT |
|
||||
SERVICE_ACCEPT_STOP |
|
||||
SERVICE_ACCEPT_PRESHUTDOWN;
|
||||
break;
|
||||
|
||||
default:
|
||||
/*---------------------------------------------*\
|
||||
| Shouldn't happen |
|
||||
\*---------------------------------------------*/
|
||||
service_status_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
last_state = dwCurrentState;
|
||||
service_status.dwCurrentState = dwCurrentState;
|
||||
service_status.dwWin32ExitCode = dwWin32ExitCode;
|
||||
service_status.dwWaitHint = dwWaitHint;
|
||||
|
||||
SetServiceStatus(service_status_handle, &service_status);
|
||||
|
||||
service_status_mutex.unlock();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| ServiceControlHandler |
|
||||
| |
|
||||
| Handler function to register with |
|
||||
| RegisterServiceControlHandlerEx, to handle service |
|
||||
| control operations (stop/shutdown). |
|
||||
\*---------------------------------------------------------*/
|
||||
static DWORD WINAPI ServiceControlHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
|
||||
{
|
||||
DWORD retval = NO_ERROR;
|
||||
|
||||
LOG_TRACE("ServiceControlHandler(%lu, %lu, %p, %p) called, PID %lu Thread %lu", dwControl, dwEventType, lpEventData, lpContext, GetCurrentProcessId(), GetCurrentThreadId());
|
||||
|
||||
switch(dwControl)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| TODO either this or |
|
||||
| RegisterSuspendResumeNotification somewhere |
|
||||
\*-------------------------------------------------*/
|
||||
// case SERVICE_CONTROL_POWEREVENT:
|
||||
case SERVICE_CONTROL_INTERROGATE:
|
||||
/*---------------------------------------------*\
|
||||
| Should do nothing but return NO_ERROR |
|
||||
\*---------------------------------------------*/
|
||||
break;
|
||||
|
||||
case SERVICE_CONTROL_STOP:
|
||||
case SERVICE_CONTROL_PRESHUTDOWN:
|
||||
ReportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 10000);
|
||||
service_stop_requested = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = ERROR_CALL_NOT_IMPLEMENTED;
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| LogEvent |
|
||||
| |
|
||||
| Log an event to the service log when running as a |
|
||||
| service. Only used for startup and shutdown events. |
|
||||
\*---------------------------------------------------------*/
|
||||
static void LogEvent(const char *fmt, ...)
|
||||
{
|
||||
va_list args1;
|
||||
va_list args2;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Handle variable arguments |
|
||||
\*-----------------------------------------------------*/
|
||||
va_start(args1, fmt);
|
||||
va_copy(args2, args1);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Determine string length and allocate buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
int len = vsnprintf(NULL, 0, fmt, args1);
|
||||
char * buf = (char *)malloc(len + 1);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Fill the string buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
va_end(args1);
|
||||
vsnprintf(buf, len + 1, fmt, args2);
|
||||
va_end(args2);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If we somehow ended up here with a working console |
|
||||
| then just print it |
|
||||
\*-----------------------------------------------------*/
|
||||
if(have_console)
|
||||
{
|
||||
printf("%s\n", buf);
|
||||
free(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Otherwise, register event source with our service |
|
||||
| name and report event to the event log |
|
||||
\*-----------------------------------------------------*/
|
||||
HANDLE event_source = RegisterEventSource(NULL, service_name);
|
||||
|
||||
if(event_source)
|
||||
{
|
||||
LPCSTR strings[2] = { service_name, buf };
|
||||
ReportEvent(
|
||||
event_source,
|
||||
EVENTLOG_ERROR_TYPE,
|
||||
0,
|
||||
1, // error #
|
||||
NULL,
|
||||
2,
|
||||
0,
|
||||
strings,
|
||||
NULL);
|
||||
DeregisterEventSource(event_source);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| No console, no logmanager, no eventlog, no more |
|
||||
| ideas. Sometimes it's OK to give up. |
|
||||
\*-------------------------------------------------*/
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Free the buffer |
|
||||
\*-----------------------------------------------------*/
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| LogError |
|
||||
| |
|
||||
| Log an error event when running as a service |
|
||||
\*---------------------------------------------------------*/
|
||||
static void LogError(const char *fn, DWORD lasterror)
|
||||
{
|
||||
std::string message = std::system_category().message(lasterror);
|
||||
LogEvent("%s failed with error code %lu \"%s\"", fn, lasterror, message.c_str());
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| ServiceMain |
|
||||
| |
|
||||
| Figures out whether we are started as a service, calls |
|
||||
| common_main one way or another. |
|
||||
\*---------------------------------------------------------*/
|
||||
static void WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize service status |
|
||||
\*-----------------------------------------------------*/
|
||||
service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
service_status.dwServiceSpecificExitCode = 0;
|
||||
|
||||
started_as_service = true;
|
||||
service_status_handle = RegisterServiceCtrlHandlerEx(service_name, ServiceControlHandler, NULL);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Exit if service status handle is invalid |
|
||||
\*-----------------------------------------------------*/
|
||||
if(service_status_handle == 0)
|
||||
{
|
||||
LogError("RegisterServiceCtrlHandlerEx", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Report service status start pending |
|
||||
\*-----------------------------------------------------*/
|
||||
ReportServiceStatus(SERVICE_START_PENDING, NO_ERROR, 30000);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Perform common main processing |
|
||||
\*-----------------------------------------------------*/
|
||||
int exitval = common_main(dwArgc, lpszArgv);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Log if exit was unsuccessful |
|
||||
\*-----------------------------------------------------*/
|
||||
if(exitval != EXIT_SUCCESS)
|
||||
{
|
||||
LogEvent("OpenRGB finishing with exit code %d", exitval);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Report service status stopped |
|
||||
\*-----------------------------------------------------*/
|
||||
ReportServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| main |
|
||||
| |
|
||||
| Entry point, checks if started as a service and then |
|
||||
| calls the common main processing |
|
||||
\*---------------------------------------------------------*/
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
started_as_service = false;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| This will call ServiceMain() if we are started as a |
|
||||
| service |
|
||||
\*-----------------------------------------------------*/
|
||||
bool sscd = StartServiceCtrlDispatcher(service_dispatch_table);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| ... returns a specific error code otherwise |
|
||||
\*-----------------------------------------------------*/
|
||||
DWORD lasterror = GetLastError();
|
||||
|
||||
if(sscd)
|
||||
{
|
||||
if(started_as_service)
|
||||
{
|
||||
/*---------------------------------------------*\
|
||||
| StartServiceCtrlDispatcher reported success, |
|
||||
| ServiceMain was reached. Don't log anything, |
|
||||
| it's the normal exit path when a service is |
|
||||
| stopped. |
|
||||
\*---------------------------------------------*/
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*---------------------------------------------*\
|
||||
| Should not happen |
|
||||
\*---------------------------------------------*/
|
||||
LogEvent("StartServiceCtrlDispatcher returned true, but ServiceMain has not been reached.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| StartServiceCtrlDispatcher reported failure. |
|
||||
| First, check that the error isn't the one returned |
|
||||
| when it's called from an app |
|
||||
\*-----------------------------------------------------*/
|
||||
if(lasterror != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
|
||||
{
|
||||
LogError("StartServiceCtrlDispatcher", lasterror);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Now we are quite sure we are not started as a service |
|
||||
\*-----------------------------------------------------*/
|
||||
if(started_as_service)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| ... so this should not happen |
|
||||
\*-------------------------------------------------*/
|
||||
LogEvent("StartServiceCtrlDispatcher returned false, but ServiceMain has been reached.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Code that needs to be run at startup when we are |
|
||||
| started as an app and not as a service goes here. |
|
||||
\*-----------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Attach console output |
|
||||
\*-----------------------------------------------------*/
|
||||
if(AttachConsole(ATTACH_PARENT_PROCESS))
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| We are running under some terminal context; |
|
||||
| otherwise leave the GUI and CRT alone |
|
||||
\*-------------------------------------------------*/
|
||||
freopen("CONIN$", "r", stdin);
|
||||
freopen("CONOUT$", "w", stdout);
|
||||
freopen("CONOUT$", "w", stderr);
|
||||
have_console = true;
|
||||
}
|
||||
|
||||
return common_main(argc, argv);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| InitializeTimerResolutionThreadFunction (Win32) |
|
||||
| |
|
||||
| On Windows, the default timer resolution is 15.6ms. |
|
||||
| For higher accuracy delays, the timer resolution should |
|
||||
| be set to a shorter interval. The shortest interval |
|
||||
| that can be set is 0.5ms. |
|
||||
\*---------------------------------------------------------*/
|
||||
void InitializeTimerResolutionThreadFunction()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| NtSetTimerResolution function pointer type |
|
||||
\*-----------------------------------------------------*/
|
||||
typedef unsigned int NTSTATUS;
|
||||
typedef NTSTATUS (*NTSETTIMERRESOLUTION)(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION = 4, |
|
||||
| isn't defined in Win10 headers |
|
||||
\*-----------------------------------------------------*/
|
||||
PROCESS_POWER_THROTTLING_STATE PowerThrottlingState { PROCESS_POWER_THROTTLING_CURRENT_VERSION, 4, 0 };
|
||||
|
||||
ULONG CurrentResolution;
|
||||
HMODULE NtDllHandle;
|
||||
NTSETTIMERRESOLUTION NtSetTimerResolution;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Load ntdll.dll and get pointer to NtSetTimerResolution|
|
||||
\*-----------------------------------------------------*/
|
||||
NtDllHandle = LoadLibrary("ntdll.dll");
|
||||
NtSetTimerResolution = (NTSETTIMERRESOLUTION)GetProcAddress(NtDllHandle, "NtSetTimerResolution");
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Windows 11 requires |
|
||||
| PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION |
|
||||
\*-----------------------------------------------------*/
|
||||
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottlingState, sizeof(PowerThrottlingState));
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Call NtSetTimerResolution to set timer resolution to |
|
||||
| 0.5ms every 500ms |
|
||||
\*-----------------------------------------------------*/
|
||||
while(1)
|
||||
{
|
||||
NtSetTimerResolution(5000, TRUE, &CurrentResolution);
|
||||
|
||||
std::this_thread::sleep_for(500ms);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| WaitWhileServerOnline |
|
||||
| |
|
||||
| Wait while NetworkServer is online and return only when |
|
||||
| it has shut down |
|
||||
\*---------------------------------------------------------*/
|
||||
static void WaitWhileServerOnline(NetworkServer* srv)
|
||||
{
|
||||
while(srv->GetOnline())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
if(service_stop_requested)
|
||||
{
|
||||
srv->StopServer();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| InstallWinRing0 |
|
||||
| |
|
||||
| Install SMBus Driver WinRing0, If not already installed |
|
||||
| (Win32) |
|
||||
\*---------------------------------------------------------*/
|
||||
void InstallWinRing0()
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Driver final location usually |
|
||||
| C:\windows\system32\drivers\WinRing0x64.sys |
|
||||
\*-----------------------------------------------------*/
|
||||
TCHAR winring0_install_location[MAX_PATH];
|
||||
uint system_path_length = GetSystemDirectory(winring0_install_location, MAX_PATH);
|
||||
std::string winring0_filename = "WinRing0.sys";
|
||||
BOOL bIsWow64 = false;
|
||||
#if _WIN64
|
||||
winring0_filename = "WinRing0x64.sys";
|
||||
#else
|
||||
BOOL (*fnIsWow64Process)(HANDLE, PBOOL) = (BOOL (__cdecl *)(HANDLE, PBOOL))GetProcAddress(GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
|
||||
if (fnIsWow64Process)
|
||||
{
|
||||
fnIsWow64Process(GetCurrentProcess(),&bIsWow64);
|
||||
}
|
||||
if(bIsWow64)
|
||||
{
|
||||
winring0_filename = "WinRing0x64.sys";
|
||||
}
|
||||
#endif
|
||||
std::strncat(winring0_install_location, "\\drivers\\", MAX_PATH - system_path_length - 1);
|
||||
std::strncat(winring0_install_location, winring0_filename.c_str(), MAX_PATH - system_path_length - 10);
|
||||
|
||||
std::string driver_name = winring0_filename.substr(0, winring0_filename.size() - 4); // driver name: WinRing0 or WinRing0x64
|
||||
SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||
if (manager)
|
||||
{
|
||||
PVOID wow64_fsredirection_OldValue = NULL;
|
||||
if(bIsWow64)
|
||||
{
|
||||
Wow64DisableWow64FsRedirection(&wow64_fsredirection_OldValue);
|
||||
}
|
||||
if(INVALID_FILE_ATTRIBUTES == GetFileAttributes(winring0_install_location) && GetLastError()==ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
char module_path_buffer[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, module_path_buffer, MAX_PATH);
|
||||
std::string::size_type exe_loc = std::string(module_path_buffer).find_last_of("\\/");
|
||||
std::string driver_source_path = std::string(module_path_buffer).substr(0, exe_loc + 1) + winring0_filename;
|
||||
CopyFile(driver_source_path.c_str(), winring0_install_location, true);
|
||||
}
|
||||
if(bIsWow64)
|
||||
{
|
||||
Wow64RevertWow64FsRedirection(wow64_fsredirection_OldValue);
|
||||
}
|
||||
|
||||
SC_HANDLE service = OpenService(manager, driver_name.c_str(), SERVICE_ALL_ACCESS);
|
||||
if(!service)
|
||||
{
|
||||
std::string service_sys_path = "System32\\Drivers\\" + winring0_filename;
|
||||
service = CreateService(manager,
|
||||
driver_name.c_str(),
|
||||
driver_name.c_str(),
|
||||
SERVICE_ALL_ACCESS,
|
||||
SERVICE_KERNEL_DRIVER,
|
||||
SERVICE_AUTO_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
service_sys_path.c_str(),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
CloseServiceHandle(service);
|
||||
CloseServiceHandle(manager);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| common_main |
|
||||
| |
|
||||
| Common entry functionality after determining whether we |
|
||||
| were started as a service or not. |
|
||||
\*---------------------------------------------------------*/
|
||||
static int common_main(int argc, char* argv[])
|
||||
{
|
||||
unsigned int ret_flags;
|
||||
|
||||
if(started_as_service)
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Passing command line arguments to a service is |
|
||||
| difficult, can cause all kinds of trouble and |
|
||||
| doesn't have a way to warn about them |
|
||||
\*-------------------------------------------------*/
|
||||
ret_flags = RET_FLAG_START_SERVER | RET_FLAG_NO_AUTO_CONNECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| Perform CLI pre-detection processing to get |
|
||||
| return flags |
|
||||
\*-------------------------------------------------*/
|
||||
ret_flags = cli_pre_detection(argc, argv);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Start timer resolution correction thread |
|
||||
\*-----------------------------------------------------*/
|
||||
std::thread * InitializeTimerResolutionThread;
|
||||
InitializeTimerResolutionThread = new std::thread(InitializeTimerResolutionThreadFunction);
|
||||
InitializeTimerResolutionThread->detach();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Install SMBus Driver WinRing0 |
|
||||
\*-----------------------------------------------------*/
|
||||
InstallWinRing0();
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize ResourceManager |
|
||||
\*-----------------------------------------------------*/
|
||||
ResourceManager::get()->Initialize(
|
||||
!(ret_flags & RET_FLAG_NO_AUTO_CONNECT),
|
||||
!(ret_flags & RET_FLAG_NO_DETECT),
|
||||
ret_flags & RET_FLAG_START_SERVER,
|
||||
ret_flags & RET_FLAG_CLI_POST_DETECTION);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If running as a service, register the service startup |
|
||||
| progress callback with ResourceManager to report |
|
||||
| startup progress based on detection progress. |
|
||||
\*-----------------------------------------------------*/
|
||||
if(started_as_service)
|
||||
{
|
||||
ResourceManager::get()->RegisterDetectionProgressCallback(ServiceStartupProgress, NULL);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Perform application startup and run the application. |
|
||||
| This call returns only when the GUI application is |
|
||||
| closing or if not running the GUI. |
|
||||
\*-----------------------------------------------------*/
|
||||
int exitval = startup(argc, argv, ret_flags);
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If running as a service, unregister the service |
|
||||
| startup progress callback when shutting down. |
|
||||
\*-----------------------------------------------------*/
|
||||
if(started_as_service)
|
||||
{
|
||||
ResourceManager::get()->UnregisterDetectionProgressCallback(ServiceStartupProgress, NULL);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If started in headless server mode, wait until server |
|
||||
| shuts down before closing application. |
|
||||
\*-----------------------------------------------------*/
|
||||
if((ret_flags & RET_FLAG_START_SERVER) && !(ret_flags & RET_FLAG_START_GUI))
|
||||
{
|
||||
NetworkServer* server = ResourceManager::get()->GetServer();
|
||||
if(server && server->GetOnline())
|
||||
{
|
||||
if(started_as_service)
|
||||
{
|
||||
ReportServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);
|
||||
}
|
||||
WaitWhileServerOnline(server);
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Perform ResourceManager cleanup before exiting |
|
||||
\*-----------------------------------------------------*/
|
||||
ResourceManager::get()->Cleanup();
|
||||
|
||||
LOG_TRACE("OpenRGB finishing with exit code %d", exitval);
|
||||
|
||||
return exitval;
|
||||
}
|
||||
134
startup/startup.cpp
Normal file
134
startup/startup.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| startup.cpp |
|
||||
| |
|
||||
| Startup for the OpenRGB application |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#include "cli.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "NetworkServer.h"
|
||||
#include "startup.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include "OpenRGBDialog.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "macutils.h"
|
||||
#endif
|
||||
|
||||
/******************************************************************************************\
|
||||
* *
|
||||
* startup *
|
||||
* *
|
||||
* Opens the main windows or starts the server *
|
||||
* *
|
||||
\******************************************************************************************/
|
||||
int startup(int argc, char* argv[], unsigned int ret_flags)
|
||||
{
|
||||
/*-----------------------------------------------------*\
|
||||
| Initialize exit value, which will be returned on exit |
|
||||
| in main() |
|
||||
\*-----------------------------------------------------*/
|
||||
int exitval = EXIT_SUCCESS;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| If the command line parser indicates that the GUI |
|
||||
| should run, or if there were no command line |
|
||||
| arguments, start the GUI. |
|
||||
\*-----------------------------------------------------*/
|
||||
if(ret_flags & RET_FLAG_START_GUI)
|
||||
{
|
||||
LOG_TRACE("[main] initializing GUI");
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Enable high DPI scaling support |
|
||||
\*-------------------------------------------------*/
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Enable high DPI fractional scaling support on |
|
||||
| Windows |
|
||||
\*-------------------------------------------------*/
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && defined(Q_OS_WIN)
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Create Qt application |
|
||||
\*-------------------------------------------------*/
|
||||
QApplication a(argc, argv);
|
||||
QGuiApplication::setDesktopFileName("org.openrgb.OpenRGB");
|
||||
LOG_TRACE("[startup] QApplication created");
|
||||
|
||||
/*-------------------------------------------------*\
|
||||
| Main UI widget |
|
||||
\*-------------------------------------------------*/
|
||||
OpenRGBDialog dlg;
|
||||
LOG_TRACE("[startup] Dialog created");
|
||||
|
||||
if(ret_flags & RET_FLAG_I2C_TOOLS)
|
||||
{
|
||||
dlg.AddI2CToolsPage();
|
||||
}
|
||||
|
||||
dlg.AddClientTab();
|
||||
|
||||
if(ret_flags & RET_FLAG_START_MINIMIZED)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/*---------------------------------------------*\
|
||||
| Show the window always, even if it will |
|
||||
| immediately be hidden. On Windows, events |
|
||||
| are not delivered to nativeEventFilter (for |
|
||||
| SuspendResume) until the window has been |
|
||||
| shown once. |
|
||||
| |
|
||||
| TODO Try using |
|
||||
| RegisterSuspendResumeNotification instead, |
|
||||
| that should work in headless mode too |
|
||||
\*---------------------------------------------*/
|
||||
dlg.showMinimized();
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
MacUtils::ToggleApplicationDocklessState(false);
|
||||
#endif
|
||||
dlg.hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
dlg.show();
|
||||
}
|
||||
|
||||
LOG_TRACE("[main] Ready to exec() the dialog");
|
||||
exitval = a.exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
/*-------------------------------------------------*\
|
||||
| If no GUI is needed, we let the background |
|
||||
| threads run as long as they need, but we need to |
|
||||
| AT LEAST wait for initialization to finish |
|
||||
\*-------------------------------------------------*/
|
||||
ResourceManager::get()->WaitForInitialization();
|
||||
|
||||
if(ret_flags & RET_FLAG_START_SERVER)
|
||||
{
|
||||
NetworkServer* server = ResourceManager::get()->GetServer();
|
||||
if(server)
|
||||
{
|
||||
exitval = !server->GetOnline();
|
||||
}
|
||||
else
|
||||
{
|
||||
exitval = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(exitval);
|
||||
}
|
||||
12
startup/startup.h
Normal file
12
startup/startup.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| startup.h |
|
||||
| |
|
||||
| Startup for the OpenRGB application |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-only |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int startup(int argc, char* argv[], unsigned int ret_flags);
|
||||
Reference in New Issue
Block a user