Skip to content

Commit

Permalink
Add new API to modify display devices for Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
FrogTheFrog committed Feb 4, 2024
1 parent 0d4dfcd commit 5721f82
Show file tree
Hide file tree
Showing 44 changed files with 3,826 additions and 35 deletions.
9 changes: 9 additions & 0 deletions cmake/compile_definitions/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ set(SUNSHINE_TARGET_FILES
src/audio.cpp
src/audio.h
src/platform/common.h
src/display_device/display_device.h
src/display_device/parsed_config.cpp
src/display_device/parsed_config.h
src/display_device/session.cpp
src/display_device/session.h
src/display_device/settings.cpp
src/display_device/settings.h
src/display_device/to_string.cpp
src/display_device/to_string.h
src/process.cpp
src/process.h
src/network.cpp
Expand Down
1 change: 1 addition & 0 deletions cmake/compile_definitions/linux.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ list(APPEND PLATFORM_TARGET_FILES
src/platform/linux/misc.cpp
src/platform/linux/audio.cpp
src/platform/linux/input.cpp
src/platform/linux/display_device.cpp
third-party/glad/src/egl.c
third-party/glad/src/gl.c
third-party/glad/include/EGL/eglplatform.h
Expand Down
1 change: 1 addition & 0 deletions cmake/compile_definitions/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ set(PLATFORM_TARGET_FILES
src/platform/macos/nv12_zero_device.cpp
src/platform/macos/nv12_zero_device.h
src/platform/macos/publish.cpp
src/platform/macos/display_device.cpp
third-party/TPCircularBuffer/TPCircularBuffer.c
third-party/TPCircularBuffer/TPCircularBuffer.h
${APPLE_PLIST_FILE})
Expand Down
10 changes: 10 additions & 0 deletions cmake/compile_definitions/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ set(PLATFORM_TARGET_FILES
src/platform/windows/display_vram.cpp
src/platform/windows/display_ram.cpp
src/platform/windows/audio.cpp
src/platform/windows/display_device/device_hdr_states.cpp
src/platform/windows/display_device/device_modes.cpp
src/platform/windows/display_device/device_topology.cpp
src/platform/windows/display_device/general_functions.cpp
src/platform/windows/display_device/settings_data.h
src/platform/windows/display_device/settings_topology.cpp
src/platform/windows/display_device/settings_topology.h
src/platform/windows/display_device/settings.cpp
src/platform/windows/display_device/windows_utils.h
src/platform/windows/display_device/windows_utils.cpp
third-party/ViGEmClient/src/ViGEmClient.cpp
third-party/ViGEmClient/include/ViGEm/Client.h
third-party/ViGEmClient/include/ViGEm/Common.h
Expand Down
28 changes: 24 additions & 4 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ keybindings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

**Description**
Select the display number you want to stream.
Select the display you want to stream.

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

Expand All @@ -631,9 +631,29 @@ keybindings
.. todo:: macOS

**Windows**
.. code-block:: batch
During Sunshine startup, you should see the list of detected display devices:

tools\dxgi-info.exe
.. code-block:: text
DEVICE ID: \\?\DISPLAY-ACI27EC-5&4fd2de4&2&UID4355-{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
DISPLAY NAME: \\.\DISPLAY1
FRIENDLY NAME: ROG PG279Q
DEVICE STATE: PRIMARY
HDR STATE: UNKNOWN
-----------------------
DEVICE ID: \\?\DISPLAY-LNX0000-1&28a6823a&4&UID256-{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
DISPLAY NAME: NOT AVAILABLE
FRIENDLY NAME: IDD HDR
DEVICE STATE: INACTIVE
HDR STATE: UNKNOWN
-----------------------
DEVICE ID: \\?\DISPLAY-XMD009A-5&4fd2de4&2&UID4354-{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
DISPLAY NAME: NOT AVAILABLE
FRIENDLY NAME: Mi TV
DEVICE STATE: INACTIVE
HDR STATE: UNKNOWN
You need to use the ``DEVICE ID`` value.

**Default**
Sunshine will select the default display.
Expand All @@ -649,7 +669,7 @@ keybindings
**Windows**
.. code-block:: text
output_name = \\.\DISPLAY1
output_name = \\?\DISPLAY-LNX0000-1&28a6823a&4&UID256-{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
`resolutions <https://localhost:47990/config/#resolutions>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
14 changes: 14 additions & 0 deletions docs/source/source_code/source_code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ Source

src/*

.. toctree::
:caption: src/display_device
:maxdepth: 1
:glob:

src/display_device/*

.. toctree::
:caption: src/platform
:maxdepth: 1
Expand Down Expand Up @@ -89,3 +96,10 @@ Source
:glob:

src/platform/windows/*

.. toctree::
:caption: src/platform/windows/display_device
:maxdepth: 1
:glob:

src/platform/windows/display_device/*
4 changes: 4 additions & 0 deletions docs/source/source_code/src/display_device/display_device.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
display_device
==============

.. todo:: Add display_device.h
4 changes: 4 additions & 0 deletions docs/source/source_code/src/display_device/parsed_config.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parsed_config
=============

.. todo:: Add parsed_config.h
4 changes: 4 additions & 0 deletions docs/source/source_code/src/display_device/session.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
session
=======

.. todo:: Add session.h
4 changes: 4 additions & 0 deletions docs/source/source_code/src/display_device/settings.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
settings
========

.. todo:: Add settings.h
4 changes: 4 additions & 0 deletions docs/source/source_code/src/display_device/to_string.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
to_string
=========

.. todo:: Add to_string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
settings_data
=============

.. todo:: Add settings_data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
settings_topology
=================

.. todo:: Add settings_topology.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
windows_utils
=============

.. todo:: Add windows_utils.h
15 changes: 15 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "rtsp.h"
#include "utility.h"

#include "display_device/parsed_config.h"
#include "platform/common.h"

#ifdef _WIN32
Expand Down Expand Up @@ -363,7 +364,14 @@ namespace config {
{}, // capture
{}, // encoder
{}, // adapter_name

{}, // output_name
(int) display_device::parsed_config_t::device_prep_e::no_operation, // display_device_prep
(int) display_device::parsed_config_t::resolution_change_e::automatic, // resolution_change
{}, // manual_resolution
(int) display_device::parsed_config_t::refresh_rate_change_e::automatic, // refresh_rate_change
{}, // manual_refresh_rate
(int) display_device::parsed_config_t::hdr_prep_e::automatic // hdr_prep
};

audio_t audio {
Expand Down Expand Up @@ -996,7 +1004,14 @@ namespace config {
string_f(vars, "capture", video.capture);
string_f(vars, "encoder", video.encoder);
string_f(vars, "adapter_name", video.adapter_name);

string_f(vars, "output_name", video.output_name);
int_f(vars, "display_device_prep", video.display_device_prep, display_device::parsed_config_t::device_prep_from_view);
int_f(vars, "resolution_change", video.resolution_change, display_device::parsed_config_t::resolution_change_from_view);
string_f(vars, "manual_resolution", video.manual_resolution);
int_f(vars, "refresh_rate_change", video.refresh_rate_change, display_device::parsed_config_t::refresh_rate_change_from_view);
string_f(vars, "manual_refresh_rate", video.manual_refresh_rate);
int_f(vars, "hdr_prep", video.hdr_prep, display_device::parsed_config_t::hdr_prep_from_view);

path_f(vars, "pkey", nvhttp.pkey);
path_f(vars, "cert", nvhttp.cert);
Expand Down
7 changes: 7 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,14 @@ namespace config {
std::string capture;
std::string encoder;
std::string adapter_name;

std::string output_name;
int display_device_prep;
int resolution_change;
std::string manual_resolution;
int refresh_rate_change;
std::string manual_refresh_rate;
int hdr_prep;
};

struct audio_t {
Expand Down
182 changes: 182 additions & 0 deletions src/display_device/display_device.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#pragma once

// standard includes
#include <map>
#include <string>
#include <unordered_set>
#include <vector>

// lib includes
#include <boost/optional.hpp>
#include <nlohmann/json.hpp>

namespace display_device {

enum class device_state_e {
inactive,
active,
primary //! On Windows we can have multiple primary displays (when they are duplicated).
};

enum class hdr_state_e {
unknown, //! HDR state could not be retrieved from the system (even if the display could support it).
disabled,
enabled
};

// For JSON serialization for hdr_state_e
NLOHMANN_JSON_SERIALIZE_ENUM(hdr_state_e, { { hdr_state_e::unknown, "unknown" },
{ hdr_state_e::disabled, "disabled" },
{ hdr_state_e::enabled, "enabled" } })

//! A map of device id to its HDR state (ordered, for predictable print order)
using hdr_state_map_t = std::map<std::string, hdr_state_e>;

struct device_info_t {
//! A name used by the system to represent the logical display this device is connected to.
std::string display_name;

//! A more human-readable name for the device.
std::string friendly_name;

//! Current state of the device.
device_state_e device_state;

//! Current state of the HDR support.
hdr_state_e hdr_state;
};

//! A map of device id to its info data (ordered, for predictable print order).
using device_info_map_t = std::map<std::string, device_info_t>;

struct resolution_t {
unsigned int width;
unsigned int height;

// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(resolution_t, width, height)
};

//! Stores a floating point number in a "numerator/denominator" form
struct refresh_rate_t {
unsigned int numerator;
unsigned int denominator;

// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(refresh_rate_t, numerator, denominator)
};

struct display_mode_t {
resolution_t resolution;
refresh_rate_t refresh_rate;

// For JSON serialization
NLOHMANN_DEFINE_TYPE_INTRUSIVE(display_mode_t, resolution, refresh_rate)
};

// A map of device id to its mode data (ordered, for predictable print order).
using device_display_mode_map_t = std::map<std::string, display_mode_t>;

/*!
* A list of a list of device ids representing the current topology.
*
* For example:
* ```
* [[EXTENDED_DISPLAY_1], [DUPLICATED_DISPLAY_1, DUPLICATED_DISPLAY_2], [EXTENDED_DISPLAY_2]]
* ```
*
* @note On Windows the order does not matter as Windows will take care of the device the placement anyway.
*/
using active_topology_t = std::vector<std::vector<std::string>>;

/*!
* Enumerates the available devices in the system.
*/
device_info_map_t
enum_available_devices();

/*!
* Gets display name associated with the device.
* @note returns empty string if the device_id is empty or device is inactive.
*/
std::string
get_display_name(const std::string &device_id);

/*!
* Get current display mode for the provided devices.
*
* @note empty map will be returned if any of the devices does not have a mode.
*/
device_display_mode_map_t
get_current_display_modes(const std::unordered_set<std::string> &device_ids);

/*!
* Try to set the new display modes for the devices.
*
* @warning if any of the specified display are duplicated, modes MUST be provided
* for duplicates too!
*/
bool
set_display_modes(const device_display_mode_map_t &modes);

/*!
* Check whether the specified device is primary.
*/
bool
is_primary_device(const std::string &device_id);

/*!
* Try to set the device as a primary display.
*
* @note if the device is duplicated, the other paired device will also become a primary display.
*/
bool
set_as_primary_device(const std::string &device_id);

/*!
* Try to get the HDR state for the provided devices.
*
* @note on Windows the state cannot be retrieved until the device is active.
*/
hdr_state_map_t
get_current_hdr_states(const std::unordered_set<std::string> &device_ids);

/*!
* Try to set the HDR state for the devices.
*
* @note if UNKNOWN states are provided, they will be ignored.
*/
bool
set_hdr_states(const hdr_state_map_t &states);

/*!
* Get the currently active topology.
*
* @note empty list will be returned if topology could not be retrieved.
*/
active_topology_t
get_current_topology();

/*!
* Simply validates the topology to be valid.
*/
bool
is_topology_valid(const active_topology_t &topology);

/*!
* Checks if the topologies are close enough to be considered the same by the system.
*/
bool
is_topology_the_same(const active_topology_t &a, const active_topology_t &b);

/*!
* Try to set the active states for the devices.
*
* @warning there is a bug on Windows (yay) where it is unable to sometimes set
* topology correctly, but it thinks it did! See implementation for more
* details.
*/
bool
set_topology(const active_topology_t &new_topology);

} // namespace display_device
Loading

0 comments on commit 5721f82

Please sign in to comment.