diff --git a/src/process.cpp b/src/process.cpp index b0cd448a..6a5f1ec0 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -58,9 +58,6 @@ namespace proc { std::unique_ptr init() { - #ifdef _WIN32 - vdisplayDriverInitialized = VDISPLAY::openVDisplayDevice(); - #endif return std::make_unique(); } @@ -210,7 +207,9 @@ namespace proc { _launch_session = launch_session; #ifdef _WIN32 - if (launch_session->virtual_display) { + if (launch_session->virtual_display || _app.virtual_display) { + // Mark as no vdisplay by default + launch_session->virtual_display = false; if (!vdisplayDriverInitialized) { // Try init driver again vdisplayDriverInitialized = VDISPLAY::openVDisplayDevice(); @@ -226,6 +225,8 @@ namespace proc { launch_session->fps, launch_session->display_guid ); + // Set virtual_display to true when everything went fine + launch_session->virtual_display = true; VDISPLAY::changeDisplaySettings(vdisplay_name.c_str(), launch_session->width, launch_session->height, launch_session->fps); VDISPLAY::setPrimaryDisplay(vdisplay_name.c_str()); @@ -633,6 +634,32 @@ namespace proc { std::set ids; std::vector apps; int i = 0; + + if (vdisplayDriverInitialized) { + proc::ctx_t ctx; + ctx.name = "Virtual Display"; + ctx.image_path = parse_env_val(this_env, "virtual_desktop.png"); + ctx.virtual_display = true; + + ctx.elevated = false; + ctx.auto_detach = true; + ctx.wait_all = true; + ctx.exit_timeout = 5s; + + auto possible_ids = calculate_app_id(ctx.name, ctx.image_path, i++); + if (ids.count(std::get<0>(possible_ids)) == 0) { + // Avoid using index to generate id if possible + ctx.id = std::get<0>(possible_ids); + } + else { + // Fallback to include index on collision + ctx.id = std::get<1>(possible_ids); + } + ids.insert(ctx.id); + + apps.emplace_back(std::move(ctx)); + } + for (auto &[_, app_node] : apps_node) { proc::ctx_t ctx; @@ -648,6 +675,7 @@ namespace proc { auto auto_detach = app_node.get_optional("auto-detach"s); auto wait_all = app_node.get_optional("wait-all"s); auto exit_timeout = app_node.get_optional("exit-timeout"s); + auto virtual_display = app_node.get_optional("virtual-display"); std::vector prep_cmds; if (!exclude_global_prep.value_or(false)) { @@ -715,6 +743,7 @@ namespace proc { ctx.auto_detach = auto_detach.value_or(true); ctx.wait_all = wait_all.value_or(true); ctx.exit_timeout = std::chrono::seconds { exit_timeout.value_or(5) }; + ctx.virtual_display = virtual_display.value_or(false); auto possible_ids = calculate_app_id(name, ctx.image_path, i++); if (ids.count(std::get<0>(possible_ids)) == 0) { @@ -747,6 +776,12 @@ namespace proc { void refresh(const std::string &file_name) { + #ifdef _WIN32 + if (!vdisplayDriverInitialized) { + vdisplayDriverInitialized = VDISPLAY::openVDisplayDevice(); + } + #endif + auto proc_opt = proc::parse(file_name); if (proc_opt) { diff --git a/src/process.h b/src/process.h index fa0d8462..8044cc43 100644 --- a/src/process.h +++ b/src/process.h @@ -64,6 +64,7 @@ namespace proc { bool elevated; bool auto_detach; bool wait_all; + bool virtual_display; std::chrono::seconds exit_timeout; }; diff --git a/src_assets/common/assets/virtual_desktop.png b/src_assets/common/assets/virtual_desktop.png new file mode 100644 index 00000000..3fb94598 Binary files /dev/null and b/src_assets/common/assets/virtual_desktop.png differ diff --git a/src_assets/common/assets/web/apps.html b/src_assets/common/assets/web/apps.html index fa5cd1cd..f3452e0b 100644 --- a/src_assets/common/assets/web/apps.html +++ b/src_assets/common/assets/web/apps.html @@ -228,6 +228,13 @@ true-value="true" false-value="false" />
{{ $t('apps.wait_all_desc') }}
+ +
+ + +
{{ $t('apps.virtual_display_desc') }}
+
diff --git a/src_assets/common/assets/web/public/assets/locale/en.json b/src_assets/common/assets/web/public/assets/locale/en.json index 0da1fc4b..360922ac 100644 --- a/src_assets/common/assets/web/public/assets/locale/en.json +++ b/src_assets/common/assets/web/public/assets/locale/en.json @@ -79,7 +79,9 @@ "wait_all": "Continue streaming until all app processes exit", "wait_all_desc": "This will continue streaming until all processes started by the app have terminated. When unchecked, streaming will stop when the initial app process exits, even if other app processes are still running.", "working_dir": "Working Directory", - "working_dir_desc": "The working directory that should be passed to the process. For example, some applications use the working directory to search for configuration files. If not set, Apollo will default to the parent directory of the command" + "working_dir_desc": "The working directory that should be passed to the process. For example, some applications use the working directory to search for configuration files. If not set, Apollo will default to the parent directory of the command", + "virtual_display": "Use Virtual Display", + "virtual_display_desc": "Always use Virtual Display on this app, overriding client request." }, "config": { "adapter_name": "Adapter Name", diff --git a/third-party/sudovda/sudovda.h b/third-party/sudovda/sudovda.h index db1e2266..1e585d07 100644 --- a/third-party/sudovda/sudovda.h +++ b/third-party/sudovda/sudovda.h @@ -79,7 +79,7 @@ static const bool AddVirtualDisplay(HANDLE hDevice, UINT Width, UINT Height, UIN ); if (!success) { - std::cerr << "AddVirtualDisplay failed: " << GetLastError() << std::endl; + std::cerr << "[SUVDA] AddVirtualDisplay failed: " << GetLastError() << std::endl; } return success; @@ -100,7 +100,7 @@ static const bool RemoveVirtualDisplay(HANDLE hDevice, const GUID& MonitorGuid) ); if (!success) { - std::cerr << "RemoveVirtualDisplay failed: " << GetLastError() << std::endl; + std::cerr << "[SUVDA] RemoveVirtualDisplay failed: " << GetLastError() << std::endl; } return success; @@ -121,7 +121,7 @@ static const bool SetRenderAdapter(HANDLE hDevice, const LUID& AdapterLuid) { ); if (!success) { - std::cerr << "SetRenderAdapter failed: " << GetLastError() << std::endl; + std::cerr << "[SUVDA] SetRenderAdapter failed: " << GetLastError() << std::endl; } return success; @@ -141,7 +141,7 @@ static const bool GetWatchdogTimeout(HANDLE hDevice, VIRTUAL_DISPLAY_GET_WATCHDO ); if (!success) { - std::cerr << "DeviceIoControl failed: " << GetLastError() << std::endl; + std::cerr << "[SUVDA] GetWatchdogTimeout failed: " << GetLastError() << std::endl; } return success; @@ -161,7 +161,7 @@ static const bool GetProtocolVersion(HANDLE hDevice, VIRTUAL_DISPLAY_GET_PROTOCO ); if (!success) { - std::cerr << "DeviceIoControl failed: " << GetLastError() << std::endl; + std::cerr << "[SUVDA] GetProtocolVersion failed: " << GetLastError() << std::endl; } return success; @@ -206,7 +206,7 @@ static const bool PingDriver(HANDLE hDevice) { ); if (!success) { - std::cerr << "DeviceIoControl failed: " << GetLastError() << std::endl; + std::cerr << "[SUVDA] PingDriver failed: " << GetLastError() << std::endl; } return success;