Merge remote-tracking branch 'origin/master'

This commit is contained in:
Yukino Song
2024-12-14 19:02:11 +08:00
24 changed files with 347 additions and 107 deletions

4
.gitmodules vendored
View File

@@ -22,6 +22,10 @@
path = third-party/inputtino path = third-party/inputtino
url = https://github.com/games-on-whales/inputtino.git url = https://github.com/games-on-whales/inputtino.git
branch = stable branch = stable
[submodule "third-party/libdisplaydevice"]
path = third-party/libdisplaydevice
url = https://github.com/LizardByte/libdisplaydevice.git
branch = master
[submodule "third-party/nanors"] [submodule "third-party/nanors"]
path = third-party/nanors path = third-party/nanors
url = https://github.com/sleepybishop/nanors.git url = https://github.com/sleepybishop/nanors.git

View File

@@ -68,6 +68,8 @@ set(SUNSHINE_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/uuid.h" "${CMAKE_SOURCE_DIR}/src/uuid.h"
"${CMAKE_SOURCE_DIR}/src/config.h" "${CMAKE_SOURCE_DIR}/src/config.h"
"${CMAKE_SOURCE_DIR}/src/config.cpp" "${CMAKE_SOURCE_DIR}/src/config.cpp"
"${CMAKE_SOURCE_DIR}/src/display_device.h"
"${CMAKE_SOURCE_DIR}/src/display_device.cpp"
"${CMAKE_SOURCE_DIR}/src/entry_handler.cpp" "${CMAKE_SOURCE_DIR}/src/entry_handler.cpp"
"${CMAKE_SOURCE_DIR}/src/entry_handler.h" "${CMAKE_SOURCE_DIR}/src/entry_handler.h"
"${CMAKE_SOURCE_DIR}/src/file_handler.cpp" "${CMAKE_SOURCE_DIR}/src/file_handler.cpp"
@@ -146,6 +148,7 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${MINIUPNP_LIBRARIES} ${MINIUPNP_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
enet enet
libdisplaydevice::display_device
opus opus
${FFMPEG_LIBRARIES} ${FFMPEG_LIBRARIES}
${Boost_LIBRARIES} ${Boost_LIBRARIES}

View File

@@ -12,6 +12,9 @@ add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/enet")
# web server # web server
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/Simple-Web-Server") add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/Simple-Web-Server")
# libdisplaydevice
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/libdisplaydevice")
# common dependencies # common dependencies
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)

View File

@@ -865,10 +865,56 @@ editing the `conf` file in a text editor. Use the examples as reference.
<br> <br>
**Windows:** **Windows:**
<br> <br>
Enter the following command in command prompt or PowerShell. During Sunshine startup, you should see the list of detected displays:
@code{} @code{}
%ProgramFiles%\Sunshine\tools\dxgi-info.exe Info: Currently available display devices:
[
{
"device_id": "{64243705-4020-5895-b923-adc862c3457e}",
"display_name": "",
"friendly_name": "IDD HDR",
"info": null
},
{
"device_id": "{77f67f3e-754f-5d31-af64-ee037e18100a}",
"display_name": "",
"friendly_name": "SunshineHDR",
"info": null
},
{
"device_id": "{daeac860-f4db-5208-b1f5-cf59444fb768}",
"display_name": "\\\\.\\DISPLAY1",
"friendly_name": "ROG PG279Q",
"info": {
"hdr_state": null,
"origin_point": {
"x": 0,
"y": 0
},
"primary": true,
"refresh_rate": {
"type": "rational",
"value": {
"denominator": 1000,
"numerator": 119998
}
},
"resolution": {
"height": 1440,
"width": 2560
},
"resolution_scale": {
"type": "rational",
"value": {
"denominator": 100,
"numerator": 100
}
}
}
}
]
@endcode @endcode
You need to use the `device_id` value.
} }
</td> </td>
</tr> </tr>
@@ -891,7 +937,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
<tr> <tr>
<td>Example (Windows)</td> <td>Example (Windows)</td>
<td colspan="2">@code{} <td colspan="2">@code{}
output_name = \\.\DISPLAY1 output_name = {daeac860-f4db-5208-b1f5-cf59444fb768}
@endcode</td> @endcode</td>
</tr> </tr>
</table> </table>

View File

@@ -23,6 +23,9 @@ If you forgot your credentials to the web UI, try this.
@tip{Don't forget to replace `{new-username}` and `{new-password}` with your new credentials. @tip{Don't forget to replace `{new-username}` and `{new-password}` with your new credentials.
Do not include the curly braces.} Do not include the curly braces.}
### Unusual Mouse Behavior
If you experience unusual mouse behavior, try attaching a physical mouse to the Sunshine host.
### Web UI Access ### Web UI Access
Can't access the web UI? Can't access the web UI?
@@ -190,6 +193,9 @@ has. You may get permission denied errors when attempting to launch a game or ap
You will need to modify the security permissions on your disk. Ensure that user/principal SYSTEM has full You will need to modify the security permissions on your disk. Ensure that user/principal SYSTEM has full
permissions on the disk. permissions on the disk.
### Stuttering
If you experience stuttering using NVIDIA, try disabling `vsync:fast` in the NVIDIA Control Panel.
<div class="section_buttons"> <div class="section_buttons">
| Previous | Next | | Previous | Next |

View File

@@ -32,6 +32,9 @@ modules:
# Test dependencies # Test dependencies
- "modules/xvfb/xvfb.json" - "modules/xvfb/xvfb.json"
# Build dependencies
- "modules/nlohmann_json.json"
# Runtime dependencies # Runtime dependencies
- shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json - shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json
- "modules/avahi.json" - "modules/avahi.json"

View File

@@ -0,0 +1,15 @@
{
"name": "nlohmann_json",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DJSON_MultipleHeaders=OFF",
"-DJSON_BuildTests=OFF"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz",
"sha256": "d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d"
}
]
}

85
src/display_device.cpp Normal file
View File

@@ -0,0 +1,85 @@
/**
* @file src/display_device.cpp
* @brief Definitions for display device handling.
*/
// header include
#include "display_device.h"
// lib includes
#include <display_device/json.h>
#include <display_device/retry_scheduler.h>
#include <display_device/settings_manager_interface.h>
// local includes
#include "platform/common.h"
// platform-specific includes
#ifdef _WIN32
#include <display_device/windows/settings_manager.h>
#include <display_device/windows/win_api_layer.h>
#include <display_device/windows/win_display_device.h>
#endif
namespace display_device {
namespace {
/**
* @brief A global for the settings manager interface whose lifetime is managed by `display_device::init()`.
*/
std::unique_ptr<RetryScheduler<SettingsManagerInterface>> SM_INSTANCE;
/**
* @brief Construct a settings manager interface to manage display device settings.
* @return An interface or nullptr if the OS does not support the interface.
*/
std::unique_ptr<SettingsManagerInterface>
make_settings_manager() {
#ifdef _WIN32
// TODO: In the upcoming PR, add audio context capture and settings persistence
return std::make_unique<SettingsManager>(
std::make_shared<WinDisplayDevice>(std::make_shared<WinApiLayer>()),
nullptr,
std::make_unique<PersistentState>(nullptr),
WinWorkarounds {});
#else
return nullptr;
#endif
}
} // namespace
std::unique_ptr<platf::deinit_t>
init() {
// We can support re-init without any issues, however we should make sure to cleanup first!
SM_INSTANCE = nullptr;
// If we fail to create settings manager, this means platform is not supported and
// we will need to provided error-free passtrough in other methods
if (auto settings_manager { make_settings_manager() }) {
SM_INSTANCE = std::make_unique<RetryScheduler<SettingsManagerInterface>>(std::move(settings_manager));
const auto available_devices { SM_INSTANCE->execute([](auto &settings_iface) { return settings_iface.enumAvailableDevices(); }) };
BOOST_LOG(info) << "Currently available display devices:\n"
<< toJson(available_devices);
// TODO: In the upcoming PR, schedule recovery here
}
class deinit_t: public platf::deinit_t {
public:
~deinit_t() override {
// TODO: In the upcoming PR, execute recovery once here
SM_INSTANCE = nullptr;
}
};
return std::make_unique<deinit_t>();
}
std::string
map_output_name(const std::string &output_name) {
if (!SM_INSTANCE) {
// Fallback to giving back the output name if the platform is not supported.
return output_name;
}
return SM_INSTANCE->execute([&output_name](auto &settings_iface) { return settings_iface.getDisplayName(output_name); });
}
} // namespace display_device

39
src/display_device.h Normal file
View File

@@ -0,0 +1,39 @@
/**
* @file src/display_device.h
* @brief Declarations for display device handling.
*/
#pragma once
// lib includes
#include <memory>
// forward declarations
namespace platf {
class deinit_t;
} // namespace platf
namespace display_device {
/**
* @brief Initialize the implementation and perform the initial state recovery (if needed).
* @returns A deinit_t instance that performs cleanup when destroyed.
*
* @examples
* const auto init_guard { display_device::init() };
* @examples_end
*/
std::unique_ptr<platf::deinit_t>
init();
/**
* @brief Map the output name to a specific display.
* @param output_name The user-configurable output name.
* @returns Mapped display name or empty string if the output name could not be mapped.
*
* @examples
* const auto mapped_name_config { map_output_name(config::video.output_name) };
* const auto mapped_name_custom { map_output_name("{some-device-id}") };
* @examples_end
*/
std::string
map_output_name(const std::string &output_name);
} // namespace display_device

View File

@@ -15,6 +15,7 @@
#include <boost/log/expressions.hpp> #include <boost/log/expressions.hpp>
#include <boost/log/sinks.hpp> #include <boost/log/sinks.hpp>
#include <boost/log/sources/severity_logger.hpp> #include <boost/log/sources/severity_logger.hpp>
#include <display_device/logging.h>
// local includes // local includes
#include "logging.h" #include "logging.h"
@@ -106,6 +107,7 @@ namespace logging {
} }
setup_av_logging(min_log_level); setup_av_logging(min_log_level);
setup_libdisplaydevice_logging(min_log_level);
sink = boost::make_shared<text_sink>(); sink = boost::make_shared<text_sink>();
@@ -159,6 +161,37 @@ namespace logging {
}); });
} }
void
setup_libdisplaydevice_logging(int min_log_level) {
constexpr int min_level { static_cast<int>(display_device::Logger::LogLevel::verbose) };
constexpr int max_level { static_cast<int>(display_device::Logger::LogLevel::fatal) };
const auto log_level { static_cast<display_device::Logger::LogLevel>(std::min(std::max(min_level, min_log_level), max_level)) };
display_device::Logger::get().setLogLevel(log_level);
display_device::Logger::get().setCustomCallback([](const display_device::Logger::LogLevel level, const std::string &message) {
switch (level) {
case display_device::Logger::LogLevel::verbose:
BOOST_LOG(verbose) << message;
break;
case display_device::Logger::LogLevel::debug:
BOOST_LOG(debug) << message;
break;
case display_device::Logger::LogLevel::info:
BOOST_LOG(info) << message;
break;
case display_device::Logger::LogLevel::warning:
BOOST_LOG(warning) << message;
break;
case display_device::Logger::LogLevel::error:
BOOST_LOG(error) << message;
break;
case display_device::Logger::LogLevel::fatal:
BOOST_LOG(fatal) << message;
break;
}
});
}
void void
log_flush() { log_flush() {
if (sink) { if (sink) {

View File

@@ -66,6 +66,13 @@ namespace logging {
void void
setup_av_logging(int min_log_level); setup_av_logging(int min_log_level);
/**
* @brief Setup logging for libdisplaydevice.
* @param min_log_level The log level.
*/
void
setup_libdisplaydevice_logging(int min_log_level);
/** /**
* @brief Flush the log. * @brief Flush the log.
* @examples * @examples

View File

@@ -10,6 +10,7 @@
// local includes // local includes
#include "confighttp.h" #include "confighttp.h"
#include "display_device.h"
#include "entry_handler.h" #include "entry_handler.h"
#include "globals.h" #include "globals.h"
#include "httpcommon.h" #include "httpcommon.h"
@@ -135,6 +136,14 @@ main(int argc, char *argv[]) {
return fn->second(argv[0], config::sunshine.cmd.argc, config::sunshine.cmd.argv); return fn->second(argv[0], config::sunshine.cmd.argc, config::sunshine.cmd.argv);
} }
// Adding guard here first as it also performs recovery after crash,
// otherwise people could theoretically end up without display output.
// It also should be destroyed before forced shutdown to expedite the cleanup.
auto display_device_deinit_guard = display_device::init();
if (!display_device_deinit_guard) {
BOOST_LOG(error) << "Display device session failed to initialize"sv;
}
#ifdef WIN32 #ifdef WIN32
// Modify relevant NVIDIA control panel settings if the system has corresponding gpu // Modify relevant NVIDIA control panel settings if the system has corresponding gpu
if (nvprefs_instance.load()) { if (nvprefs_instance.load()) {
@@ -232,7 +241,7 @@ main(int argc, char *argv[]) {
// Create signal handler after logging has been initialized // Create signal handler after logging has been initialized
auto shutdown_event = mail::man->event<bool>(mail::shutdown); auto shutdown_event = mail::man->event<bool>(mail::shutdown);
on_signal(SIGINT, [&force_shutdown, shutdown_event]() { on_signal(SIGINT, [&force_shutdown, &display_device_deinit_guard, shutdown_event]() {
BOOST_LOG(info) << "Interrupt handler called"sv; BOOST_LOG(info) << "Interrupt handler called"sv;
auto task = []() { auto task = []() {
@@ -243,9 +252,10 @@ main(int argc, char *argv[]) {
force_shutdown = task_pool.pushDelayed(task, 10s).task_id; force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
shutdown_event->raise(true); shutdown_event->raise(true);
display_device_deinit_guard = nullptr;
}); });
on_signal(SIGTERM, [&force_shutdown, shutdown_event]() { on_signal(SIGTERM, [&force_shutdown, &display_device_deinit_guard, shutdown_event]() {
BOOST_LOG(info) << "Terminate handler called"sv; BOOST_LOG(info) << "Terminate handler called"sv;
auto task = []() { auto task = []() {
@@ -256,6 +266,7 @@ main(int argc, char *argv[]) {
force_shutdown = task_pool.pushDelayed(task, 10s).task_id; force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
shutdown_event->raise(true); shutdown_event->raise(true);
display_device_deinit_guard = nullptr;
}); });
#ifdef _WIN32 #ifdef _WIN32

View File

@@ -8,6 +8,7 @@
#include <chrono> #include <chrono>
#include <mach/mach.h> #include <mach/mach.h>
#include "src/display_device.h"
#include "src/logging.h" #include "src/logging.h"
#include "src/platform/common.h" #include "src/platform/common.h"
#include "src/utility.h" #include "src/utility.h"
@@ -541,7 +542,7 @@ const KeyCodeMap kKeyCodesMap[] = {
// Default to main display // Default to main display
macos_input->display = CGMainDisplayID(); macos_input->display = CGMainDisplayID();
auto output_name = config::video.output_name; auto output_name = display_device::map_output_name(config::video.output_name);
// If output_name is set, try to find the display with that display id // If output_name is set, try to find the display with that display id
if (!output_name.empty()) { if (!output_name.empty()) {
uint32_t max_display = 32; uint32_t max_display = 32;

View File

@@ -16,6 +16,7 @@ typedef long NTSTATUS;
#include "display.h" #include "display.h"
#include "misc.h" #include "misc.h"
#include "src/config.h" #include "src/config.h"
#include "src/display_device.h"
#include "src/logging.h" #include "src/logging.h"
#include "src/platform/common.h" #include "src/platform/common.h"
#include "src/video.h" #include "src/video.h"
@@ -1101,7 +1102,8 @@ namespace platf {
BOOST_LOG(debug) << "Detecting monitors..."sv; BOOST_LOG(debug) << "Detecting monitors..."sv;
// We must set the GPU preference before calling any DXGI APIs! // We must set the GPU preference before calling any DXGI APIs!
if (!dxgi::probe_for_gpu_preference(config::video.output_name)) { const auto output_name { display_device::map_output_name(config::video.output_name) };
if (!dxgi::probe_for_gpu_preference(output_name)) {
BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv; BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv;
} }

View File

@@ -19,6 +19,7 @@ extern "C" {
#include "process.h" #include "process.h"
#include "cbs.h" #include "cbs.h"
#include "config.h" #include "config.h"
#include "display_device.h"
#include "globals.h" #include "globals.h"
#include "input.h" #include "input.h"
#include "logging.h" #include "logging.h"
@@ -995,6 +996,8 @@ namespace video {
*/ */
void void
refresh_displays(platf::mem_type_e dev_type, std::vector<std::string> &display_names, int &current_display_index, std::string &preferred_display_name) { refresh_displays(platf::mem_type_e dev_type, std::vector<std::string> &display_names, int &current_display_index, std::string &preferred_display_name) {
// It is possible that the output name may be empty even if it wasn't before (device disconnected) or vice-versa
const auto output_name { display_device::map_output_name(config::video.output_name) };
std::string current_display_name = preferred_display_name; std::string current_display_name = preferred_display_name;
// If we have a current display index, let's start with that // If we have a current display index, let's start with that
@@ -1013,7 +1016,7 @@ namespace video {
return; return;
} }
else if (display_names.empty()) { else if (display_names.empty()) {
display_names.emplace_back(config::video.output_name); display_names.emplace_back(output_name);
} }
// We now have a new display name list, so reset the index back to 0 // We now have a new display name list, so reset the index back to 0
@@ -1033,7 +1036,15 @@ namespace video {
} }
// The old display was removed, so we'll start back at the first display again // The old display was removed, so we'll start back at the first display again
BOOST_LOG(warning) << "Desired display ["sv << current_display_name << "] does not exist"sv; BOOST_LOG(warning) << "Previous active display ["sv << current_display_name << "] is no longer present"sv;
}
else {
for (int x = 0; x < display_names.size(); ++x) {
if (display_names[x] == output_name) {
current_display_index = x;
return;
}
}
} }
} }
@@ -2359,6 +2370,7 @@ namespace video {
bool bool
validate_encoder(encoder_t &encoder, bool expect_failure) { validate_encoder(encoder_t &encoder, bool expect_failure) {
const auto output_name { display_device::map_output_name(config::video.output_name) };
std::shared_ptr<platf::display_t> disp; std::shared_ptr<platf::display_t> disp;
BOOST_LOG(info) << "Trying encoder ["sv << encoder.name << ']'; BOOST_LOG(info) << "Trying encoder ["sv << encoder.name << ']';
@@ -2378,7 +2390,7 @@ namespace video {
config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0, 0 }; config_t config_autoselect { 1920, 1080, 60, 1000, 1, 0, 1, 0, 0, 0 };
// If the encoder isn't supported at all (not even H.264), bail early // If the encoder isn't supported at all (not even H.264), bail early
reset_display(disp, encoder.platform_formats->dev_type, config::video.output_name, config_autoselect); reset_display(disp, encoder.platform_formats->dev_type, output_name, config_autoselect);
if (!disp) { if (!disp) {
return false; return false;
} }
@@ -2486,7 +2498,7 @@ namespace video {
const config_t generic_hdr_config = { 1920, 1080, 60, 1000, 1, 0, 3, 1, 1, 0 }; const config_t generic_hdr_config = { 1920, 1080, 60, 1000, 1, 0, 3, 1, 1, 0 };
// Reset the display since we're switching from SDR to HDR // Reset the display since we're switching from SDR to HDR
reset_display(disp, encoder.platform_formats->dev_type, config::video.output_name, generic_hdr_config); reset_display(disp, encoder.platform_formats->dev_type, output_name, generic_hdr_config);
if (!disp) { if (!disp) {
return false; return false;
} }
@@ -2667,8 +2679,9 @@ namespace video {
} }
if (chosen_encoder == nullptr) { if (chosen_encoder == nullptr) {
const auto output_name { display_device::map_output_name(config::video.output_name) };
BOOST_LOG(fatal) << "Unable to find display or encoder during startup."sv; BOOST_LOG(fatal) << "Unable to find display or encoder during startup."sv;
if (!config::video.adapter_name.empty() || !config::video.output_name.empty()) { if (!config::video.adapter_name.empty() || !output_name.empty()) {
BOOST_LOG(fatal) << "Please ensure your manually chosen GPU and monitor are connected and powered on."sv; BOOST_LOG(fatal) << "Please ensure your manually chosen GPU and monitor are connected and powered on."sv;
} }
else { else {

View File

@@ -3,8 +3,7 @@ import {ref, computed, inject} from 'vue'
import {$tp} from '../../platform-i18n' import {$tp} from '../../platform-i18n'
import PlatformLayout from '../../PlatformLayout.vue' import PlatformLayout from '../../PlatformLayout.vue'
import AdapterNameSelector from './audiovideo/AdapterNameSelector.vue' import AdapterNameSelector from './audiovideo/AdapterNameSelector.vue'
import LegacyDisplayOutputSelector from './audiovideo/LegacyDisplayOutputSelector.vue' import DisplayOutputSelector from './audiovideo/DisplayOutputSelector.vue'
import NewDisplayOutputSelector from './audiovideo/NewDisplayOutputSelector.vue'
import DisplayDeviceOptions from "./audiovideo/DisplayDeviceOptions.vue"; import DisplayDeviceOptions from "./audiovideo/DisplayDeviceOptions.vue";
import DisplayModesSettings from "./audiovideo/DisplayModesSettings.vue"; import DisplayModesSettings from "./audiovideo/DisplayModesSettings.vue";
@@ -103,7 +102,7 @@ const validateFallbackMode = (event) => {
:config="config" :config="config"
/> />
<LegacyDisplayOutputSelector <DisplayOutputSelector
:platform="platform" :platform="platform"
:config="config" :config="config"
/> />

View File

@@ -0,0 +1,53 @@
<script setup>
import { ref } from 'vue'
import { $tp } from '../../../platform-i18n'
import PlatformLayout from '../../../PlatformLayout.vue'
const props = defineProps([
'platform',
'config'
])
const config = ref(props.config)
const outputNamePlaceholder = (props.platform === 'windows') ? '{de9bb7e2-186e-505b-9e93-f48793333810}' : '0'
</script>
<template>
<div class="mb-3">
<label for="output_name" class="form-label">{{ $tp('config.output_name') }}</label>
<input type="text" class="form-control" id="output_name" :placeholder="outputNamePlaceholder"
v-model="config.output_name"/>
<div class="form-text">
{{ $tp('config.output_name_desc') }}<br>
<PlatformLayout :platform="platform">
<template #windows>
<pre style="white-space: pre-line;">
<b>&nbsp;&nbsp;{</b>
<b>&nbsp;&nbsp;&nbsp;&nbsp;"device_id": "{de9bb7e2-186e-505b-9e93-f48793333810}"</b>
<b>&nbsp;&nbsp;&nbsp;&nbsp;"display_name": "\\\\.\\DISPLAY1"</b>
<b>&nbsp;&nbsp;&nbsp;&nbsp;"friendly_name": "ROG PG279Q"</b>
<b>&nbsp;&nbsp;&nbsp;&nbsp;...</b>
<b>&nbsp;&nbsp;}</b>
</pre>
</template>
<template #linux>
<pre style="white-space: pre-line;">
Info: Detecting displays
Info: Detected display: DVI-D-0 (id: 0) connected: false
Info: Detected display: HDMI-0 (id: 1) connected: true
Info: Detected display: DP-0 (id: 2) connected: true
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false
</pre>
</template>
<template #macos>
<pre style="white-space: pre-line;">
Info: Detecting displays
Info: Detected display: Monitor-0 (id: 3) connected: true
Info: Detected display: Monitor-1 (id: 2) connected: true
</pre>
</template>
</PlatformLayout>
</div>
</div>
</template>

View File

@@ -1,46 +0,0 @@
<script setup>
import { ref } from 'vue'
import { $tp } from '../../../platform-i18n'
import PlatformLayout from '../../../PlatformLayout.vue'
const props = defineProps([
'platform',
'config'
])
const config = ref(props.config)
const outputNamePlaceholder = (props.platform === 'windows') ? '\\\\.\\DISPLAY1' : '0'
</script>
<template>
<div class="mb-3">
<label for="output_name" class="form-label">{{ $tp('config.output_name') }}</label>
<input type="text" class="form-control" id="output_name" :placeholder="outputNamePlaceholder"
v-model="config.output_name"/>
<div class="form-text">
{{ $tp('config.output_name_desc') }}<br>
<PlatformLayout :platform="platform">
<template #windows>
<pre>tools\dxgi-info.exe</pre>
</template>
<template #linux>
<pre style="white-space: pre-line;">
Info: Detecting displays
Info: Detected display: DVI-D-0 (id: 0) connected: false
Info: Detected display: HDMI-0 (id: 1) connected: true
Info: Detected display: DP-0 (id: 2) connected: true
Info: Detected display: DP-1 (id: 3) connected: false
Info: Detected display: DVI-D-1 (id: 4) connected: false
</pre>
</template>
<template #macos>
<pre style="white-space: pre-line;">
Info: Detecting displays
Info: Detected display: Monitor-0 (id: 3) connected: true
Info: Detected display: Monitor-1 (id: 2) connected: true
</pre>
</template>
</PlatformLayout>
</div>
</div>
</template>

View File

@@ -1,38 +0,0 @@
<script setup>
import { ref } from 'vue'
import { $tp } from '../../../platform-i18n'
import PlatformLayout from '../../../PlatformLayout.vue'
const props = defineProps([
'platform',
'config',
'displays'
])
const config = ref(props.config)
const outputNamePlaceholder = (props.platform === 'windows') ? '{de9bb7e2-186e-505b-9e93-f48793333810}' : '4531345'
</script>
<template>
<div class="mb-3">
<label for="output_name" class="form-label">{{ $tp('config.output_name') }}</label>
<input type="text" class="form-control" id="output_name" :placeholder="outputNamePlaceholder"
v-model="config.output_name"/>
<div class="form-text">
<p style="white-space: pre-line">{{ $tp('config.output_name_desc') }}</p>
<PlatformLayout :platform="platform">
<template #windows>
<b>&nbsp;&nbsp;&nbsp;&nbsp;DEVICE ID: {de9bb7e2-186e-505b-9e93-f48793333810}</b><br>
<b>&nbsp;&nbsp;&nbsp;&nbsp;DISPLAY NAME: \\.\DISPLAY1</b><br>
<b>&nbsp;&nbsp;&nbsp;&nbsp;FRIENDLY NAME: ROG PG279Q</b><br>
<b>&nbsp;&nbsp;&nbsp;&nbsp;DEVICE STATE: PRIMARY</b><br>
<b>&nbsp;&nbsp;&nbsp;&nbsp;HDR STATE: UNKNOWN</b>
</template>
<template #linux>
</template>
<template #macos>
</template>
</PlatformLayout>
</div>
</div>
</template>

View File

@@ -1,6 +1,6 @@
{ {
"_common": { "_common": {
"apply": "Anwenden", "apply": "Übernehmen",
"auto": "Automatisch", "auto": "Automatisch",
"autodetect": "AutoDetection (empfohlen)", "autodetect": "AutoDetection (empfohlen)",
"beta": "(Beta)", "beta": "(Beta)",

View File

@@ -291,9 +291,9 @@
"origin_web_ui_allowed_pc": "Only localhost may access Web UI", "origin_web_ui_allowed_pc": "Only localhost may access Web UI",
"origin_web_ui_allowed_wan": "Anyone may access Web UI", "origin_web_ui_allowed_wan": "Anyone may access Web UI",
"output_name_desc_unix": "During Apollo startup, you should see the list of detected displays. Note: You need to use the id value inside the parenthesis. Below is an example; the actual output can be found in the Troubleshooting tab.", "output_name_desc_unix": "During Apollo startup, you should see the list of detected displays. Note: You need to use the id value inside the parenthesis. Below is an example; the actual output can be found in the Troubleshooting tab.",
"output_name_desc_windows": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. The appropriate values can be found using the following command:", "output_name_desc_windows": "Manually specify a display device id to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. During Sunshine startup, you should see the list of detected displays. Below is an example; the actual output can be found in the Troubleshooting tab.",
"output_name_unix": "Display number", "output_name_unix": "Display number",
"output_name_windows": "Output Name", "output_name_windows": "Display Device Id",
"ping_timeout": "Ping Timeout", "ping_timeout": "Ping Timeout",
"ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream", "ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream",
"pkey": "Private Key", "pkey": "Private Key",

View File

@@ -70,7 +70,7 @@
"image": "Imagem:", "image": "Imagem:",
"image_desc": "Caminho da aplicação icon/imagem/imagem que será enviado para o cliente. Imagem deve ser um arquivo PNG. Se não estiver definido, Apollo irá enviar a imagem da caixa padrão.", "image_desc": "Caminho da aplicação icon/imagem/imagem que será enviado para o cliente. Imagem deve ser um arquivo PNG. Se não estiver definido, Apollo irá enviar a imagem da caixa padrão.",
"loading": "Carregandochar@@0", "loading": "Carregandochar@@0",
"name": "Nome:", "name": "Nome",
"output_desc": "O arquivo onde a saída do comando é armazenada, se não for especificado, a saída é ignorada", "output_desc": "O arquivo onde a saída do comando é armazenada, se não for especificado, a saída é ignorada",
"output_name": "Saída", "output_name": "Saída",
"run_as_desc": "Isto pode ser necessário para que alguns aplicativos que requerem permissões de administrador sejam executados corretamente.", "run_as_desc": "Isto pode ser necessário para que alguns aplicativos que requerem permissões de administrador sejam executados corretamente.",
@@ -110,7 +110,7 @@
"amd_rc_cqp": "cqp -- modo qp constante", "amd_rc_cqp": "cqp -- modo qp constante",
"amd_rc_desc": "Isto controla o método de controle da taxa para garantir que não estamos a exceder o alvo da taxa de bits do cliente. 'cqp' não é adequado para segmentação de taxa de bits e outras opções além de 'vbr_latency' dependem da aplicação HRD para ajudar a restringir os fluxos de taxa de bits.", "amd_rc_desc": "Isto controla o método de controle da taxa para garantir que não estamos a exceder o alvo da taxa de bits do cliente. 'cqp' não é adequado para segmentação de taxa de bits e outras opções além de 'vbr_latency' dependem da aplicação HRD para ajudar a restringir os fluxos de taxa de bits.",
"amd_rc_group": "Configurações de controle de taxa AMF", "amd_rc_group": "Configurações de controle de taxa AMF",
"amd_rc_vbr_latency": "latência vbr_ency -- taxa de bits com restrição de latência", "amd_rc_vbr_latency": "vbr_latency -- bitrate variável limitado pela latência (recomendado se o HDR estiver desabilitado; padrão)",
"amd_rc_vbr_peak": "vbr_pico -- pico de taxa de bits variável restrita", "amd_rc_vbr_peak": "vbr_pico -- pico de taxa de bits variável restrita",
"amd_usage": "Uso do AMF", "amd_usage": "Uso do AMF",
"amd_usage_desc": "Isso define o perfil de codificação base. Todas as opções apresentadas abaixo substituirão um subconjunto do perfil de uso, mas há configurações ocultas adicionais aplicadas que não podem ser configuradas em outro lugar.", "amd_usage_desc": "Isso define o perfil de codificação base. Todas as opções apresentadas abaixo substituirão um subconjunto do perfil de uso, mas há configurações ocultas adicionais aplicadas que não podem ser configuradas em outro lugar.",
@@ -362,7 +362,7 @@
}, },
"pin": { "pin": {
"device_name": "Nome do dispositivo", "device_name": "Nome do dispositivo",
"pair_failure": "Falha no pareamento: Verifique se o PIN é digitado corretamente", "pair_failure": "Pareamento Falhou: Verifique se o PIN foi digitado corretamente",
"pair_success": "Sucesso! Por favor, verifique a Lua Lunar para continuar", "pair_success": "Sucesso! Por favor, verifique a Lua Lunar para continuar",
"pin_pairing": "PIN Pairing", "pin_pairing": "PIN Pairing",
"send": "Mandar", "send": "Mandar",

View File

@@ -140,7 +140,7 @@
"amd_usage_ultralowlatency": "ultralowlatency -- 超低延迟(最快;默认)", "amd_usage_ultralowlatency": "ultralowlatency -- 超低延迟(最快;默认)",
"amd_usage_webcam": "webcam -- 网络摄像头(慢)", "amd_usage_webcam": "webcam -- 网络摄像头(慢)",
"amd_vbaq": "AMF 基于方差的自适应量化 (VBAQ)", "amd_vbaq": "AMF 基于方差的自适应量化 (VBAQ)",
"amd_vbaq_desc": "人的视觉系统通常对高成形地区的艺术品不太敏感。VBAQ 模式下,像素差异用于表示空间纹理的复杂性,使编码器能够将更多的比特分配给较平的区域。 启用此功能可提高主观视觉品质及一些内容。", "amd_vbaq_desc": "人的视觉系统通常对高度纹理化区域中的瑕疵不太敏感。VBAQ模式下像素方差被用来指示空间纹理的复杂性,这使得编码器可以将更多的比特分配给更平滑的区域。启用这个特性可以在某些内容上提升主观视觉质量。",
"apply_note": "点击“应用”重启 Apollo 并应用更改。这将终止任何正在运行的会话。", "apply_note": "点击“应用”重启 Apollo 并应用更改。这将终止任何正在运行的会话。",
"audio_sink": "音频输出设备", "audio_sink": "音频输出设备",
"audio_sink_desc_linux": "手动指定需要抓取的音频输出设备。如果您没有指定此变量PulseAudio 将选择默认监测设备。 您可以使用以下任何命令找到音频输出设备的名称:", "audio_sink_desc_linux": "手动指定需要抓取的音频输出设备。如果您没有指定此变量PulseAudio 将选择默认监测设备。 您可以使用以下任何命令找到音频输出设备的名称:",
@@ -195,7 +195,7 @@
"follow_client_hdr_desc": "根据客户端请求自动设置串流屏幕 HDR 的开关。(不建议启用)", "follow_client_hdr_desc": "根据客户端请求自动设置串流屏幕 HDR 的开关。(不建议启用)",
"gamepad": "模拟游戏手柄类型", "gamepad": "模拟游戏手柄类型",
"gamepad_auto": "自动选择选项", "gamepad_auto": "自动选择选项",
"gamepad_desc": "选择要在主机上模拟的游戏手类型", "gamepad_desc": "选择要在主机上模拟的游戏手类型",
"gamepad_ds4": "DS4 (PS4)", "gamepad_ds4": "DS4 (PS4)",
"gamepad_ds5": "DS5 (PS5)", "gamepad_ds5": "DS5 (PS5)",
"gamepad_switch": "Nintendo Pro (Switch)", "gamepad_switch": "Nintendo Pro (Switch)",

1
third-party/libdisplaydevice vendored Submodule