Add app config to set virtual display as primary

This commit is contained in:
Yukino Song
2024-08-19 08:29:10 +08:00
parent 25eb5c8714
commit e0924e3a21
7 changed files with 75 additions and 15 deletions

View File

@@ -36,6 +36,24 @@ LONG changeDisplaySettings(const wchar_t* deviceName, int width, int height, int
return 0;
}
std::wstring getPrimaryDisplay() {
DISPLAY_DEVICEW displayDevice;
displayDevice.cb = sizeof(DISPLAY_DEVICE);
std::wstring primaryDeviceName;
int deviceIndex = 0;
while (EnumDisplayDevicesW(NULL, deviceIndex, &displayDevice, 0)) {
if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
primaryDeviceName = displayDevice.DeviceName;
break;
}
deviceIndex++;
}
return primaryDeviceName;
}
bool setPrimaryDisplay(const wchar_t* primaryDeviceName) {
DEVMODEW primaryDevMode{};
if (!getDeviceSettings(primaryDeviceName, primaryDevMode)) {
@@ -178,7 +196,7 @@ std::wstring createVirtualDisplay(
retryInterval *= 2;
}
wprintf(L"[SUDOVDA] Virtual display added successfully: %ws\n", deviceName);
wprintf(L"[SUDOVDA] Virtual display added successfully: %ls\n", deviceName);
printf("[SUDOVDA] Configuration: W: %d, H: %d, FPS: %d\n", width, height, fps);
return std::wstring(deviceName);

View File

@@ -13,6 +13,7 @@ namespace VDISPLAY {
LONG getDeviceSettings(const wchar_t* deviceName, DEVMODEW& devMode);
LONG changeDisplaySettings(const wchar_t* deviceName, int width, int height, int refresh_rate);
std::wstring getPrimaryDisplay();
bool setPrimaryDisplay(const wchar_t* primaryDeviceName);
bool startPingThread();

View File

@@ -208,15 +208,15 @@ namespace proc {
#ifdef _WIN32
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();
}
if (vdisplayDriverInitialized) {
std::wstring vdisplay_name = VDISPLAY::createVirtualDisplay(
std::wstring prevPrimaryDisplayName = VDISPLAY::getPrimaryDisplay();
std::wstring vdisplayName = VDISPLAY::createVirtualDisplay(
launch_session->unique_id.c_str(),
launch_session->device_name.c_str(),
_app.name.c_str(),
@@ -225,11 +225,33 @@ 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());
std::wstring currentPrimaryDisplayName = VDISPLAY::getPrimaryDisplay();
// Apply display settings
VDISPLAY::changeDisplaySettings(vdisplayName.c_str(), launch_session->width, launch_session->height, launch_session->fps);
// Determine if we need to set the virtual display as primary
bool shouldSetPrimary = false;
if (launch_session->virtual_display || _app.virtual_display_primary) {
shouldSetPrimary = (currentPrimaryDisplayName != vdisplayName);
} else {
shouldSetPrimary = (currentPrimaryDisplayName != prevPrimaryDisplayName);
}
// Set primary display if needed
if (shouldSetPrimary) {
VDISPLAY::setPrimaryDisplay(
(launch_session->virtual_display || _app.virtual_display_primary)
? vdisplayName.c_str()
: prevPrimaryDisplayName.c_str()
);
}
// Set virtual_display to true when everything went fine
this->virtual_display = true;
this->display_name = platf::to_utf8(vdisplayName);
}
}
#endif
@@ -369,7 +391,7 @@ namespace proc {
_pipe.reset();
#ifdef _WIN32
if (vdisplayDriverInitialized && _launch_session && _launch_session->virtual_display) {
if (vdisplayDriverInitialized && _launch_session && this->virtual_display) {
VDISPLAY::removeVirtualDisplay(_launch_session->display_guid);
}
#endif
@@ -387,6 +409,7 @@ namespace proc {
_app_id = -1;
display_name.clear();
_launch_session.reset();
virtual_display = false;
}
const std::vector<ctx_t> &
@@ -640,6 +663,7 @@ namespace proc {
ctx.name = "Virtual Display";
ctx.image_path = parse_env_val(this_env, "virtual_desktop.png");
ctx.virtual_display = true;
ctx.virtual_display_primary = true;
ctx.elevated = false;
ctx.auto_detach = true;
@@ -676,6 +700,7 @@ namespace proc {
auto wait_all = app_node.get_optional<bool>("wait-all"s);
auto exit_timeout = app_node.get_optional<int>("exit-timeout"s);
auto virtual_display = app_node.get_optional<bool>("virtual-display");
auto virtual_display_primary = app_node.get_optional<bool>("virtual-display-primary");
std::vector<proc::cmd_t> prep_cmds;
if (!exclude_global_prep.value_or(false)) {
@@ -744,6 +769,7 @@ namespace proc {
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);
ctx.virtual_display_primary = virtual_display_primary.value_or(true);
auto possible_ids = calculate_app_id(name, ctx.image_path, i++);
if (ids.count(std::get<0>(possible_ids)) == 0) {

View File

@@ -65,6 +65,7 @@ namespace proc {
bool auto_detach;
bool wait_all;
bool virtual_display;
bool virtual_display_primary;
std::chrono::seconds exit_timeout;
};
@@ -73,6 +74,7 @@ namespace proc {
KITTY_DEFAULT_CONSTR_MOVE_THROW(proc_t)
std::string display_name;
bool virtual_display;
proc_t(
boost::process::environment &&env,

View File

@@ -249,8 +249,6 @@ namespace system_tray {
return;
}
printf("Tray playing: %s\n", app_name.c_str());
tray.notification_title = NULL;
tray.notification_text = NULL;
tray.notification_cb = NULL;

View File

@@ -229,12 +229,19 @@
<div class="form-text">{{ $t('apps.wait_all_desc') }}</div>
</div>
<!-- use virtual display -->
<div class="mb-3 form-check">
<div class="mb-3 form-check" v-if="platform === 'windows'">
<label for="virtualDisplay" class="form-check-label">{{ $t('apps.virtual_display') }}</label>
<input type="checkbox" class="form-check-input" id="virtualDisplay" v-model="editForm['virtual-display']"
true-value="true" false-value="false" />
<div class="form-text">{{ $t('apps.virtual_display_desc') }}</div>
</div>
<!-- set virtual display to primary -->
<div class="mb-3 form-check" v-if="platform === 'windows' && editForm['virtual-display'] == 'true'">
<label for="virtualDisplayPrimary" class="form-check-label">{{ $t('apps.virtual_display_primary') }}</label>
<input type="checkbox" class="form-check-input" id="virtualDisplayPrimary" v-model="editForm['virtual-display-primary']"
true-value="true" false-value="false" />
<div class="form-text">{{ $t('apps.virtual_display_primary_desc') }}</div>
</div>
<!-- exit timeout -->
<div class="mb-3">
<label for="exitTimeout" class="form-label">{{ $t('apps.exit_timeout') }}</label>
@@ -373,11 +380,13 @@
import { createApp } from 'vue'
import { initApp } from './init'
import Navbar from './Navbar.vue'
import PlatformLayout from './PlatformLayout.vue'
import { Dropdown } from 'bootstrap/dist/js/bootstrap'
const app = createApp({
components: {
Navbar
Navbar,
PlatformLayout
},
data() {
return {
@@ -417,7 +426,8 @@
"exit-timeout": 5,
"prep-cmd": [],
detached: [],
"image-path": ""
"image-path": "",
"virtual-display-primary": true
};
this.editForm.index = -1;
this.showEditForm = true;
@@ -443,6 +453,9 @@
if (this.editForm["exit-timeout"] === undefined) {
this.editForm["exit-timeout"] = 5;
}
if (typeof this.editForm["virtual-display-primary"] === "undefined") {
this.editForm["virtual-display-primary"] = true;
}
this.showEditForm = true;
},
showDeleteForm(id) {

View File

@@ -81,7 +81,9 @@
"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",
"virtual_display": "Use Virtual Display",
"virtual_display_desc": "Always use Virtual Display on this app, overriding client request."
"virtual_display_desc": "Always use Virtual Display on this app, overriding client request. Please make sure the SudoVDA driver is installed and enabled.",
"virtual_display_primary": "Set Virtual Display as Primary Display",
"virtual_display_primary_desc": "Automatically set the Virtual Display as Primary Display when the app starts. (Recommended to keep on)"
},
"config": {
"adapter_name": "Adapter Name",