Add option to quit Apollo from webpage w/ hide tray menu controls

This commit is contained in:
Yukino Song
2024-08-24 04:01:38 +08:00
parent e06e1429fa
commit 276a0496a8
10 changed files with 125 additions and 16 deletions

View File

@@ -439,6 +439,7 @@ namespace config {
};
sunshine_t sunshine {
false, // hide_tray_controls
"en", // locale
2, // min_log_level
0, // flags
@@ -1102,6 +1103,7 @@ namespace config {
bool_f(vars, "high_resolution_scrolling", input.high_resolution_scrolling);
bool_f(vars, "native_pen_touch", input.native_pen_touch);
bool_f(vars, "hide_tray_controls", sunshine.hide_tray_controls);
bool_f(vars, "notify_pre_releases", sunshine.notify_pre_releases);
int port = sunshine.port;

View File

@@ -163,6 +163,7 @@ namespace config {
bool elevated;
};
struct sunshine_t {
bool hide_tray_controls;
std::string locale;
int min_log_level;
std::bitset<flag::FLAG_SIZE> flags;

View File

@@ -621,6 +621,32 @@ namespace confighttp {
platf::restart();
}
void
quit(resp_https_t response, req_https_t request) {
if (!authenticate(response, request)) return;
print_req(request);
// We do want to return here
std::thread quit_thread([]{
sleep(1000);
#ifdef _WIN32
// If we're running in a service, return a special status to
// tell it to terminate too, otherwise it will just respawn us.
if (GetConsoleWindow() == NULL) {
lifetime::exit_sunshine(ERROR_SHUTDOWN_IN_PROGRESS, true);
return;
}
#endif
lifetime::exit_sunshine(0, true);
});
quit_thread.detach();
response->write();
}
void
savePassword(resp_https_t response, req_https_t request) {
if (!config::sunshine.username.empty() && !authenticate(response, request)) return;
@@ -825,6 +851,7 @@ namespace confighttp {
server.resource["^/api/config$"]["POST"] = saveConfig;
server.resource["^/api/configLocale$"]["GET"] = getLocale;
server.resource["^/api/restart$"]["POST"] = restart;
server.resource["^/api/quit$"]["POST"] = quit;
server.resource["^/api/password$"]["POST"] = savePassword;
server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp;
server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll;

View File

@@ -289,7 +289,7 @@ main(int argc, char *argv[]) {
BOOST_LOG(fatal) << "HTTP interface failed to initialize"sv;
#ifdef _WIN32
BOOST_LOG(fatal) << "To relaunch Sunshine successfully, use the shortcut in the Start Menu. Do not run Sunshine.exe manually."sv;
BOOST_LOG(fatal) << "To relaunch Apollo successfully, use the shortcut in the Start Menu. Do not run Sunshine.exe manually."sv;
std::this_thread::sleep_for(10s);
#endif
@@ -317,8 +317,8 @@ main(int argc, char *argv[]) {
#ifdef _WIN32
// If we're using the default port and GameStream is enabled, warn the user
if (config::sunshine.port == 47989 && is_gamestream_enabled()) {
BOOST_LOG(fatal) << "GameStream is still enabled in GeForce Experience! This *will* cause streaming problems with Sunshine!"sv;
BOOST_LOG(fatal) << "Disable GameStream on the SHIELD tab in GeForce Experience or change the Port setting on the Advanced tab in the Sunshine Web UI."sv;
BOOST_LOG(fatal) << "GameStream is still enabled in GeForce Experience! This *will* cause streaming problems with Apollo!"sv;
BOOST_LOG(fatal) << "Disable GameStream on the SHIELD tab in GeForce Experience or change the Port setting on the Advanced tab in the Apollo Web UI."sv;
}
#endif

View File

@@ -97,6 +97,7 @@ namespace system_tray {
(struct tray_menu[]) {
// todo - use boost/locale to translate menu strings
{ .text = "Open Apollo", .cb = tray_open_ui_cb },
{ .text = "-" },
// { .text = "-" },
// { .text = "Donate",
// .submenu =
@@ -231,6 +232,10 @@ namespace system_tray {
#endif
tray.menu[0].text = title_str.c_str();
if (config::sunshine.hide_tray_controls) {
tray.menu[1].text = nullptr;
}
std::thread tray_thread(system_tray);
tray_thread.detach();
#endif
@@ -269,7 +274,7 @@ namespace system_tray {
tray.notification_text = msg;
tray.tooltip = msg;
tray.notification_icon = TRAY_ICON_PLAYING;
tray.menu[1].text = force_close_msg;
tray.menu[2].text = force_close_msg;
tray_update(&tray);
}
@@ -320,7 +325,7 @@ namespace system_tray {
tray.notification_title = "Application Stopped";
tray.notification_text = msg;
tray.tooltip = PROJECT_NAME;
tray.menu[1].text = TRAY_MSG_NO_APP_RUNNING;
tray.menu[2].text = TRAY_MSG_NO_APP_RUNNING;
tray_update(&tray);
}

View File

@@ -129,6 +129,13 @@ function removeCmd(index) {
</button>
</div>
<!--hide_tray_controls-->
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="hide_tray_controls" v-model="config.hide_tray_controls" true-value="enabled" false-value="disabled"/>
<label for="qp" class="form-check-label">{{ $t('config.hide_tray_controls') }}</label>
<div class="form-text">{{ $t('config.hide_tray_controls_desc') }}</div>
</div>
<!-- Notify Pre-Releases -->
<div class="mb-3">
<label for="notify_pre_releases" class="form-label">{{ $t('config.notify_pre_releases') }}</label>

View File

@@ -36,8 +36,8 @@ const fpsIn = ref("")
</div>
<!--headless_mode-->
<div class="mb-3">
<input type="checkbox" min="1" max="3" class="form-check-input" id="headless_mode" placeholder="1" v-model="config.headless_mode" true-value="enabled" false-value="disabled"/>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="headless_mode" v-model="config.headless_mode" true-value="enabled" false-value="disabled"/>
<label for="qp" class="form-check-label">{{ $t('config.headless_mode') }}</label>
<div class="form-text">{{ $t('config.headless_mode_desc') }}</div>
</div>

View File

@@ -191,6 +191,8 @@
"hevc_mode_2": "Apollo will advertise support for HEVC Main profile",
"hevc_mode_3": "Apollo will advertise support for HEVC Main and Main10 (HDR) profiles",
"hevc_mode_desc": "Allows the client to request HEVC Main or HEVC Main10 video streams. HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding.",
"hide_tray_controls": "Hide tray control options",
"hide_tray_controls_desc": "Do not show \"Force Stop\", \"Restart\" and \"Quit\" in tray menu.",
"high_resolution_scrolling": "High Resolution Scrolling Support",
"high_resolution_scrolling_desc": "When enabled, Apollo will pass through high resolution scroll events from Moonlight clients. This can be useful to disable for older applications that scroll too fast with high resolution scroll events.",
"install_steam_audio_drivers": "Install Steam Audio Drivers",
@@ -394,6 +396,10 @@
"restart_apollo": "Restart Apollo",
"restart_apollo_desc": "If Apollo isn't working properly, you can try restarting it. This will terminate any running sessions.",
"restart_apollo_success": "Apollo is restarting",
"quit_apollo": "Quit Apollo",
"quit_apollo_desc": "Exit Apollo. This will terminate any running sessions.",
"quit_apollo_success": "Apollo has exited.",
"quit_apollo_success_ongoing": "Apollo is quitting...",
"troubleshooting": "Troubleshooting",
"unpair_all": "Unpair All",
"unpair_all_error": "Error while unpairing",

View File

@@ -190,6 +190,8 @@
"hevc_mode_2": "Apollo 将通告 HEVC Main 配置支持",
"hevc_mode_3": "Apollo 将通告 HEVC Main 和 Main10 (HDR) 配置支持",
"hevc_mode_desc": "允许客户端请求HEVC 主流或 HEVC Main10 视频流。 HEVC更需要编码因此在使用软件编码时可能降低性能。",
"hide_tray_controls": "隐藏托盘图标控制选项",
"hide_tray_controls_desc": "不在托盘图标菜单内显示 \"Force Stop\", \"Restart\" and \"Quit\"。",
"high_resolution_scrolling": "高分辨率鼠标滚动支持",
"high_resolution_scrolling_desc": "启用后Apollo 将透传来自 Moonlight 客户端的高分辨率滚动事件。对于那些使用高分辨率滚动事件时滚动速度过快的旧版应用程序来说,禁用此功能非常有用。",
"install_steam_audio_drivers": "安装 Steam 音频驱动程序",
@@ -392,9 +394,13 @@
"logs": "日志",
"logs_desc": "查看 Apollo 上传的日志",
"logs_find": "查找...",
"restart_apollo": "重启 Sunhine",
"restart_apollo_desc": "如果 Apollo 无法正常工作,可以尝试重新启动。这将终止任何正在行的会话。",
"restart_apollo_success": "Sunhine 正在重启",
"restart_apollo": "重启 Apollo",
"restart_apollo_desc": "如果 Apollo 无法正常工作,可以尝试重新启动。这将终止任何正在行的会话。",
"restart_apollo_success": "Apollo 正在重启",
"quit_apollo": "退出 Apollo",
"quit_apollo_desc": "停止运行 Apollo。这将会终止任何正在进行的会话。",
"quit_apollo_success": "Apollo has exited.",
"quit_apollo_success_ongoing": "Apollo is quitting...",
"troubleshooting": "故障排除",
"unpair_all": "全部取消配对",
"unpair_all_error": "取消配对时出错",

View File

@@ -65,16 +65,35 @@
<h2 id="restart">{{ $t('troubleshooting.restart_apollo') }}</h2>
<br>
<p>{{ $t('troubleshooting.restart_apollo_desc') }}</p>
<div class="alert alert-success" v-if="restartPressed === true">
<div class="alert alert-success" v-if="serverRestarting">
{{ $t('troubleshooting.restart_apollo_success') }}
</div>
<div>
<button class="btn btn-warning" :disabled="restartPressed" @click="restart">
<button class="btn btn-warning" :disabled="serverQuitting || serverRestarting" @click="restart">
{{ $t('troubleshooting.restart_apollo') }}
</button>
</div>
</div>
</div>
<!-- Quit Apollo -->
<div class="card p-2 my-4">
<div class="card-body">
<h2 id="quit">{{ $t('troubleshooting.quit_apollo') }}</h2>
<br>
<p>{{ $t('troubleshooting.quit_apollo_desc') }}</p>
<div class="alert alert-success" v-if="serverQuit">
{{ $t('troubleshooting.quit_apollo_success') }}
</div>
<div class="alert alert-success" v-if="serverQuitting">
{{ $t('troubleshooting.quit_apollo_success_ongoing') }}
</div>
<div>
<button class="btn btn-warning" :disabled="serverQuitting || serverRestarting" @click="quit">
{{ $t('troubleshooting.quit_apollo') }}
</button>
</div>
</div>
</div>
<!-- Unpair Clients -->
<div class="card my-4">
<div class="card-body">
@@ -107,7 +126,7 @@
<ul v-else class="list-group list-group-flush list-group-item-light">
<div class="list-group-item p-3 text-center"><em>{{ $t('troubleshooting.unpair_single_no_devices') }}</em></div>
</ul>
</div>
<!-- Logs -->
<div class="card p-2 my-4">
@@ -144,7 +163,9 @@
logs: 'Loading...',
logFilter: null,
logInterval: null,
restartPressed: false,
serterRestarting: false,
serverQuitting: false,
serverQuit: false,
showApplyMessage: false,
unpairAllPressed: false,
unpairAllStatus: null,
@@ -228,14 +249,48 @@
navigator.clipboard.writeText(this.actualLogs);
},
restart() {
this.restartPressed = true;
this.serverRestarting = true;
setTimeout(() => {
this.restartPressed = false;
this.serverRestarting = false;
}, 5000);
fetch("/api/restart", {
method: "POST",
});
},
quit() {
if (window.confirm("Do you really want to quit Apollo? You'll not be able to start Apollo again if you have no other methods to operate your computer.")) {
this.serverQuitting = true;
const timeoutID = setTimeout(() => {
this.serverQuitting = false;
}, 5000);
fetch("/api/quit", {
method: "POST",
})
.then(() => {
clearTimeout(timeoutID);
return new Promise((resolve, reject) => {
setTimeout(() => {
fetch("/", {
signal: AbortSignal.timeout(1000)
}).then(() => {
reject();
}).catch(() => {
resolve();
})
}, 1000);
});
})
.then(() => {
this.serverQuitting = false;
this.serverQuit = true;
})
.catch(() => {
this.serverQuitting = false;
this.serverQuit = false;
alert("Exit failed!");
});
}
}
},
});