Skip to content

Commit eea095b

Browse files
committed
feat: Add libdisplaydevice dependency and output name mapping
1 parent f4dda21 commit eea095b

File tree

17 files changed

+311
-59
lines changed

17 files changed

+311
-59
lines changed

.gitmodules

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
path = third-party/inputtino
1919
url = https://github.com/games-on-whales/inputtino.git
2020
branch = stable
21+
[submodule "third-party/libdisplaydevice"]
22+
path = third-party/libdisplaydevice
23+
url = https://github.com/LizardByte/libdisplaydevice.git
24+
branch = master
2125
[submodule "third-party/moonlight-common-c"]
2226
path = third-party/moonlight-common-c
2327
url = https://github.com/moonlight-stream/moonlight-common-c.git

cmake/compile_definitions/common.cmake

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ set(SUNSHINE_TARGET_FILES
6161
"${CMAKE_SOURCE_DIR}/src/uuid.h"
6262
"${CMAKE_SOURCE_DIR}/src/config.h"
6363
"${CMAKE_SOURCE_DIR}/src/config.cpp"
64+
"${CMAKE_SOURCE_DIR}/src/display_device.h"
65+
"${CMAKE_SOURCE_DIR}/src/display_device.cpp"
6466
"${CMAKE_SOURCE_DIR}/src/entry_handler.cpp"
6567
"${CMAKE_SOURCE_DIR}/src/entry_handler.h"
6668
"${CMAKE_SOURCE_DIR}/src/file_handler.cpp"
@@ -137,4 +139,5 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
137139
${FFMPEG_LIBRARIES}
138140
${Boost_LIBRARIES}
139141
${OPENSSL_LIBRARIES}
140-
${PLATFORM_LIBRARIES})
142+
${PLATFORM_LIBRARIES}
143+
libdisplaydevice::display_device)

cmake/dependencies/common.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c/enet")
1212
# web server
1313
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/Simple-Web-Server")
1414

15+
# libdisplaydevice
16+
add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/libdisplaydevice")
17+
1518
# common dependencies
1619
find_package(OpenSSL REQUIRED)
1720
find_package(PkgConfig REQUIRED)

docs/source/about/advanced_usage.rst

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ keybindings
586586
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
587587

588588
**Description**
589-
Select the display number you want to stream.
589+
Select the display you want to stream.
590590

591591
.. tip:: To find the name of the appropriate values follow these instructions.
592592

@@ -616,9 +616,58 @@ keybindings
616616
You need to use the id value inside the parenthesis, e.g. ``3``.
617617

618618
**Windows**
619-
.. code-block:: batch
619+
During Sunshine startup, you should see the list of detected displays:
620620

621-
tools\dxgi-info.exe
621+
.. code-block:: text
622+
623+
Info: Currently available display devices:
624+
[
625+
{
626+
"device_id": "{64243705-4020-5895-b923-adc862c3457e}",
627+
"display_name": "",
628+
"friendly_name": "IDD HDR",
629+
"info": null
630+
},
631+
{
632+
"device_id": "{77f67f3e-754f-5d31-af64-ee037e18100a}",
633+
"display_name": "",
634+
"friendly_name": "SunshineHDR",
635+
"info": null
636+
},
637+
{
638+
"device_id": "{daeac860-f4db-5208-b1f5-cf59444fb768}",
639+
"display_name": "\\\\.\\DISPLAY1",
640+
"friendly_name": "ROG PG279Q",
641+
"info": {
642+
"hdr_state": null,
643+
"origin_point": {
644+
"x": 0,
645+
"y": 0
646+
},
647+
"primary": true,
648+
"refresh_rate": {
649+
"type": "rational",
650+
"value": {
651+
"denominator": 1000,
652+
"numerator": 119998
653+
}
654+
},
655+
"resolution": {
656+
"height": 1440,
657+
"width": 2560
658+
},
659+
"resolution_scale": {
660+
"type": "rational",
661+
"value": {
662+
"denominator": 100,
663+
"numerator": 100
664+
}
665+
}
666+
}
667+
}
668+
]
669+
670+
You need to use the ``device_id`` value.
622671

623672
**Default**
624673
Sunshine will select the default display.
@@ -637,7 +686,7 @@ keybindings
637686
**Windows**
638687
.. code-block:: text
639688
640-
output_name = \\.\DISPLAY1
689+
output_name = {daeac860-f4db-5208-b1f5-cf59444fb768}
641690
642691
`resolutions <https://localhost:47990/config/#resolutions>`__
643692
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/display_device.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @file src/display_device.cpp
3+
* @brief Definitions for display device handling.
4+
*/
5+
// header include
6+
#include "display_device.h"
7+
8+
// lib includes
9+
#include <display_device/json.h>
10+
#include <display_device/retry_scheduler.h>
11+
#include <display_device/settings_manager_interface.h>
12+
13+
// platform-specific includes
14+
#ifdef _WIN32
15+
#include <display_device/windows/settings_manager.h>
16+
#include <display_device/windows/win_api_layer.h>
17+
#include <display_device/windows/win_display_device.h>
18+
#endif
19+
20+
namespace display_device {
21+
namespace {
22+
std::unique_ptr<SettingsManagerInterface>
23+
make_settings_manager() {
24+
#ifdef _WIN32
25+
// TODO: In the upcoming PR, add audio context capture and settings persistence
26+
return std::make_unique<SettingsManager>(
27+
std::make_shared<WinDisplayDevice>(std::make_shared<WinApiLayer>()),
28+
nullptr,
29+
std::make_unique<PersistentState>(nullptr));
30+
#else
31+
return nullptr;
32+
#endif
33+
}
34+
} // namespace
35+
36+
session_t &
37+
session_t::get() {
38+
static session_t session;
39+
return session;
40+
}
41+
42+
std::unique_ptr<platf::deinit_t>
43+
session_t::init() {
44+
// We can support re-init without any issues, however we should make sure to cleanup first!
45+
get().impl = nullptr;
46+
47+
// If we fail to create settings manager, this means platform is not supported and
48+
// we will need to provided error-free passtrough in other methods
49+
if (auto settings_manager { make_settings_manager() }) {
50+
get().impl = std::make_unique<RetryScheduler<SettingsManagerInterface>>(std::move(settings_manager));
51+
52+
const auto available_devices { get().impl->execute([](auto &settings_iface) { return settings_iface.enumAvailableDevices(); }) };
53+
BOOST_LOG(info) << "Currently available display devices:\n"
54+
<< toJson(available_devices);
55+
56+
// TODO: In the upcoming PR, schedule recovery here
57+
}
58+
59+
class deinit_t: public platf::deinit_t {
60+
public:
61+
~deinit_t() override {
62+
// TODO: In the upcoming PR, execute recovery once here
63+
get().impl = nullptr;
64+
}
65+
};
66+
return std::make_unique<deinit_t>();
67+
}
68+
69+
std::string
70+
session_t::map_output_name(const std::string &output_name) const {
71+
if (impl) {
72+
return impl->execute([&output_name](auto &settings_iface) { return settings_iface.getDisplayName(output_name); });
73+
}
74+
75+
// Fallback to giving back the output name if the platform is not supported.
76+
return output_name;
77+
}
78+
79+
session_t::session_t() = default;
80+
} // namespace display_device

src/display_device.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @file src/display_device.h
3+
* @brief Declarations for display device handling.
4+
*/
5+
#pragma once
6+
7+
// local includes
8+
#include "platform/common.h"
9+
10+
// forward declarations
11+
namespace display_device {
12+
template <class T>
13+
class RetryScheduler;
14+
class SettingsManagerInterface;
15+
} // namespace display_device
16+
17+
namespace display_device {
18+
/**
19+
* @brief A singleton class for managing the display device configuration for the whole Sunshine session.
20+
*
21+
* This class is meant to be an entry point for applying the configuration and reverting it later
22+
* from within the various places in the Sunshine's source code.
23+
*/
24+
class session_t {
25+
public:
26+
/**
27+
* @brief Get the singleton instance.
28+
* @returns Singleton instance for the class.
29+
*
30+
* EXAMPLES:
31+
* ```cpp
32+
* session_t& session { session_t::get() };
33+
* ```
34+
*/
35+
[[nodiscard]] static session_t &
36+
get();
37+
38+
/**
39+
* @brief Initialize the singleton and perform the initial state recovery (if needed).
40+
* @returns A deinit_t instance that performs cleanup when destroyed.
41+
*
42+
* EXAMPLES:
43+
* ```cpp
44+
* const auto session_guard { session_t::init() };
45+
* ```
46+
*/
47+
[[nodiscard]] static std::unique_ptr<platf::deinit_t>
48+
init();
49+
50+
/**
51+
* @brief Map the output name to a specific display.
52+
* @param output_name The user-configurable output name.
53+
* @returns Mapped display name or empty string if the output name could not be mapped.
54+
*
55+
* EXAMPLES:
56+
* ```cpp
57+
* session_t& session { session_t::get() };
58+
* const auto mapped_name_config { session.get_display_name(config::video.output_name) };
59+
* const auto mapped_name_custom { session.get_display_name("{some-device-id}") };
60+
* ```
61+
*/
62+
[[nodiscard]] std::string
63+
map_output_name(const std::string &output_name) const;
64+
65+
/**
66+
* @brief A deleted copy constructor for singleton pattern.
67+
* @note Public to ensure better error message.
68+
*/
69+
session_t(session_t const &) = delete;
70+
71+
/**
72+
* @brief A deleted assignment operator for singleton pattern.
73+
* @note Public to ensure better error message.
74+
*/
75+
void
76+
operator=(session_t const &) = delete;
77+
78+
private:
79+
/**
80+
* @brief A private constructor to ensure the singleton pattern.
81+
* @note Cannot be defaulted in declaration because of forward declared RetryScheduler.
82+
*/
83+
explicit session_t();
84+
85+
std::unique_ptr<RetryScheduler<SettingsManagerInterface>> impl; /**< Platform specific interface for managing settings (with retry functionality). */
86+
};
87+
} // namespace display_device

src/logging.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <boost/log/expressions.hpp>
1414
#include <boost/log/sinks.hpp>
1515
#include <boost/log/sources/severity_logger.hpp>
16+
#include <display_device/logging.h>
1617

1718
// local includes
1819
#include "logging.h"
@@ -56,6 +57,7 @@ namespace logging {
5657
}
5758

5859
setup_av_logging(min_log_level);
60+
setup_libdisplaydevice_logging(min_log_level);
5961

6062
sink = boost::make_shared<text_sink>();
6163

@@ -142,6 +144,37 @@ namespace logging {
142144
});
143145
}
144146

147+
void
148+
setup_libdisplaydevice_logging(int min_log_level) {
149+
constexpr int min_level { static_cast<int>(display_device::Logger::LogLevel::verbose) };
150+
constexpr int max_level { static_cast<int>(display_device::Logger::LogLevel::fatal) };
151+
const auto log_level { static_cast<display_device::Logger::LogLevel>(std::min(std::max(min_level, min_log_level), max_level)) };
152+
153+
display_device::Logger::get().setLogLevel(log_level);
154+
display_device::Logger::get().setCustomCallback([](const display_device::Logger::LogLevel level, const std::string &message) {
155+
switch (level) {
156+
case display_device::Logger::LogLevel::verbose:
157+
BOOST_LOG(verbose) << message;
158+
break;
159+
case display_device::Logger::LogLevel::debug:
160+
BOOST_LOG(debug) << message;
161+
break;
162+
case display_device::Logger::LogLevel::info:
163+
BOOST_LOG(info) << message;
164+
break;
165+
case display_device::Logger::LogLevel::warning:
166+
BOOST_LOG(warning) << message;
167+
break;
168+
case display_device::Logger::LogLevel::error:
169+
BOOST_LOG(error) << message;
170+
break;
171+
case display_device::Logger::LogLevel::fatal:
172+
BOOST_LOG(fatal) << message;
173+
break;
174+
}
175+
});
176+
}
177+
145178
void
146179
log_flush() {
147180
if (sink) {

src/logging.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ namespace logging {
6060
void
6161
setup_av_logging(int min_log_level);
6262

63+
/**
64+
* @brief Setup logging for libdisplaydevice.
65+
* @param min_log_level The log level.
66+
*/
67+
void
68+
setup_libdisplaydevice_logging(int min_log_level);
69+
6370
/**
6471
* @brief Flush the log.
6572
* @examples

0 commit comments

Comments
 (0)