feat(display)!: Add libdisplaydevice dependency and output name mapping (#2894)
This commit is contained in:
parent
0cc98f113e
commit
1543f584ab
20 changed files with 327 additions and 101 deletions
85
src/display_device.cpp
Normal file
85
src/display_device.cpp
Normal 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
39
src/display_device.h
Normal 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
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <display_device/logging.h>
|
||||
|
||||
// local includes
|
||||
#include "logging.h"
|
||||
|
|
@ -106,6 +107,7 @@ namespace logging {
|
|||
}
|
||||
|
||||
setup_av_logging(min_log_level);
|
||||
setup_libdisplaydevice_logging(min_log_level);
|
||||
|
||||
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
|
||||
log_flush() {
|
||||
if (sink) {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,13 @@ namespace logging {
|
|||
void
|
||||
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.
|
||||
* @examples
|
||||
|
|
|
|||
15
src/main.cpp
15
src/main.cpp
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
// local includes
|
||||
#include "confighttp.h"
|
||||
#include "display_device.h"
|
||||
#include "entry_handler.h"
|
||||
#include "globals.h"
|
||||
#include "httpcommon.h"
|
||||
|
|
@ -133,6 +134,14 @@ main(int argc, char *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
|
||||
// Modify relevant NVIDIA control panel settings if the system has corresponding gpu
|
||||
if (nvprefs_instance.load()) {
|
||||
|
|
@ -230,7 +239,7 @@ main(int argc, char *argv[]) {
|
|||
|
||||
// Create signal handler after logging has been initialized
|
||||
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;
|
||||
|
||||
auto task = []() {
|
||||
|
|
@ -241,9 +250,10 @@ main(int argc, char *argv[]) {
|
|||
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
|
||||
|
||||
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;
|
||||
|
||||
auto task = []() {
|
||||
|
|
@ -254,6 +264,7 @@ main(int argc, char *argv[]) {
|
|||
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
|
||||
|
||||
shutdown_event->raise(true);
|
||||
display_device_deinit_guard = nullptr;
|
||||
});
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <chrono>
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include "src/display_device.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
|
@ -541,7 +542,7 @@ const KeyCodeMap kKeyCodesMap[] = {
|
|||
// Default to main display
|
||||
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.empty()) {
|
||||
uint32_t max_display = 32;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ typedef long NTSTATUS;
|
|||
#include "display.h"
|
||||
#include "misc.h"
|
||||
#include "src/config.h"
|
||||
#include "src/display_device.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/video.h"
|
||||
|
|
@ -1101,7 +1102,8 @@ namespace platf {
|
|||
BOOST_LOG(debug) << "Detecting monitors..."sv;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ extern "C" {
|
|||
|
||||
#include "cbs.h"
|
||||
#include "config.h"
|
||||
#include "display_device.h"
|
||||
#include "globals.h"
|
||||
#include "input.h"
|
||||
#include "logging.h"
|
||||
|
|
@ -994,6 +995,8 @@ namespace video {
|
|||
*/
|
||||
void
|
||||
refresh_displays(platf::mem_type_e dev_type, std::vector<std::string> &display_names, int ¤t_display_index) {
|
||||
// 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;
|
||||
|
||||
// If we have a current display index, let's start with that
|
||||
|
|
@ -1012,7 +1015,7 @@ namespace video {
|
|||
return;
|
||||
}
|
||||
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
|
||||
|
|
@ -1032,7 +1035,7 @@ namespace video {
|
|||
}
|
||||
else {
|
||||
for (int x = 0; x < display_names.size(); ++x) {
|
||||
if (display_names[x] == config::video.output_name) {
|
||||
if (display_names[x] == output_name) {
|
||||
current_display_index = x;
|
||||
return;
|
||||
}
|
||||
|
|
@ -2346,6 +2349,7 @@ namespace video {
|
|||
|
||||
bool
|
||||
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;
|
||||
|
||||
BOOST_LOG(info) << "Trying encoder ["sv << encoder.name << ']';
|
||||
|
|
@ -2365,7 +2369,7 @@ namespace video {
|
|||
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
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2473,7 +2477,7 @@ namespace video {
|
|||
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_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) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2654,8 +2658,9 @@ namespace video {
|
|||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue