Add UUID to apps w/ add option to use App's id for virtual display (resolves #48)
This commit is contained in:
@@ -395,38 +395,8 @@ namespace confighttp {
|
|||||||
pt::read_json(ss, inputTree);
|
pt::read_json(ss, inputTree);
|
||||||
pt::read_json(config::stream.file_apps, fileTree);
|
pt::read_json(config::stream.file_apps, fileTree);
|
||||||
|
|
||||||
if (inputTree.get_child("prep-cmd").empty()) {
|
proc::migrate_apps(&fileTree, &inputTree);
|
||||||
inputTree.erase("prep-cmd");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputTree.get_child("detached").empty()) {
|
|
||||||
inputTree.erase("detached");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &apps_node = fileTree.get_child("apps"s);
|
|
||||||
int index = inputTree.get<int>("index");
|
|
||||||
|
|
||||||
inputTree.erase("index");
|
|
||||||
|
|
||||||
if (index == -1) {
|
|
||||||
apps_node.push_back(std::make_pair("", inputTree));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick
|
|
||||||
pt::ptree newApps;
|
|
||||||
int i = 0;
|
|
||||||
for (const auto &kv : apps_node) {
|
|
||||||
if (i == index) {
|
|
||||||
newApps.push_back(std::make_pair("", inputTree));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newApps.push_back(std::make_pair("", kv.second));
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
fileTree.erase("apps");
|
|
||||||
fileTree.push_back(std::make_pair("apps", newApps));
|
|
||||||
}
|
|
||||||
pt::write_json(config::stream.file_apps, fileTree);
|
pt::write_json(config::stream.file_apps, fileTree);
|
||||||
}
|
}
|
||||||
catch (std::exception &e) {
|
catch (std::exception &e) {
|
||||||
@@ -450,33 +420,38 @@ namespace confighttp {
|
|||||||
pt::ptree outputTree;
|
pt::ptree outputTree;
|
||||||
auto g = util::fail_guard([&]() {
|
auto g = util::fail_guard([&]() {
|
||||||
std::ostringstream data;
|
std::ostringstream data;
|
||||||
|
|
||||||
pt::write_json(data, outputTree);
|
pt::write_json(data, outputTree);
|
||||||
response->write(data.str());
|
response->write(data.str());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto args = request->parse_query_string();
|
||||||
|
if (
|
||||||
|
args.find("uuid"s) == std::end(args)
|
||||||
|
) {
|
||||||
|
outputTree.put("status", false);
|
||||||
|
outputTree.put("error", "Missing a required launch parameter");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto uuid = nvhttp::get_arg(args, "uuid");
|
||||||
|
|
||||||
pt::ptree fileTree;
|
pt::ptree fileTree;
|
||||||
try {
|
try {
|
||||||
pt::read_json(config::stream.file_apps, fileTree);
|
pt::read_json(config::stream.file_apps, fileTree);
|
||||||
auto &apps_node = fileTree.get_child("apps"s);
|
auto &apps_node = fileTree.get_child("apps"s);
|
||||||
int index = stoi(request->path_match[1]);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
outputTree.put("status", "false");
|
|
||||||
outputTree.put("error", "Invalid Index");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick
|
// Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick
|
||||||
pt::ptree newApps;
|
pt::ptree newApps;
|
||||||
int i = 0;
|
|
||||||
for (const auto &kv : apps_node) {
|
for (const auto &kv : apps_node) {
|
||||||
if (i++ != index) {
|
auto app_uuid = kv.second.get_optional<std::string>("uuid"s);
|
||||||
|
if (!app_uuid || app_uuid.value() != uuid) {
|
||||||
newApps.push_back(std::make_pair("", kv.second));
|
newApps.push_back(std::make_pair("", kv.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileTree.erase("apps");
|
fileTree.erase("apps");
|
||||||
fileTree.push_back(std::make_pair("apps", newApps));
|
fileTree.push_back(std::make_pair("apps", newApps));
|
||||||
}
|
|
||||||
pt::write_json(config::stream.file_apps, fileTree);
|
pt::write_json(config::stream.file_apps, fileTree);
|
||||||
}
|
}
|
||||||
catch (std::exception &e) {
|
catch (std::exception &e) {
|
||||||
@@ -951,7 +926,7 @@ namespace confighttp {
|
|||||||
|
|
||||||
auto args = request->parse_query_string();
|
auto args = request->parse_query_string();
|
||||||
if (
|
if (
|
||||||
args.find("id"s) == std::end(args)
|
args.find("uuid"s) == std::end(args)
|
||||||
) {
|
) {
|
||||||
outputTree.put("status", false);
|
outputTree.put("status", false);
|
||||||
outputTree.put("error", "Missing a required launch parameter");
|
outputTree.put("error", "Missing a required launch parameter");
|
||||||
@@ -959,19 +934,12 @@ namespace confighttp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto idx_str = nvhttp::get_arg(args, "id");
|
auto uuid = nvhttp::get_arg(args, "uuid");
|
||||||
auto idx = util::from_view(idx_str);
|
|
||||||
|
|
||||||
const auto& apps = proc::proc.get_apps();
|
const auto& apps = proc::proc.get_apps();
|
||||||
|
|
||||||
if (idx >= apps.size()) {
|
for (auto& app : apps) {
|
||||||
BOOST_LOG(error) << "Couldn't find app with index ["sv << idx_str << ']';
|
if (app.uuid == uuid) {
|
||||||
outputTree.put("status", false);
|
|
||||||
outputTree.put("error", "Cannot find requested application");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& app = apps[idx];
|
|
||||||
auto appid = util::from_view(app.id);
|
auto appid = util::from_view(app.id);
|
||||||
|
|
||||||
crypto::named_cert_t named_cert {
|
crypto::named_cert_t named_cert {
|
||||||
@@ -994,6 +962,14 @@ namespace confighttp {
|
|||||||
} else {
|
} else {
|
||||||
outputTree.put("status", true);
|
outputTree.put("status", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG(error) << "Couldn't find app with uuid ["sv << uuid << ']';
|
||||||
|
outputTree.put("status", false);
|
||||||
|
outputTree.put("error", "Cannot find requested application");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1086,22 +1062,22 @@ namespace confighttp {
|
|||||||
server.resource["^/api/pin$"]["POST"] = savePin;
|
server.resource["^/api/pin$"]["POST"] = savePin;
|
||||||
server.resource["^/api/otp$"]["GET"] = getOTP;
|
server.resource["^/api/otp$"]["GET"] = getOTP;
|
||||||
server.resource["^/api/apps$"]["GET"] = getApps;
|
server.resource["^/api/apps$"]["GET"] = getApps;
|
||||||
server.resource["^/api/logs$"]["GET"] = getLogs;
|
|
||||||
server.resource["^/api/apps$"]["POST"] = saveApp;
|
server.resource["^/api/apps$"]["POST"] = saveApp;
|
||||||
|
server.resource["^/api/apps/delete$"]["POST"] = deleteApp;
|
||||||
|
server.resource["^/api/apps/launch$"]["POST"] = launchApp;
|
||||||
|
server.resource["^/api/apps/close$"]["POST"] = closeApp;
|
||||||
|
server.resource["^/api/logs$"]["GET"] = getLogs;
|
||||||
server.resource["^/api/config$"]["GET"] = getConfig;
|
server.resource["^/api/config$"]["GET"] = getConfig;
|
||||||
server.resource["^/api/config$"]["POST"] = saveConfig;
|
server.resource["^/api/config$"]["POST"] = saveConfig;
|
||||||
server.resource["^/api/configLocale$"]["GET"] = getLocale;
|
server.resource["^/api/configLocale$"]["GET"] = getLocale;
|
||||||
server.resource["^/api/restart$"]["POST"] = restart;
|
server.resource["^/api/restart$"]["POST"] = restart;
|
||||||
server.resource["^/api/quit$"]["POST"] = quit;
|
server.resource["^/api/quit$"]["POST"] = quit;
|
||||||
server.resource["^/api/password$"]["POST"] = savePassword;
|
server.resource["^/api/password$"]["POST"] = savePassword;
|
||||||
server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp;
|
|
||||||
server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll;
|
server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll;
|
||||||
server.resource["^/api/clients/list$"]["GET"] = listClients;
|
server.resource["^/api/clients/list$"]["GET"] = listClients;
|
||||||
server.resource["^/api/clients/update$"]["POST"] = updateClient;
|
server.resource["^/api/clients/update$"]["POST"] = updateClient;
|
||||||
server.resource["^/api/clients/unpair$"]["POST"] = unpair;
|
server.resource["^/api/clients/unpair$"]["POST"] = unpair;
|
||||||
server.resource["^/api/clients/disconnect$"]["POST"] = disconnect;
|
server.resource["^/api/clients/disconnect$"]["POST"] = disconnect;
|
||||||
server.resource["^/api/apps/launch$"]["POST"] = launchApp;
|
|
||||||
server.resource["^/api/apps/close$"]["POST"] = closeApp;
|
|
||||||
server.resource["^/api/covers/upload$"]["POST"] = uploadCover;
|
server.resource["^/api/covers/upload$"]["POST"] = uploadCover;
|
||||||
server.resource["^/images/apollo.ico$"]["GET"] = getFaviconImage;
|
server.resource["^/images/apollo.ico$"]["GET"] = getFaviconImage;
|
||||||
server.resource["^/images/logo-apollo-45.png$"]["GET"] = getSunshineLogoImage;
|
server.resource["^/images/logo-apollo-45.png$"]["GET"] = getSunshineLogoImage;
|
||||||
|
|||||||
@@ -211,13 +211,16 @@ namespace proc {
|
|||||||
VDISPLAY::setRenderAdapterByName(platf::from_utf8(config::video.adapter_name));
|
VDISPLAY::setRenderAdapterByName(platf::from_utf8(config::video.adapter_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto device_uuid = uuid_util::uuid_t::parse(launch_session->unique_id);
|
auto device_uuid_str = _app.use_app_identity ? _app.uuid : launch_session->unique_id;
|
||||||
|
auto device_name = _app.use_app_identity ? _app.name : launch_session->device_name;
|
||||||
|
|
||||||
|
auto device_uuid = uuid_util::uuid_t::parse(device_uuid_str);
|
||||||
|
|
||||||
memcpy(&launch_session->display_guid, &device_uuid, sizeof(GUID));
|
memcpy(&launch_session->display_guid, &device_uuid, sizeof(GUID));
|
||||||
|
|
||||||
std::wstring vdisplayName = VDISPLAY::createVirtualDisplay(
|
std::wstring vdisplayName = VDISPLAY::createVirtualDisplay(
|
||||||
launch_session->unique_id.c_str(),
|
device_uuid_str.c_str(),
|
||||||
launch_session->device_name.c_str(),
|
device_name.c_str(),
|
||||||
render_width,
|
render_width,
|
||||||
render_height,
|
render_height,
|
||||||
launch_session->fps ? launch_session->fps : 60,
|
launch_session->fps ? launch_session->fps : 60,
|
||||||
@@ -794,6 +797,63 @@ namespace proc {
|
|||||||
return std::make_tuple(id_no_index, id_with_index);
|
return std::make_tuple(id_no_index, id_with_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
migrate_apps(pt::ptree* fileTree_p, pt::ptree* inputTree_p) {
|
||||||
|
std::string new_app_uuid;
|
||||||
|
|
||||||
|
if (inputTree_p) {
|
||||||
|
auto inputTree = *inputTree_p;
|
||||||
|
auto input_uuid = inputTree_p->get_optional<std::string>("uuid"s);
|
||||||
|
if (input_uuid && !input_uuid.value().empty()) {
|
||||||
|
new_app_uuid = input_uuid.value();
|
||||||
|
} else {
|
||||||
|
new_app_uuid = uuid_util::uuid_t::generate().string();
|
||||||
|
inputTree_p->erase("uuid");
|
||||||
|
inputTree_p->put("uuid", new_app_uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputTree_p->get_child("prep-cmd").empty()) {
|
||||||
|
inputTree_p->erase("prep-cmd");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputTree_p->get_child("detached").empty()) {
|
||||||
|
inputTree_p->erase("detached");
|
||||||
|
}
|
||||||
|
|
||||||
|
inputTree_p->erase("launching");
|
||||||
|
inputTree_p->erase("index");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &apps_node = fileTree_p->get_child("apps"s);
|
||||||
|
|
||||||
|
pt::ptree newApps;
|
||||||
|
for (auto &kv : apps_node) {
|
||||||
|
// Check if we have apps that have not got an uuid assigned
|
||||||
|
auto app_uuid = kv.second.get_optional<std::string>("uuid"s);
|
||||||
|
if (!app_uuid || app_uuid.value().empty()) {
|
||||||
|
kv.second.erase("uuid");
|
||||||
|
kv.second.put("uuid", uuid_util::uuid_t::generate().string());
|
||||||
|
kv.second.erase("launching");
|
||||||
|
newApps.push_back(std::make_pair("", kv.second));
|
||||||
|
} else {
|
||||||
|
if (!new_app_uuid.empty() && app_uuid.value() == new_app_uuid) {
|
||||||
|
newApps.push_back(std::make_pair("", *inputTree_p));
|
||||||
|
new_app_uuid.clear();
|
||||||
|
} else {
|
||||||
|
newApps.push_back(std::make_pair("", kv.second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally add the new app
|
||||||
|
if (!new_app_uuid.empty()) {
|
||||||
|
newApps.push_back(std::make_pair("", *inputTree_p));
|
||||||
|
}
|
||||||
|
|
||||||
|
fileTree_p->erase("apps");
|
||||||
|
fileTree_p->push_back(std::make_pair("apps", newApps));
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<proc::proc_t>
|
std::optional<proc::proc_t>
|
||||||
parse(const std::string &file_name) {
|
parse(const std::string &file_name) {
|
||||||
pt::ptree tree;
|
pt::ptree tree;
|
||||||
@@ -816,6 +876,20 @@ namespace proc {
|
|||||||
for (auto &[_, app_node] : apps_node) {
|
for (auto &[_, app_node] : apps_node) {
|
||||||
proc::ctx_t ctx;
|
proc::ctx_t ctx;
|
||||||
|
|
||||||
|
auto app_uuid = app_node.get_optional<std::string>("uuid"s);
|
||||||
|
|
||||||
|
if (!app_uuid) {
|
||||||
|
// We need an upgrade to the app list
|
||||||
|
try {
|
||||||
|
migrate_apps(&tree, nullptr);
|
||||||
|
pt::write_json(file_name, tree);
|
||||||
|
return parse(file_name);
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
BOOST_LOG(warning) << "Error happened wilie migrating the app list: "sv << e.what();
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s);
|
auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s);
|
||||||
auto detached_nodes_opt = app_node.get_child_optional("detached"s);
|
auto detached_nodes_opt = app_node.get_child_optional("detached"s);
|
||||||
auto exclude_global_prep = app_node.get_optional<bool>("exclude-global-prep-cmd"s);
|
auto exclude_global_prep = app_node.get_optional<bool>("exclude-global-prep-cmd"s);
|
||||||
@@ -831,6 +905,9 @@ namespace proc {
|
|||||||
auto virtual_display = app_node.get_optional<bool>("virtual-display"s);
|
auto virtual_display = app_node.get_optional<bool>("virtual-display"s);
|
||||||
auto virtual_display_primary = app_node.get_optional<bool>("virtual-display-primary"s);
|
auto virtual_display_primary = app_node.get_optional<bool>("virtual-display-primary"s);
|
||||||
auto resolution_scale_factor = app_node.get_optional<int>("scale-factor"s);
|
auto resolution_scale_factor = app_node.get_optional<int>("scale-factor"s);
|
||||||
|
auto use_app_identity = app_node.get_optional<bool>("use-app-identity"s);
|
||||||
|
|
||||||
|
ctx.uuid = app_uuid.value();
|
||||||
|
|
||||||
std::vector<proc::cmd_t> prep_cmds;
|
std::vector<proc::cmd_t> prep_cmds;
|
||||||
if (!exclude_global_prep.value_or(false)) {
|
if (!exclude_global_prep.value_or(false)) {
|
||||||
@@ -901,6 +978,7 @@ namespace proc {
|
|||||||
ctx.virtual_display = virtual_display.value_or(false);
|
ctx.virtual_display = virtual_display.value_or(false);
|
||||||
ctx.virtual_display_primary = virtual_display_primary.value_or(true);
|
ctx.virtual_display_primary = virtual_display_primary.value_or(true);
|
||||||
ctx.scale_factor = resolution_scale_factor.value_or(100);
|
ctx.scale_factor = resolution_scale_factor.value_or(100);
|
||||||
|
ctx.use_app_identity = use_app_identity.value_or(false);
|
||||||
|
|
||||||
auto possible_ids = calculate_app_id(name, ctx.image_path, i++);
|
auto possible_ids = calculate_app_id(name, ctx.image_path, i++);
|
||||||
if (ids.count(std::get<0>(possible_ids)) == 0) {
|
if (ids.count(std::get<0>(possible_ids)) == 0) {
|
||||||
@@ -923,11 +1001,13 @@ namespace proc {
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK) {
|
if (vDisplayDriverStatus == VDISPLAY::DRIVER_STATUS::OK) {
|
||||||
proc::ctx_t ctx;
|
proc::ctx_t ctx;
|
||||||
|
// ctx.uuid = ""; // We're not using uuid for this special entry
|
||||||
ctx.name = "Virtual Display";
|
ctx.name = "Virtual Display";
|
||||||
ctx.image_path = parse_env_val(this_env, "virtual_desktop.png");
|
ctx.image_path = parse_env_val(this_env, "virtual_desktop.png");
|
||||||
ctx.virtual_display = true;
|
ctx.virtual_display = true;
|
||||||
ctx.virtual_display_primary = true;
|
ctx.virtual_display_primary = true;
|
||||||
ctx.scale_factor = 100;
|
ctx.scale_factor = 100;
|
||||||
|
ctx.use_app_identity = false;
|
||||||
|
|
||||||
ctx.elevated = false;
|
ctx.elevated = false;
|
||||||
ctx.auto_detach = true;
|
ctx.auto_detach = true;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <boost/process.hpp>
|
#include <boost/process.hpp>
|
||||||
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "platform/common.h"
|
#include "platform/common.h"
|
||||||
@@ -59,6 +60,7 @@ namespace proc {
|
|||||||
*/
|
*/
|
||||||
std::vector<std::string> detached;
|
std::vector<std::string> detached;
|
||||||
|
|
||||||
|
std::string uuid;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string cmd;
|
std::string cmd;
|
||||||
std::string working_dir;
|
std::string working_dir;
|
||||||
@@ -70,6 +72,7 @@ namespace proc {
|
|||||||
bool wait_all;
|
bool wait_all;
|
||||||
bool virtual_display;
|
bool virtual_display;
|
||||||
bool virtual_display_primary;
|
bool virtual_display_primary;
|
||||||
|
bool use_app_identity;
|
||||||
int scale_factor;
|
int scale_factor;
|
||||||
std::chrono::seconds exit_timeout;
|
std::chrono::seconds exit_timeout;
|
||||||
};
|
};
|
||||||
@@ -150,6 +153,8 @@ namespace proc {
|
|||||||
validate_app_image_path(std::string app_image_path);
|
validate_app_image_path(std::string app_image_path);
|
||||||
void
|
void
|
||||||
refresh(const std::string &file_name);
|
refresh(const std::string &file_name);
|
||||||
|
void
|
||||||
|
migrate_apps(boost::property_tree::ptree* fileTree_p, boost::property_tree::ptree* inputTree_p);
|
||||||
std::optional<proc::proc_t>
|
std::optional<proc::proc_t>
|
||||||
parse(const std::string &file_name);
|
parse(const std::string &file_name);
|
||||||
|
|
||||||
|
|||||||
@@ -86,16 +86,16 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(app,i) in apps" :key="i">
|
<tr v-for="(app,i) in apps" :key="app.uuid">
|
||||||
<td>{{app.name}}</td>
|
<td>{{app.name}}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-success me-2" :disabled="app.launching" @click="launchApp(i)">
|
<button class="btn btn-success me-2" :disabled="app.launching" @click="launchApp(app)">
|
||||||
<i class="fas fa-play"></i> {{ $t('apps.launch') }}
|
<i class="fas fa-play"></i> {{ $t('apps.launch') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary me-2" :disabled="app.launching" @click="editApp(i)">
|
<button class="btn btn-primary me-2" :disabled="app.launching" @click="editApp(app)">
|
||||||
<i class="fas fa-edit"></i> {{ $t('apps.edit') }}
|
<i class="fas fa-edit"></i> {{ $t('apps.edit') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger" :disabled="app.launching" @click="showDeleteForm(i)">
|
<button class="btn btn-danger" :disabled="app.launching" @click="showDeleteForm(app)">
|
||||||
<i class="fas fa-trash"></i> {{ $t('apps.delete') }}
|
<i class="fas fa-trash"></i> {{ $t('apps.delete') }}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
@@ -284,6 +284,13 @@
|
|||||||
true-value="true" false-value="false" />
|
true-value="true" false-value="false" />
|
||||||
<div class="form-text">{{ $t('apps.virtual_display_primary_desc') }}</div>
|
<div class="form-text">{{ $t('apps.virtual_display_primary_desc') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- use app identity -->
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
<label for="useAppIdentity" class="form-check-label">{{ $t('apps.use_app_identity') }}</label>
|
||||||
|
<input type="checkbox" class="form-check-input" id="useAppIdentity" v-model="editForm['use-app-identity']"
|
||||||
|
true-value="true" false-value="false" />
|
||||||
|
<div class="form-text">{{ $t('apps.use_app_identity_desc') }}</div>
|
||||||
|
</div>
|
||||||
<!-- resolution scale factor -->
|
<!-- resolution scale factor -->
|
||||||
<div class="mb-3" v-if="platform === 'windows'">
|
<div class="mb-3" v-if="platform === 'windows'">
|
||||||
<label for="resolutionScaleFactor" class="form-label">{{ $t('apps.resolution_scale_factor') }}: {{editForm['scale-factor']}}%</label>
|
<label for="resolutionScaleFactor" class="form-label">{{ $t('apps.resolution_scale_factor') }}: {{editForm['scale-factor']}}%</label>
|
||||||
@@ -392,6 +399,23 @@
|
|||||||
import PlatformLayout from './PlatformLayout.vue'
|
import PlatformLayout from './PlatformLayout.vue'
|
||||||
import { Dropdown } from 'bootstrap/dist/js/bootstrap'
|
import { Dropdown } from 'bootstrap/dist/js/bootstrap'
|
||||||
|
|
||||||
|
const newApp = {
|
||||||
|
name: "",
|
||||||
|
output: "",
|
||||||
|
cmd: [],
|
||||||
|
"exclude-global-prep-cmd": false,
|
||||||
|
elevated: false,
|
||||||
|
"auto-detach": true,
|
||||||
|
"wait-all": true,
|
||||||
|
"exit-timeout": 5,
|
||||||
|
"prep-cmd": [],
|
||||||
|
detached: [],
|
||||||
|
"image-path": "",
|
||||||
|
"virtual-display-primary": true,
|
||||||
|
"scale-factor": "100",
|
||||||
|
"use-app-identity": false
|
||||||
|
}
|
||||||
|
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
components: {
|
components: {
|
||||||
Navbar,
|
Navbar,
|
||||||
@@ -429,30 +453,13 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
newApp() {
|
newApp() {
|
||||||
this.editForm = {
|
this.editForm = Object.assign({}, newApp);
|
||||||
name: "",
|
|
||||||
output: "",
|
|
||||||
cmd: [],
|
|
||||||
index: -1,
|
|
||||||
"exclude-global-prep-cmd": false,
|
|
||||||
elevated: false,
|
|
||||||
"auto-detach": true,
|
|
||||||
"wait-all": true,
|
|
||||||
"exit-timeout": 5,
|
|
||||||
"prep-cmd": [],
|
|
||||||
detached: [],
|
|
||||||
"image-path": "",
|
|
||||||
"virtual-display-primary": true,
|
|
||||||
"scale-factor": "100"
|
|
||||||
};
|
|
||||||
this.editForm.index = -1;
|
|
||||||
this.showEditForm = true;
|
this.showEditForm = true;
|
||||||
},
|
},
|
||||||
launchApp(id) {
|
launchApp(app) {
|
||||||
const app = this.apps[id];
|
|
||||||
if (confirm(this.$t('apps.launch_warning'))) {
|
if (confirm(this.$t('apps.launch_warning'))) {
|
||||||
app.launching = true;
|
app.launching = true;
|
||||||
fetch("/api/apps/launch?id=" + id, {
|
fetch("/api/apps/launch?uuid=" + app.uuid, {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
method: "POST",
|
method: "POST",
|
||||||
})
|
})
|
||||||
@@ -467,56 +474,31 @@
|
|||||||
.finally(() => app.launching = false);
|
.finally(() => app.launching = false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editApp(id) {
|
editApp(app) {
|
||||||
this.editForm = JSON.parse(JSON.stringify(this.apps[id]));
|
this.editForm = Object.assign({}, newApp, JSON.parse(JSON.stringify(app)));
|
||||||
this.editForm.index = id;
|
|
||||||
if (this.editForm["prep-cmd"] === undefined)
|
|
||||||
this.editForm["prep-cmd"] = [];
|
|
||||||
if (this.editForm["detached"] === undefined)
|
|
||||||
this.editForm["detached"] = [];
|
|
||||||
if (this.editForm["exclude-global-prep-cmd"] === undefined)
|
|
||||||
this.editForm["exclude-global-prep-cmd"] = [];
|
|
||||||
if (this.editForm["elevated"] === undefined && this.platform === 'windows') {
|
|
||||||
this.editForm["elevated"] = [];
|
|
||||||
}
|
|
||||||
if (this.editForm["auto-detach"] === undefined) {
|
|
||||||
this.editForm["auto-detach"] = true;
|
|
||||||
}
|
|
||||||
if (this.editForm["wait-all"] === undefined) {
|
|
||||||
this.editForm["wait-all"] = true;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (typeof this.editForm["scale-factor"] === "undefined") {
|
|
||||||
this.editForm["scale-factor"] = "100"
|
|
||||||
}
|
|
||||||
this.showEditForm = true;
|
this.showEditForm = true;
|
||||||
},
|
},
|
||||||
showDeleteForm(id) {
|
showDeleteForm(app) {
|
||||||
let resp = confirm(
|
const resp = confirm(
|
||||||
"Are you sure to delete " + this.apps[id].name + "?"
|
"Are you sure to delete " + app.name + "?"
|
||||||
);
|
);
|
||||||
if (resp) {
|
if (resp) {
|
||||||
fetch("/api/apps/" + id, {
|
fetch("/api/apps/delete?uuid=" + app.uuid, {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
method: "DELETE"
|
method: "POST"
|
||||||
}).then((r) => {
|
}).then((r) => {
|
||||||
if (r.status == 200) document.location.reload();
|
if (r.status == 200) document.location.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addPrepCmd() {
|
addPrepCmd() {
|
||||||
let template = {
|
const template = {
|
||||||
do: "",
|
do: "",
|
||||||
undo: ""
|
undo: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.platform === 'windows') {
|
if (this.platform === 'windows') {
|
||||||
template = { ...template, elevated: false };
|
template.elevated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editForm["prep-cmd"].push(template);
|
this.editForm["prep-cmd"].push(template);
|
||||||
@@ -618,6 +600,8 @@
|
|||||||
},
|
},
|
||||||
save() {
|
save() {
|
||||||
this.editForm["image-path"] = this.editForm["image-path"].toString().replace(/"/g, '');
|
this.editForm["image-path"] = this.editForm["image-path"].toString().replace(/"/g, '');
|
||||||
|
delete this.editForm["launching"];
|
||||||
|
delete this.editForm["id"];
|
||||||
fetch("/api/apps", {
|
fetch("/api/apps", {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -427,9 +427,11 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error(e)
|
console.error(e);
|
||||||
|
setTimeout(() => {
|
||||||
location.reload();
|
location.reload();
|
||||||
return
|
}, 1000);
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -92,7 +92,9 @@
|
|||||||
"virtual_display_primary": "Enforce Virtual Display Primary",
|
"virtual_display_primary": "Enforce Virtual Display Primary",
|
||||||
"virtual_display_primary_desc": "Automatically set the virtual display as primary display when the app starts. Virtual display will always be set to primary when client requests to use virtual display. (Recommended to keep on)",
|
"virtual_display_primary_desc": "Automatically set the virtual display as primary display when the app starts. Virtual display will always be set to primary when client requests to use virtual display. (Recommended to keep on)",
|
||||||
"resolution_scale_factor": "Resolution Scale Factor",
|
"resolution_scale_factor": "Resolution Scale Factor",
|
||||||
"resolution_scale_factor_desc": "Scale the client requested resolution based on this factor. e.g. 2000x1000 with a factor of 120% will become 2400x1200. Overrides client requested factor when the number isn't 100%. This option won't affect client requested streaming resolution."
|
"resolution_scale_factor_desc": "Scale the client requested resolution based on this factor. e.g. 2000x1000 with a factor of 120% will become 2400x1200. Overrides client requested factor when the number isn't 100%. This option won't affect client requested streaming resolution.",
|
||||||
|
"use_app_identity": "Use App Identity",
|
||||||
|
"use_app_identity_desc": "Use the app's own identity while creating virtual displays instead of client's. This is useful when you want display configuration for each APP separately."
|
||||||
},
|
},
|
||||||
"client_card": {
|
"client_card": {
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
|
|||||||
@@ -90,7 +90,9 @@
|
|||||||
"virtual_display_primary": "强制设置虚拟显示器为主显示器",
|
"virtual_display_primary": "强制设置虚拟显示器为主显示器",
|
||||||
"virtual_display_primary_desc": "在 App 启动时强制将虚拟显示器设为主显示器。当客户端请求使用虚拟显示器时将无条件设为主显示器。(覆盖全局设置,推荐保持开启)",
|
"virtual_display_primary_desc": "在 App 启动时强制将虚拟显示器设为主显示器。当客户端请求使用虚拟显示器时将无条件设为主显示器。(覆盖全局设置,推荐保持开启)",
|
||||||
"resolution_scale_factor": "分辨率缩放比例",
|
"resolution_scale_factor": "分辨率缩放比例",
|
||||||
"resolution_scale_factor_desc": "基于此比例缩放客户端请求的分辨率。例如 2000x1000 缩放 120% 将变成 2400x1200。当此项为非 100% 时覆盖客户端请求的缩放比例。此选项不会影响客户端请求的串流分辨率。"
|
"resolution_scale_factor_desc": "基于此比例缩放客户端请求的分辨率。例如 2000x1000 缩放 120% 将变成 2400x1200。当此项为非 100% 时覆盖客户端请求的缩放比例。此选项不会影响客户端请求的串流分辨率。",
|
||||||
|
"use_app_identity": "使用 App 身份",
|
||||||
|
"use_app_identity_desc": "在创建虚拟显示器时使用 App 自身的身份,而非客户端的。这样可以针对 APP 进行单独的显示器组合配置。"
|
||||||
},
|
},
|
||||||
"client_card": {
|
"client_card": {
|
||||||
"clients": "客户端",
|
"clients": "客户端",
|
||||||
|
|||||||
@@ -189,15 +189,19 @@
|
|||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.status !== 200) {
|
if (resp.status !== 200) {
|
||||||
|
setTimeout(() => {
|
||||||
location.reload();
|
location.reload();
|
||||||
return
|
}, 1000);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
this.serverRestarting = false
|
this.serverRestarting = false
|
||||||
console.error(e)
|
console.error(e);
|
||||||
|
setTimeout(() => {
|
||||||
location.reload();
|
location.reload();
|
||||||
return
|
}, 1000);
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
quit() {
|
quit() {
|
||||||
|
|||||||
Reference in New Issue
Block a user