style: adjust clang-format rules (#2186)
Co-authored-by: Vithorio Polten <reach@vithor.io>
This commit is contained in:
@@ -8,14 +8,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// platform includes
|
||||
#include <mmdeviceapi.h>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#undef DEFINE_GUID
|
||||
#ifdef __cplusplus
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
|
||||
#else
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
|
||||
#endif
|
||||
|
||||
DEFINE_GUID(IID_IPolicyConfig, 0xf8679f50, 0x850a, 0x41cf, 0x9c, 0x72, 0x43, 0x0f, 0x29, 0x02, 0x90, 0xc8);
|
||||
@@ -40,64 +41,69 @@ class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient;
|
||||
// ----------------------------------------------------------------------------
|
||||
interface IPolicyConfig: public IUnknown {
|
||||
public:
|
||||
virtual HRESULT
|
||||
GetMixFormat(
|
||||
virtual HRESULT GetMixFormat(
|
||||
PCWSTR,
|
||||
WAVEFORMATEX **);
|
||||
WAVEFORMATEX **
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetDeviceFormat(
|
||||
virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
|
||||
PCWSTR,
|
||||
INT,
|
||||
WAVEFORMATEX **);
|
||||
WAVEFORMATEX **
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat(
|
||||
PCWSTR);
|
||||
PCWSTR
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetDeviceFormat(
|
||||
PCWSTR,
|
||||
WAVEFORMATEX *,
|
||||
WAVEFORMATEX *);
|
||||
SetDeviceFormat(
|
||||
PCWSTR,
|
||||
WAVEFORMATEX *,
|
||||
WAVEFORMATEX *
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
|
||||
PCWSTR,
|
||||
INT,
|
||||
PINT64,
|
||||
PINT64);
|
||||
PINT64
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
|
||||
PCWSTR,
|
||||
PINT64);
|
||||
PINT64
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetShareMode(
|
||||
virtual HRESULT STDMETHODCALLTYPE GetShareMode(
|
||||
PCWSTR,
|
||||
struct DeviceShareMode *);
|
||||
struct DeviceShareMode *
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetShareMode(
|
||||
virtual HRESULT STDMETHODCALLTYPE SetShareMode(
|
||||
PCWSTR,
|
||||
struct DeviceShareMode *);
|
||||
struct DeviceShareMode *
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetPropertyValue(
|
||||
virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
|
||||
PCWSTR,
|
||||
const PROPERTYKEY &,
|
||||
PROPVARIANT *);
|
||||
PROPVARIANT *
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetPropertyValue(
|
||||
virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
|
||||
PCWSTR,
|
||||
const PROPERTYKEY &,
|
||||
PROPVARIANT *);
|
||||
PROPVARIANT *
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetDefaultEndpoint(
|
||||
virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
|
||||
PCWSTR wszDeviceId,
|
||||
ERole eRole);
|
||||
ERole eRole
|
||||
);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
|
||||
PCWSTR,
|
||||
INT);
|
||||
INT
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,22 +3,21 @@
|
||||
* @brief Definitions for Windows audio capture.
|
||||
*/
|
||||
#define INITGUID
|
||||
#include <audioclient.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <roapi.h>
|
||||
|
||||
// platform includes
|
||||
#include <audioclient.h>
|
||||
#include <avrt.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <newdev.h>
|
||||
#include <roapi.h>
|
||||
#include <synchapi.h>
|
||||
|
||||
#include <newdev.h>
|
||||
|
||||
#include <avrt.h>
|
||||
|
||||
// local includes
|
||||
#include "misc.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
// Must be the last included file
|
||||
// clang-format off
|
||||
#include "PolicyConfig.h"
|
||||
@@ -65,8 +64,7 @@ namespace {
|
||||
_size,
|
||||
};
|
||||
|
||||
constexpr WAVEFORMATEXTENSIBLE
|
||||
create_waveformat(sample_format_e sample_format, WORD channel_count, DWORD channel_mask) {
|
||||
constexpr WAVEFORMATEXTENSIBLE create_waveformat(sample_format_e sample_format, WORD channel_count, DWORD channel_mask) {
|
||||
WAVEFORMATEXTENSIBLE waveformat = {};
|
||||
|
||||
switch (sample_format) {
|
||||
@@ -119,9 +117,8 @@ namespace {
|
||||
|
||||
using virtual_sink_waveformats_t = std::vector<WAVEFORMATEXTENSIBLE>;
|
||||
|
||||
template <WORD channel_count>
|
||||
virtual_sink_waveformats_t
|
||||
create_virtual_sink_waveformats() {
|
||||
template<WORD channel_count>
|
||||
virtual_sink_waveformats_t create_virtual_sink_waveformats() {
|
||||
if constexpr (channel_count == 2) {
|
||||
auto channel_mask = waveformat_mask_stereo;
|
||||
// only choose 24 or 16-bit formats to avoid clobbering existing Dolby/DTS spatial audio settings
|
||||
@@ -130,8 +127,7 @@ namespace {
|
||||
create_waveformat(sample_format_e::s24, channel_count, channel_mask),
|
||||
create_waveformat(sample_format_e::s16, channel_count, channel_mask),
|
||||
};
|
||||
}
|
||||
else if (channel_count == 6) {
|
||||
} else if (channel_count == 6) {
|
||||
auto channel_mask1 = waveformat_mask_surround51_with_backspeakers;
|
||||
auto channel_mask2 = waveformat_mask_surround51_with_sidespeakers;
|
||||
return {
|
||||
@@ -146,8 +142,7 @@ namespace {
|
||||
create_waveformat(sample_format_e::s16, channel_count, channel_mask1),
|
||||
create_waveformat(sample_format_e::s16, channel_count, channel_mask2),
|
||||
};
|
||||
}
|
||||
else if (channel_count == 8) {
|
||||
} else if (channel_count == 8) {
|
||||
auto channel_mask = waveformat_mask_surround71;
|
||||
return {
|
||||
create_waveformat(sample_format_e::f32, channel_count, channel_mask),
|
||||
@@ -159,8 +154,7 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
waveformat_to_pretty_string(const WAVEFORMATEXTENSIBLE &waveformat) {
|
||||
std::string waveformat_to_pretty_string(const WAVEFORMATEXTENSIBLE &waveformat) {
|
||||
std::string result = waveformat.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ? "F" :
|
||||
waveformat.SubFormat == KSDATAFORMAT_SUBTYPE_PCM ? "S" :
|
||||
"UNKNOWN";
|
||||
@@ -196,16 +190,15 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::audio {
|
||||
template <class T>
|
||||
void
|
||||
Release(T *p) {
|
||||
template<class T>
|
||||
void Release(T *p) {
|
||||
p->Release();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
co_task_free(T *p) {
|
||||
template<class T>
|
||||
void co_task_free(T *p) {
|
||||
CoTaskMemFree((LPVOID) p);
|
||||
}
|
||||
|
||||
@@ -272,14 +265,14 @@ namespace platf::audio {
|
||||
},
|
||||
};
|
||||
|
||||
audio_client_t
|
||||
make_audio_client(device_t &device, const format_t &format) {
|
||||
audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
||||
audio_client_t audio_client;
|
||||
auto status = device->Activate(
|
||||
IID_IAudioClient,
|
||||
CLSCTX_ALL,
|
||||
nullptr,
|
||||
(void **) &audio_client);
|
||||
(void **) &audio_client
|
||||
);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
@@ -311,9 +304,11 @@ namespace platf::audio {
|
||||
AUDCLNT_SHAREMODE_SHARED,
|
||||
AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
|
||||
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, // Enable automatic resampling to 48 KHz
|
||||
0, 0,
|
||||
0,
|
||||
0,
|
||||
(LPWAVEFORMATEX) &capture_waveformat,
|
||||
nullptr);
|
||||
nullptr
|
||||
);
|
||||
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't initialize audio client for ["sv << format.name << "]: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
@@ -325,14 +320,14 @@ namespace platf::audio {
|
||||
return audio_client;
|
||||
}
|
||||
|
||||
device_t
|
||||
default_device(device_enum_t &device_enum) {
|
||||
device_t default_device(device_enum_t &device_enum) {
|
||||
device_t device;
|
||||
HRESULT status;
|
||||
status = device_enum->GetDefaultAudioEndpoint(
|
||||
eRender,
|
||||
eConsole,
|
||||
&device);
|
||||
&device
|
||||
);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't get default audio endpoint [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
@@ -345,68 +340,68 @@ namespace platf::audio {
|
||||
|
||||
class audio_notification_t: public ::IMMNotificationClient {
|
||||
public:
|
||||
audio_notification_t() {}
|
||||
audio_notification_t() {
|
||||
}
|
||||
|
||||
// IUnknown implementation (unused by IMMDeviceEnumerator)
|
||||
ULONG STDMETHODCALLTYPE
|
||||
AddRef() {
|
||||
ULONG STDMETHODCALLTYPE AddRef() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE
|
||||
Release() {
|
||||
ULONG STDMETHODCALLTYPE Release() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
QueryInterface(REFIID riid, VOID **ppvInterface) {
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) {
|
||||
if (IID_IUnknown == riid) {
|
||||
AddRef();
|
||||
*ppvInterface = (IUnknown *) this;
|
||||
return S_OK;
|
||||
}
|
||||
else if (__uuidof(IMMNotificationClient) == riid) {
|
||||
} else if (__uuidof(IMMNotificationClient) == riid) {
|
||||
AddRef();
|
||||
*ppvInterface = (IMMNotificationClient *) this;
|
||||
return S_OK;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
*ppvInterface = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
// IMMNotificationClient
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) {
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) {
|
||||
if (flow == eRender) {
|
||||
default_render_device_changed_flag.store(true);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnDeviceAdded(LPCWSTR pwstrDeviceId) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnDeviceRemoved(LPCWSTR pwstrDeviceId) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnDeviceStateChanged(
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
|
||||
LPCWSTR pwstrDeviceId,
|
||||
DWORD dwNewState) { return S_OK; }
|
||||
DWORD dwNewState
|
||||
) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnPropertyValueChanged(
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
|
||||
LPCWSTR pwstrDeviceId,
|
||||
const PROPERTYKEY key) { return S_OK; }
|
||||
const PROPERTYKEY key
|
||||
) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the default rendering device changed and resets the change flag
|
||||
* @return `true` if the device changed since last call
|
||||
*/
|
||||
bool
|
||||
check_default_render_device_changed() {
|
||||
bool check_default_render_device_changed() {
|
||||
return default_render_device_changed_flag.exchange(false);
|
||||
}
|
||||
|
||||
@@ -416,8 +411,7 @@ namespace platf::audio {
|
||||
|
||||
class mic_wasapi_t: public mic_t {
|
||||
public:
|
||||
capture_e
|
||||
sample(std::vector<float> &sample_out) override {
|
||||
capture_e sample(std::vector<float> &sample_out) override {
|
||||
auto sample_size = sample_out.size();
|
||||
|
||||
// Refill the sample buffer if needed
|
||||
@@ -438,8 +432,7 @@ namespace platf::audio {
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
int
|
||||
init(std::uint32_t sample_rate, std::uint32_t frame_size, std::uint32_t channels_out) {
|
||||
int init(std::uint32_t sample_rate, std::uint32_t frame_size, std::uint32_t channels_out) {
|
||||
audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr));
|
||||
if (!audio_event) {
|
||||
BOOST_LOG(error) << "Couldn't create Event handle"sv;
|
||||
@@ -454,7 +447,8 @@ namespace platf::audio {
|
||||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator,
|
||||
(void **) &device_enum);
|
||||
(void **) &device_enum
|
||||
);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create Device Enumerator [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
@@ -509,7 +503,7 @@ namespace platf::audio {
|
||||
}
|
||||
|
||||
// *2 --> needs to fit double
|
||||
sample_buf = util::buffer_t<float> { std::max(frames, frame_size) * 2 * channels_out };
|
||||
sample_buf = util::buffer_t<float> {std::max(frames, frame_size) * 2 * channels_out};
|
||||
sample_buf_pos = std::begin(sample_buf);
|
||||
|
||||
status = audio_client->GetService(IID_IAudioCaptureClient, (void **) &audio_capture);
|
||||
@@ -559,8 +553,7 @@ namespace platf::audio {
|
||||
}
|
||||
|
||||
private:
|
||||
capture_e
|
||||
_fill_buffer() {
|
||||
capture_e _fill_buffer() {
|
||||
HRESULT status;
|
||||
|
||||
// Total number of samples
|
||||
@@ -600,13 +593,16 @@ namespace platf::audio {
|
||||
for (
|
||||
status = audio_capture->GetNextPacketSize(&packet_size);
|
||||
SUCCEEDED(status) && packet_size > 0;
|
||||
status = audio_capture->GetNextPacketSize(&packet_size)) {
|
||||
status = audio_capture->GetNextPacketSize(&packet_size)
|
||||
) {
|
||||
DWORD buffer_flags;
|
||||
status = audio_capture->GetBuffer(
|
||||
(BYTE **) &sample_aligned.samples,
|
||||
&block_aligned.audio_sample_size,
|
||||
&buffer_flags,
|
||||
nullptr, nullptr);
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
|
||||
switch (status) {
|
||||
case S_OK:
|
||||
@@ -631,8 +627,7 @@ namespace platf::audio {
|
||||
|
||||
if (buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) {
|
||||
std::fill_n(sample_buf_pos, n, 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
std::copy_n(sample_aligned.samples, n, sample_buf_pos);
|
||||
}
|
||||
|
||||
@@ -674,8 +669,7 @@ namespace platf::audio {
|
||||
|
||||
class audio_control_t: public ::platf::audio_control_t {
|
||||
public:
|
||||
std::optional<sink_t>
|
||||
sink_info() override {
|
||||
std::optional<sink_t> sink_info() override {
|
||||
sink_t sink;
|
||||
|
||||
// Fill host sink name with the device_id of the current default audio device.
|
||||
@@ -697,8 +691,7 @@ namespace platf::audio {
|
||||
match_fields_list_t match_list;
|
||||
if (config::audio.virtual_sink.empty()) {
|
||||
match_list = match_steam_speakers();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
match_list = match_all_fields(from_utf8(config::audio.virtual_sink));
|
||||
}
|
||||
|
||||
@@ -714,16 +707,14 @@ namespace platf::audio {
|
||||
"virtual-"s + formats[1].name + device_id,
|
||||
"virtual-"s + formats[2].name + device_id,
|
||||
});
|
||||
}
|
||||
else if (!config::audio.virtual_sink.empty()) {
|
||||
} else if (!config::audio.virtual_sink.empty()) {
|
||||
BOOST_LOG(warning) << "Couldn't find the specified virtual audio sink " << config::audio.virtual_sink;
|
||||
}
|
||||
|
||||
return sink;
|
||||
}
|
||||
|
||||
bool
|
||||
is_sink_available(const std::string &sink) override {
|
||||
bool is_sink_available(const std::string &sink) override {
|
||||
const auto match_list = match_all_fields(from_utf8(sink));
|
||||
const auto matched = find_device_id(match_list);
|
||||
return static_cast<bool>(matched);
|
||||
@@ -735,8 +726,7 @@ namespace platf::audio {
|
||||
* @return A pair of device_id and format reference if the sink name matches
|
||||
* our naming scheme for virtual audio sinks, `std::nullopt` otherwise.
|
||||
*/
|
||||
std::optional<std::pair<std::wstring, std::reference_wrapper<const format_t>>>
|
||||
extract_virtual_sink_info(const std::string &sink) {
|
||||
std::optional<std::pair<std::wstring, std::reference_wrapper<const format_t>>> extract_virtual_sink_info(const std::string &sink) {
|
||||
// Encoding format:
|
||||
// [virtual-(format name)]device_id
|
||||
std::string current = sink;
|
||||
@@ -756,8 +746,7 @@ namespace platf::audio {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<mic_t>
|
||||
microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
|
||||
std::unique_ptr<mic_t> microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
|
||||
auto mic = std::make_unique<mic_wasapi_t>();
|
||||
|
||||
if (mic->init(sample_rate, frame_size, channels)) {
|
||||
@@ -784,8 +773,7 @@ namespace platf::audio {
|
||||
* virtual-(format name)
|
||||
* If it doesn't contain that prefix, then the format will not be changed
|
||||
*/
|
||||
std::optional<std::wstring>
|
||||
set_format(const std::string &sink) {
|
||||
std::optional<std::wstring> set_format(const std::string &sink) {
|
||||
if (sink.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -799,8 +787,7 @@ namespace platf::audio {
|
||||
auto matched = find_device_id(match_all_fields(from_utf8(sink)));
|
||||
if (matched) {
|
||||
return matched->second;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(error) << "Couldn't find audio sink " << sink;
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -826,8 +813,7 @@ namespace platf::audio {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int
|
||||
set_sink(const std::string &sink) override {
|
||||
int set_sink(const std::string &sink) override {
|
||||
auto device_id = set_format(sink);
|
||||
if (!device_id) {
|
||||
return -1;
|
||||
@@ -840,8 +826,7 @@ namespace platf::audio {
|
||||
// Depending on the format of the string, we could get either of these errors
|
||||
if (status == HRESULT_FROM_WIN32(ERROR_NOT_FOUND) || status == E_INVALIDARG) {
|
||||
BOOST_LOG(warning) << "Audio sink not found: "sv << sink;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(warning) << "Couldn't set ["sv << sink << "] to role ["sv << x << "]: 0x"sv << util::hex(status).to_string_view();
|
||||
}
|
||||
|
||||
@@ -868,20 +853,18 @@ namespace platf::audio {
|
||||
using match_fields_list_t = std::vector<std::pair<match_field_e, std::wstring>>;
|
||||
using matched_field_t = std::pair<match_field_e, std::wstring>;
|
||||
|
||||
audio_control_t::match_fields_list_t
|
||||
match_steam_speakers() {
|
||||
audio_control_t::match_fields_list_t match_steam_speakers() {
|
||||
return {
|
||||
{ match_field_e::adapter_friendly_name, L"Steam Streaming Speakers" }
|
||||
{match_field_e::adapter_friendly_name, L"Steam Streaming Speakers"}
|
||||
};
|
||||
}
|
||||
|
||||
audio_control_t::match_fields_list_t
|
||||
match_all_fields(const std::wstring &name) {
|
||||
audio_control_t::match_fields_list_t match_all_fields(const std::wstring &name) {
|
||||
return {
|
||||
{ match_field_e::device_id, name }, // {0.0.0.00000000}.{29dd7668-45b2-4846-882d-950f55bf7eb8}
|
||||
{ match_field_e::device_friendly_name, name }, // Digital Audio (S/PDIF) (High Definition Audio Device)
|
||||
{ match_field_e::device_description, name }, // Digital Audio (S/PDIF)
|
||||
{ match_field_e::adapter_friendly_name, name }, // High Definition Audio Device
|
||||
{match_field_e::device_id, name}, // {0.0.0.00000000}.{29dd7668-45b2-4846-882d-950f55bf7eb8}
|
||||
{match_field_e::device_friendly_name, name}, // Digital Audio (S/PDIF) (High Definition Audio Device)
|
||||
{match_field_e::device_description, name}, // Digital Audio (S/PDIF)
|
||||
{match_field_e::adapter_friendly_name, name}, // High Definition Audio Device
|
||||
};
|
||||
}
|
||||
|
||||
@@ -890,8 +873,7 @@ namespace platf::audio {
|
||||
* @param match_list Pairs of match fields and values
|
||||
* @return Optional pair of matched field and device_id
|
||||
*/
|
||||
std::optional<matched_field_t>
|
||||
find_device_id(const match_fields_list_t &match_list) {
|
||||
std::optional<matched_field_t> find_device_id(const match_fields_list_t &match_list) {
|
||||
if (match_list.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -965,8 +947,7 @@ namespace platf::audio {
|
||||
/**
|
||||
* @brief Resets the default audio device from Steam Streaming Speakers.
|
||||
*/
|
||||
void
|
||||
reset_default_device() {
|
||||
void reset_default_device() {
|
||||
auto matched_steam = find_device_id(match_steam_speakers());
|
||||
if (!matched_steam) {
|
||||
return;
|
||||
@@ -1027,8 +1008,7 @@ namespace platf::audio {
|
||||
* @brief Installs the Steam Streaming Speakers driver, if present.
|
||||
* @return `true` if installation was successful.
|
||||
*/
|
||||
bool
|
||||
install_steam_audio_drivers() {
|
||||
bool install_steam_audio_drivers() {
|
||||
#ifdef STEAM_DRIVER_SUBDIR
|
||||
// MinGW's libnewdev.a is missing DiInstallDriverW() even though the headers have it,
|
||||
// so we have to load it at runtime. It's Vista or later, so it will always be available.
|
||||
@@ -1072,8 +1052,7 @@ namespace platf::audio {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto err = GetLastError();
|
||||
switch (err) {
|
||||
case ERROR_ACCESS_DENIED:
|
||||
@@ -1096,14 +1075,14 @@ namespace platf::audio {
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
init() {
|
||||
int init() {
|
||||
auto status = CoCreateInstance(
|
||||
CLSID_CPolicyConfigClient,
|
||||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IPolicyConfig,
|
||||
(void **) &policy);
|
||||
(void **) &policy
|
||||
);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create audio policy config: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
@@ -1116,7 +1095,8 @@ namespace platf::audio {
|
||||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator,
|
||||
(void **) &device_enum);
|
||||
(void **) &device_enum
|
||||
);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
@@ -1126,7 +1106,8 @@ namespace platf::audio {
|
||||
return 0;
|
||||
}
|
||||
|
||||
~audio_control_t() override {}
|
||||
~audio_control_t() override {
|
||||
}
|
||||
|
||||
policy_t policy;
|
||||
audio::device_enum_t device_enum;
|
||||
@@ -1138,12 +1119,10 @@ namespace platf {
|
||||
|
||||
// It's not big enough to justify it's own source file :/
|
||||
namespace dxgi {
|
||||
int
|
||||
init();
|
||||
int init();
|
||||
}
|
||||
|
||||
std::unique_ptr<audio_control_t>
|
||||
audio_control() {
|
||||
std::unique_ptr<audio_control_t> audio_control() {
|
||||
auto control = std::make_unique<audio::audio_control_t>();
|
||||
|
||||
if (control->init()) {
|
||||
@@ -1160,8 +1139,7 @@ namespace platf {
|
||||
return control;
|
||||
}
|
||||
|
||||
std::unique_ptr<deinit_t>
|
||||
init() {
|
||||
std::unique_ptr<deinit_t> init() {
|
||||
if (dxgi::init()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// platform includes
|
||||
#include <d3d11.h>
|
||||
#include <d3d11_4.h>
|
||||
#include <d3dcommon.h>
|
||||
#include <dwmapi.h>
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_6.h>
|
||||
|
||||
#include <Unknwn.h>
|
||||
#include <winrt/Windows.Graphics.Capture.h>
|
||||
|
||||
// local includes
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
#include "src/video.h"
|
||||
@@ -25,9 +26,8 @@ namespace platf::dxgi {
|
||||
// You should have a debugger like WinDbg attached to receive debug messages.
|
||||
auto constexpr D3D11_CREATE_DEVICE_FLAGS = 0;
|
||||
|
||||
template <class T>
|
||||
void
|
||||
Release(T *dxgi) {
|
||||
template<class T>
|
||||
void Release(T *dxgi) {
|
||||
dxgi->Release();
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ namespace platf::dxgi {
|
||||
} // namespace video
|
||||
|
||||
class hwdevice_t;
|
||||
|
||||
struct cursor_t {
|
||||
std::vector<std::uint8_t> img_data;
|
||||
|
||||
@@ -83,10 +84,9 @@ namespace platf::dxgi {
|
||||
class gpu_cursor_t {
|
||||
public:
|
||||
gpu_cursor_t():
|
||||
cursor_view { 0, 0, 0, 0, 0.0f, 1.0f } {};
|
||||
cursor_view {0, 0, 0, 0, 0.0f, 1.0f} {};
|
||||
|
||||
void
|
||||
set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, LONG display_height, DXGI_MODE_ROTATION display_rotation, bool visible) {
|
||||
void set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, LONG display_height, DXGI_MODE_ROTATION display_rotation, bool visible) {
|
||||
this->topleft_x = topleft_x;
|
||||
this->topleft_y = topleft_y;
|
||||
this->display_width = display_width;
|
||||
@@ -96,16 +96,14 @@ namespace platf::dxgi {
|
||||
update_viewport();
|
||||
}
|
||||
|
||||
void
|
||||
set_texture(LONG texture_width, LONG texture_height, texture2d_t &&texture) {
|
||||
void set_texture(LONG texture_width, LONG texture_height, texture2d_t &&texture) {
|
||||
this->texture = std::move(texture);
|
||||
this->texture_width = texture_width;
|
||||
this->texture_height = texture_height;
|
||||
update_viewport();
|
||||
}
|
||||
|
||||
void
|
||||
update_viewport() {
|
||||
void update_viewport() {
|
||||
switch (display_rotation) {
|
||||
case DXGI_MODE_ROTATION_UNSPECIFIED:
|
||||
case DXGI_MODE_ROTATION_IDENTITY:
|
||||
@@ -158,11 +156,9 @@ namespace platf::dxgi {
|
||||
|
||||
class display_base_t: public display_t {
|
||||
public:
|
||||
int
|
||||
init(const ::video::config_t &config, const std::string &display_name);
|
||||
int init(const ::video::config_t &config, const std::string &display_name);
|
||||
|
||||
capture_e
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override;
|
||||
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override;
|
||||
|
||||
factory1_t factory;
|
||||
adapter_t adapter;
|
||||
@@ -209,6 +205,7 @@ namespace platf::dxgi {
|
||||
UINT IndependentVidPnVSyncControl : 1;
|
||||
UINT Reserved : 28;
|
||||
};
|
||||
|
||||
UINT Value;
|
||||
};
|
||||
} D3DKMT_WDDM_2_7_CAPS;
|
||||
@@ -231,30 +228,21 @@ namespace platf::dxgi {
|
||||
typedef NTSTATUS(WINAPI *PD3DKMTQueryAdapterInfo)(D3DKMT_QUERYADAPTERINFO *);
|
||||
typedef NTSTATUS(WINAPI *PD3DKMTCloseAdapter)(D3DKMT_CLOSEADAPTER *);
|
||||
|
||||
virtual bool
|
||||
is_hdr() override;
|
||||
virtual bool
|
||||
get_hdr_metadata(SS_HDR_METADATA &metadata) override;
|
||||
virtual bool is_hdr() override;
|
||||
virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) override;
|
||||
|
||||
const char *
|
||||
dxgi_format_to_string(DXGI_FORMAT format);
|
||||
const char *
|
||||
colorspace_to_string(DXGI_COLOR_SPACE_TYPE type);
|
||||
virtual std::vector<DXGI_FORMAT>
|
||||
get_supported_capture_formats() = 0;
|
||||
const char *dxgi_format_to_string(DXGI_FORMAT format);
|
||||
const char *colorspace_to_string(DXGI_COLOR_SPACE_TYPE type);
|
||||
virtual std::vector<DXGI_FORMAT> get_supported_capture_formats() = 0;
|
||||
|
||||
protected:
|
||||
int
|
||||
get_pixel_pitch() {
|
||||
int get_pixel_pitch() {
|
||||
return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4;
|
||||
}
|
||||
|
||||
virtual capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) = 0;
|
||||
virtual capture_e
|
||||
release_snapshot() = 0;
|
||||
virtual int
|
||||
complete_img(img_t *img, bool dummy) = 0;
|
||||
virtual capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) = 0;
|
||||
virtual capture_e release_snapshot() = 0;
|
||||
virtual int complete_img(img_t *img, bool dummy) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -262,17 +250,12 @@ namespace platf::dxgi {
|
||||
*/
|
||||
class display_ram_t: public display_base_t {
|
||||
public:
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override;
|
||||
int
|
||||
dummy_img(img_t *img) override;
|
||||
int
|
||||
complete_img(img_t *img, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT>
|
||||
get_supported_capture_formats() override;
|
||||
std::shared_ptr<img_t> alloc_img() override;
|
||||
int dummy_img(img_t *img) override;
|
||||
int complete_img(img_t *img, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT> get_supported_capture_formats() override;
|
||||
|
||||
std::unique_ptr<avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
|
||||
std::unique_ptr<avcodec_encode_device_t> make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE img_info;
|
||||
texture2d_t texture;
|
||||
@@ -283,23 +266,16 @@ namespace platf::dxgi {
|
||||
*/
|
||||
class display_vram_t: public display_base_t, public std::enable_shared_from_this<display_vram_t> {
|
||||
public:
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override;
|
||||
int
|
||||
dummy_img(img_t *img_base) override;
|
||||
int
|
||||
complete_img(img_t *img_base, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT>
|
||||
get_supported_capture_formats() override;
|
||||
std::shared_ptr<img_t> alloc_img() override;
|
||||
int dummy_img(img_t *img_base) override;
|
||||
int complete_img(img_t *img_base, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT> get_supported_capture_formats() override;
|
||||
|
||||
bool
|
||||
is_codec_supported(std::string_view name, const ::video::config_t &config) override;
|
||||
bool is_codec_supported(std::string_view name, const ::video::config_t &config) override;
|
||||
|
||||
std::unique_ptr<avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
|
||||
std::unique_ptr<avcodec_encode_device_t> make_avcodec_encode_device(pix_fmt_e pix_fmt) override;
|
||||
|
||||
std::unique_ptr<nvenc_encode_device_t>
|
||||
make_nvenc_encode_device(pix_fmt_e pix_fmt) override;
|
||||
std::unique_ptr<nvenc_encode_device_t> make_nvenc_encode_device(pix_fmt_e pix_fmt) override;
|
||||
|
||||
std::atomic<uint32_t> next_image_id;
|
||||
};
|
||||
@@ -313,14 +289,10 @@ namespace platf::dxgi {
|
||||
bool has_frame {};
|
||||
std::chrono::steady_clock::time_point last_protected_content_warning_time {};
|
||||
|
||||
int
|
||||
init(display_base_t *display, const ::video::config_t &config);
|
||||
capture_e
|
||||
next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p);
|
||||
capture_e
|
||||
reset(dup_t::pointer dup_p = dup_t::pointer());
|
||||
capture_e
|
||||
release_frame();
|
||||
int init(display_base_t *display, const ::video::config_t &config);
|
||||
capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p);
|
||||
capture_e reset(dup_t::pointer dup_p = dup_t::pointer());
|
||||
capture_e release_frame();
|
||||
|
||||
~duplication_t();
|
||||
};
|
||||
@@ -330,12 +302,9 @@ namespace platf::dxgi {
|
||||
*/
|
||||
class display_ddup_ram_t: public display_ram_t {
|
||||
public:
|
||||
int
|
||||
init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
capture_e
|
||||
release_snapshot() override;
|
||||
int init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
capture_e release_snapshot() override;
|
||||
|
||||
duplication_t dup;
|
||||
cursor_t cursor;
|
||||
@@ -346,12 +315,9 @@ namespace platf::dxgi {
|
||||
*/
|
||||
class display_ddup_vram_t: public display_vram_t {
|
||||
public:
|
||||
int
|
||||
init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
capture_e
|
||||
release_snapshot() override;
|
||||
int init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
capture_e release_snapshot() override;
|
||||
|
||||
duplication_t dup;
|
||||
sampler_state_t sampler_linear;
|
||||
@@ -375,29 +341,24 @@ namespace platf::dxgi {
|
||||
* Display duplicator that uses the Windows.Graphics.Capture API.
|
||||
*/
|
||||
class wgc_capture_t {
|
||||
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice uwp_device { nullptr };
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item { nullptr };
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool { nullptr };
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureSession capture_session { nullptr };
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame produced_frame { nullptr }, consumed_frame { nullptr };
|
||||
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice uwp_device {nullptr};
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item {nullptr};
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool frame_pool {nullptr};
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureSession capture_session {nullptr};
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame produced_frame {nullptr}, consumed_frame {nullptr};
|
||||
SRWLOCK frame_lock = SRWLOCK_INIT;
|
||||
CONDITION_VARIABLE frame_present_cv;
|
||||
|
||||
void
|
||||
on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, winrt::Windows::Foundation::IInspectable const &);
|
||||
void on_frame_arrived(winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender, winrt::Windows::Foundation::IInspectable const &);
|
||||
|
||||
public:
|
||||
wgc_capture_t();
|
||||
~wgc_capture_t();
|
||||
|
||||
int
|
||||
init(display_base_t *display, const ::video::config_t &config);
|
||||
capture_e
|
||||
next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time);
|
||||
capture_e
|
||||
release_frame();
|
||||
int
|
||||
set_cursor_visible(bool);
|
||||
int init(display_base_t *display, const ::video::config_t &config);
|
||||
capture_e next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time);
|
||||
capture_e release_frame();
|
||||
int set_cursor_visible(bool);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -407,12 +368,9 @@ namespace platf::dxgi {
|
||||
wgc_capture_t dup;
|
||||
|
||||
public:
|
||||
int
|
||||
init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
capture_e
|
||||
release_snapshot() override;
|
||||
int init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
capture_e release_snapshot() override;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -422,11 +380,8 @@ namespace platf::dxgi {
|
||||
wgc_capture_t dup;
|
||||
|
||||
public:
|
||||
int
|
||||
init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
capture_e
|
||||
release_snapshot() override;
|
||||
int init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
capture_e release_snapshot() override;
|
||||
};
|
||||
} // namespace platf::dxgi
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
* @file src/platform/windows/display_base.cpp
|
||||
* @brief Definitions for the Windows display base code.
|
||||
*/
|
||||
// standard includes
|
||||
#include <cmath>
|
||||
#include <initguid.h>
|
||||
#include <thread>
|
||||
|
||||
// platform includes
|
||||
#include <initguid.h>
|
||||
|
||||
// lib includes
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/process/v1.hpp>
|
||||
|
||||
#include <MinHook.h>
|
||||
|
||||
// We have to include boost/process/v1.hpp before display.h due to WinSock.h,
|
||||
@@ -36,14 +39,14 @@ typedef enum _D3DKMT_GPU_PREFERENCE_QUERY_STATE: DWORD {
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
}
|
||||
|
||||
namespace platf::dxgi {
|
||||
namespace bp = boost::process;
|
||||
|
||||
/**
|
||||
* DDAPI-specific initialization goes here.
|
||||
*/
|
||||
int
|
||||
duplication_t::init(display_base_t *display, const ::video::config_t &config) {
|
||||
int duplication_t::init(display_base_t *display, const ::video::config_t &config) {
|
||||
HRESULT status;
|
||||
|
||||
// Capture format will be determined from the first call to AcquireNextFrame()
|
||||
@@ -81,8 +84,7 @@ namespace platf::dxgi {
|
||||
BOOST_LOG(warning) << "DuplicateOutput1 Failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(warning) << "IDXGIOutput5 is not supported by your OS. Capture performance may be reduced."sv;
|
||||
|
||||
dxgi::output1_t output1 {};
|
||||
@@ -124,8 +126,7 @@ namespace platf::dxgi {
|
||||
return 0;
|
||||
}
|
||||
|
||||
capture_e
|
||||
duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p) {
|
||||
capture_e duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p) {
|
||||
auto capture_status = release_frame();
|
||||
if (capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
@@ -157,8 +158,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
}
|
||||
|
||||
capture_e
|
||||
duplication_t::reset(dup_t::pointer dup_p) {
|
||||
capture_e duplication_t::reset(dup_t::pointer dup_p) {
|
||||
auto capture_status = release_frame();
|
||||
|
||||
dup.reset(dup_p);
|
||||
@@ -166,8 +166,7 @@ namespace platf::dxgi {
|
||||
return capture_status;
|
||||
}
|
||||
|
||||
capture_e
|
||||
duplication_t::release_frame() {
|
||||
capture_e duplication_t::release_frame() {
|
||||
if (!has_frame) {
|
||||
return capture_e::ok;
|
||||
}
|
||||
@@ -195,16 +194,14 @@ namespace platf::dxgi {
|
||||
release_frame();
|
||||
}
|
||||
|
||||
capture_e
|
||||
display_base_t::capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) {
|
||||
capture_e display_base_t::capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) {
|
||||
auto adjust_client_frame_rate = [&]() -> DXGI_RATIONAL {
|
||||
// Adjust capture frame interval when display refresh rate is not integral but very close to requested fps.
|
||||
if (display_refresh_rate.Denominator > 1) {
|
||||
DXGI_RATIONAL candidate = display_refresh_rate;
|
||||
if (client_frame_rate % display_refresh_rate_rounded == 0) {
|
||||
candidate.Numerator *= client_frame_rate / display_refresh_rate_rounded;
|
||||
}
|
||||
else if (display_refresh_rate_rounded % client_frame_rate == 0) {
|
||||
} else if (display_refresh_rate_rounded % client_frame_rate == 0) {
|
||||
candidate.Denominator *= display_refresh_rate_rounded / client_frame_rate;
|
||||
}
|
||||
double candidate_rate = (double) candidate.Numerator / candidate.Denominator;
|
||||
@@ -215,7 +212,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
}
|
||||
|
||||
return { (uint32_t) client_frame_rate, 1 };
|
||||
return {(uint32_t) client_frame_rate, 1};
|
||||
};
|
||||
|
||||
DXGI_RATIONAL client_frame_rate_adjusted = adjust_client_frame_rate();
|
||||
@@ -258,8 +255,7 @@ namespace platf::dxgi {
|
||||
frame_pacing_group_start = std::nullopt;
|
||||
frame_pacing_group_frames = 0;
|
||||
status = capture_e::timeout;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
timer->sleep_for(sleep_period);
|
||||
sleep_overshoot_logger.first_point(sleep_target);
|
||||
sleep_overshoot_logger.second_point_now_and_log();
|
||||
@@ -268,8 +264,7 @@ namespace platf::dxgi {
|
||||
|
||||
if (status == capture_e::ok && img_out) {
|
||||
frame_pacing_group_frames += 1;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
frame_pacing_group_start = std::nullopt;
|
||||
frame_pacing_group_frames = 0;
|
||||
}
|
||||
@@ -289,8 +284,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
|
||||
frame_pacing_group_frames = 1;
|
||||
}
|
||||
else if (status == platf::capture_e::timeout) {
|
||||
} else if (status == platf::capture_e::timeout) {
|
||||
// The D3D11 device is protected by an unfair lock that is held the entire time that
|
||||
// IDXGIOutputDuplication::AcquireNextFrame() is running. This is normally harmless,
|
||||
// however sometimes the encoding thread needs to interact with our ID3D11Device to
|
||||
@@ -348,8 +342,7 @@ namespace platf::dxgi {
|
||||
* @param output The DXGI output to capture.
|
||||
* @param enumeration_only Specifies whether this test is occurring for display enumeration.
|
||||
*/
|
||||
bool
|
||||
test_dxgi_duplication(adapter_t &adapter, output_t &output, bool enumeration_only) {
|
||||
bool test_dxgi_duplication(adapter_t &adapter, output_t &output, bool enumeration_only) {
|
||||
D3D_FEATURE_LEVEL featureLevels[] {
|
||||
D3D_FEATURE_LEVEL_11_1,
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
@@ -366,11 +359,13 @@ namespace platf::dxgi {
|
||||
D3D_DRIVER_TYPE_UNKNOWN,
|
||||
nullptr,
|
||||
D3D11_CREATE_DEVICE_FLAGS,
|
||||
featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL),
|
||||
featureLevels,
|
||||
sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL),
|
||||
D3D11_SDK_VERSION,
|
||||
&device,
|
||||
nullptr,
|
||||
nullptr);
|
||||
nullptr
|
||||
);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create D3D11 device for DD test [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return false;
|
||||
@@ -403,8 +398,7 @@ namespace platf::dxgi {
|
||||
// capture the current desktop, just bail immediately. Retrying won't help.
|
||||
if (enumeration_only && status == E_ACCESSDENIED) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
}
|
||||
@@ -418,8 +412,7 @@ namespace platf::dxgi {
|
||||
* @param gpuPreference A pointer to the location where the preference will be written.
|
||||
* @return Always STATUS_SUCCESS if valid arguments are provided.
|
||||
*/
|
||||
NTSTATUS
|
||||
__stdcall NtGdiDdDDIGetCachedHybridQueryValueHook(D3DKMT_GPU_PREFERENCE_QUERY_STATE *gpuPreference) {
|
||||
NTSTATUS __stdcall NtGdiDdDDIGetCachedHybridQueryValueHook(D3DKMT_GPU_PREFERENCE_QUERY_STATE *gpuPreference) {
|
||||
// By faking a cached GPU preference state of D3DKMT_GPU_PREFERENCE_STATE_UNSPECIFIED, this will
|
||||
// prevent DXGI from performing the normal GPU preference resolution that looks at the registry,
|
||||
// power settings, and the hybrid adapter DDI interface to pick a GPU. Instead, we will not be
|
||||
@@ -428,14 +421,12 @@ namespace platf::dxgi {
|
||||
if (gpuPreference) {
|
||||
*gpuPreference = D3DKMT_GPU_PREFERENCE_STATE_UNSPECIFIED;
|
||||
return 0; // STATUS_SUCCESS
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
display_base_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
int display_base_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
std::once_flag windows_cpp_once_flag;
|
||||
|
||||
std::call_once(windows_cpp_once_flag, []() {
|
||||
@@ -479,7 +470,7 @@ namespace platf::dxgi {
|
||||
adapter_t::pointer adapter_p;
|
||||
for (int tries = 0; tries < 2; ++tries) {
|
||||
for (int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) {
|
||||
dxgi::adapter_t adapter_tmp { adapter_p };
|
||||
dxgi::adapter_t adapter_tmp {adapter_p};
|
||||
|
||||
DXGI_ADAPTER_DESC1 adapter_desc;
|
||||
adapter_tmp->GetDesc1(&adapter_desc);
|
||||
@@ -490,7 +481,7 @@ namespace platf::dxgi {
|
||||
|
||||
dxgi::output_t::pointer output_p;
|
||||
for (int y = 0; adapter_tmp->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
dxgi::output_t output_tmp { output_p };
|
||||
dxgi::output_t output_tmp {output_p};
|
||||
|
||||
DXGI_OUTPUT_DESC desc;
|
||||
output_tmp->GetDesc(&desc);
|
||||
@@ -512,8 +503,7 @@ namespace platf::dxgi {
|
||||
display_rotation == DXGI_MODE_ROTATION_ROTATE270) {
|
||||
width_before_rotation = height;
|
||||
height_before_rotation = width;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
width_before_rotation = width;
|
||||
height_before_rotation = height;
|
||||
}
|
||||
@@ -570,11 +560,13 @@ namespace platf::dxgi {
|
||||
D3D_DRIVER_TYPE_UNKNOWN,
|
||||
nullptr,
|
||||
D3D11_CREATE_DEVICE_FLAGS,
|
||||
featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL),
|
||||
featureLevels,
|
||||
sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL),
|
||||
D3D11_SDK_VERSION,
|
||||
&device,
|
||||
&feature_level,
|
||||
&device_ctx);
|
||||
&device_ctx
|
||||
);
|
||||
|
||||
adapter_p->Release();
|
||||
|
||||
@@ -632,7 +624,7 @@ namespace platf::dxgi {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3DKMT_OPENADAPTERFROMLUID d3dkmt_adapter = { adapter };
|
||||
D3DKMT_OPENADAPTERFROMLUID d3dkmt_adapter = {adapter};
|
||||
if (FAILED(d3dkmt_open_adapter(&d3dkmt_adapter))) {
|
||||
BOOST_LOG(error) << "D3DKMTOpenAdapterFromLuid() failed while trying to determine GPU HAGS status";
|
||||
return false;
|
||||
@@ -649,13 +641,12 @@ namespace platf::dxgi {
|
||||
|
||||
if (SUCCEEDED(d3dkmt_query_adapter_info(&d3dkmt_adapter_info))) {
|
||||
result = d3dkmt_adapter_caps.HwSchEnabled;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(warning) << "D3DKMTQueryAdapterInfo() failed while trying to determine GPU HAGS status";
|
||||
result = false;
|
||||
}
|
||||
|
||||
D3DKMT_CLOSEADAPTER d3dkmt_close_adapter_wrap = { d3dkmt_adapter.hAdapter };
|
||||
D3DKMT_CLOSEADAPTER d3dkmt_close_adapter_wrap = {d3dkmt_adapter.hAdapter};
|
||||
if (FAILED(d3dkmt_close_adapter(&d3dkmt_close_adapter_wrap))) {
|
||||
BOOST_LOG(error) << "D3DKMTCloseAdapter() failed while trying to determine GPU HAGS status";
|
||||
}
|
||||
@@ -671,15 +662,16 @@ namespace platf::dxgi {
|
||||
// As of 2023.07, NVIDIA driver has unfixed bug(s) where "realtime" can cause unrecoverable encoding freeze or outright driver crash
|
||||
// This issue happens more frequently with HAGS, in DX12 games or when VRAM is filled close to max capacity
|
||||
// Track OBS to see if they find better workaround or NVIDIA fixes it on their end, they seem to be in communication
|
||||
if (hags_enabled && !config::video.nv_realtime_hags) priority = D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH;
|
||||
if (hags_enabled && !config::video.nv_realtime_hags) {
|
||||
priority = D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH;
|
||||
}
|
||||
}
|
||||
BOOST_LOG(info) << "Active GPU has HAGS " << (hags_enabled ? "enabled" : "disabled");
|
||||
BOOST_LOG(info) << "Using " << (priority == D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH ? "high" : "realtime") << " GPU priority";
|
||||
if (FAILED(d3dkmt_set_process_priority(GetCurrentProcess(), priority))) {
|
||||
BOOST_LOG(warning) << "Failed to adjust GPU priority. Please run application as administrator for optimal performance.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(error) << "Couldn't load D3DKMTSetProcessSchedulingPriorityClass function from gdi32.dll to adjust GPU priority";
|
||||
}
|
||||
}
|
||||
@@ -740,8 +732,7 @@ namespace platf::dxgi {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
display_base_t::is_hdr() {
|
||||
bool display_base_t::is_hdr() {
|
||||
dxgi::output6_t output6 {};
|
||||
|
||||
auto status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6);
|
||||
@@ -756,8 +747,7 @@ namespace platf::dxgi {
|
||||
return desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
|
||||
}
|
||||
|
||||
bool
|
||||
display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
||||
bool display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
||||
dxgi::output6_t output6 {};
|
||||
|
||||
std::memset(&metadata, 0, sizeof(metadata));
|
||||
@@ -928,20 +918,31 @@ namespace platf::dxgi {
|
||||
"DXGI_FORMAT_A8P8",
|
||||
"DXGI_FORMAT_B4G4R4A4_UNORM",
|
||||
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
"DXGI_FORMAT_P208",
|
||||
"DXGI_FORMAT_V208",
|
||||
"DXGI_FORMAT_V408"
|
||||
};
|
||||
|
||||
const char *
|
||||
display_base_t::dxgi_format_to_string(DXGI_FORMAT format) {
|
||||
const char *display_base_t::dxgi_format_to_string(DXGI_FORMAT format) {
|
||||
return format_str[format];
|
||||
}
|
||||
|
||||
const char *
|
||||
display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) {
|
||||
const char *display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) {
|
||||
const char *type_str[] = {
|
||||
"DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709",
|
||||
"DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709",
|
||||
@@ -972,8 +973,7 @@ namespace platf::dxgi {
|
||||
|
||||
if (type < ARRAYSIZE(type_str)) {
|
||||
return type_str[type];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
@@ -985,8 +985,7 @@ namespace platf {
|
||||
* Pick a display adapter and capture method.
|
||||
* @param hwdevice_type enables possible use of hardware encoder
|
||||
*/
|
||||
std::shared_ptr<display_t>
|
||||
display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
std::shared_ptr<display_t> display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if (config::video.capture == "ddx" || config::video.capture.empty()) {
|
||||
if (hwdevice_type == mem_type_e::dxgi) {
|
||||
auto disp = std::make_shared<dxgi::display_ddup_vram_t>();
|
||||
@@ -994,8 +993,7 @@ namespace platf {
|
||||
if (!disp->init(config, display_name)) {
|
||||
return disp;
|
||||
}
|
||||
}
|
||||
else if (hwdevice_type == mem_type_e::system) {
|
||||
} else if (hwdevice_type == mem_type_e::system) {
|
||||
auto disp = std::make_shared<dxgi::display_ddup_ram_t>();
|
||||
|
||||
if (!disp->init(config, display_name)) {
|
||||
@@ -1011,8 +1009,7 @@ namespace platf {
|
||||
if (!disp->init(config, display_name)) {
|
||||
return disp;
|
||||
}
|
||||
}
|
||||
else if (hwdevice_type == mem_type_e::system) {
|
||||
} else if (hwdevice_type == mem_type_e::system) {
|
||||
auto disp = std::make_shared<dxgi::display_wgc_ram_t>();
|
||||
|
||||
if (!disp->init(config, display_name)) {
|
||||
@@ -1025,8 +1022,7 @@ namespace platf {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
display_names(mem_type_e) {
|
||||
std::vector<std::string> display_names(mem_type_e) {
|
||||
std::vector<std::string> display_names;
|
||||
|
||||
HRESULT status;
|
||||
@@ -1066,7 +1062,7 @@ namespace platf {
|
||||
|
||||
dxgi::output_t::pointer output_p {};
|
||||
for (int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
dxgi::output_t output { output_p };
|
||||
dxgi::output_t output {output_p};
|
||||
|
||||
DXGI_OUTPUT_DESC desc;
|
||||
output->GetDesc(&desc);
|
||||
@@ -1096,8 +1092,7 @@ namespace platf {
|
||||
* @brief Returns if GPUs/drivers have changed since the last call to this function.
|
||||
* @return `true` if a change has occurred or if it is unknown whether a change occurred.
|
||||
*/
|
||||
bool
|
||||
needs_encoder_reenumeration() {
|
||||
bool needs_encoder_reenumeration() {
|
||||
// Serialize access to the static DXGI factory
|
||||
static std::mutex reenumeration_state_lock;
|
||||
auto lg = std::lock_guard(reenumeration_state_lock);
|
||||
@@ -1117,8 +1112,7 @@ namespace platf {
|
||||
// can deal with any initialization races that may occur when the system is booting.
|
||||
BOOST_LOG(info) << "Encoder reenumeration is required"sv;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// The DXGI factory from last time is still current, so no encoder changes have occurred.
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* @file src/platform/windows/display_ram.cpp
|
||||
* @brief Definitions for handling ram.
|
||||
*/
|
||||
// local includes
|
||||
#include "display.h"
|
||||
|
||||
#include "misc.h"
|
||||
#include "src/logging.h"
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
||||
void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
||||
int height = cursor.shape_info.Height / 2;
|
||||
int width = cursor.shape_info.Width;
|
||||
int pitch = cursor.shape_info.Pitch;
|
||||
@@ -82,8 +81,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apply_color_alpha(int *img_pixel_p, int cursor_pixel) {
|
||||
void apply_color_alpha(int *img_pixel_p, int cursor_pixel) {
|
||||
auto colors_out = (std::uint8_t *) &cursor_pixel;
|
||||
auto colors_in = (std::uint8_t *) img_pixel_p;
|
||||
|
||||
@@ -91,28 +89,24 @@ namespace platf::dxgi {
|
||||
auto alpha = colors_out[3];
|
||||
if (alpha == 255) {
|
||||
*img_pixel_p = cursor_pixel;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apply_color_masked(int *img_pixel_p, int cursor_pixel) {
|
||||
void apply_color_masked(int *img_pixel_p, int cursor_pixel) {
|
||||
// TODO: When use of IDXGIOutput5 is implemented, support different color formats
|
||||
auto alpha = ((std::uint8_t *) &cursor_pixel)[3];
|
||||
if (alpha == 0xFF) {
|
||||
*img_pixel_p ^= cursor_pixel;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
*img_pixel_p = cursor_pixel;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) {
|
||||
void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) {
|
||||
int height = cursor.shape_info.Height;
|
||||
int width = cursor.shape_info.Width;
|
||||
int pitch = cursor.shape_info.Pitch;
|
||||
@@ -150,8 +144,7 @@ namespace platf::dxgi {
|
||||
std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) {
|
||||
if (masked) {
|
||||
apply_color_masked(img_pixel_p, cursor_pixel);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
apply_color_alpha(img_pixel_p, cursor_pixel);
|
||||
}
|
||||
++img_pixel_p;
|
||||
@@ -159,8 +152,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
blend_cursor(const cursor_t &cursor, img_t &img) {
|
||||
void blend_cursor(const cursor_t &cursor, img_t &img) {
|
||||
switch (cursor.shape_info.Type) {
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
|
||||
blend_cursor_color(cursor, img, false);
|
||||
@@ -176,14 +168,13 @@ namespace platf::dxgi {
|
||||
}
|
||||
}
|
||||
|
||||
capture_e
|
||||
display_ddup_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) {
|
||||
capture_e display_ddup_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) {
|
||||
HRESULT status;
|
||||
DXGI_OUTDUPL_FRAME_INFO frame_info;
|
||||
|
||||
resource_t::pointer res_p {};
|
||||
auto capture_status = dup.next_frame(frame_info, timeout, &res_p);
|
||||
resource_t res { res_p };
|
||||
resource_t res {res_p};
|
||||
|
||||
if (capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
@@ -290,8 +281,7 @@ namespace platf::dxgi {
|
||||
if (dummy_img(img)) {
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Map the staging texture for CPU access (making it inaccessible for the GPU)
|
||||
status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info);
|
||||
if (FAILED(status)) {
|
||||
@@ -325,13 +315,11 @@ namespace platf::dxgi {
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e
|
||||
display_ddup_ram_t::release_snapshot() {
|
||||
capture_e display_ddup_ram_t::release_snapshot() {
|
||||
return dup.release_frame();
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::img_t>
|
||||
display_ram_t::alloc_img() {
|
||||
std::shared_ptr<platf::img_t> display_ram_t::alloc_img() {
|
||||
auto img = std::make_shared<img_t>();
|
||||
|
||||
// Initialize fields that are format-independent
|
||||
@@ -341,8 +329,7 @@ namespace platf::dxgi {
|
||||
return img;
|
||||
}
|
||||
|
||||
int
|
||||
display_ram_t::complete_img(platf::img_t *img, bool dummy) {
|
||||
int display_ram_t::complete_img(platf::img_t *img, bool dummy) {
|
||||
// If this is not a dummy image, we must know the format by now
|
||||
if (!dummy && capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(error) << "display_ram_t::complete_img() called with unknown capture format!";
|
||||
@@ -373,8 +360,7 @@ namespace platf::dxgi {
|
||||
/**
|
||||
* @memberof platf::dxgi::display_ram_t
|
||||
*/
|
||||
int
|
||||
display_ram_t::dummy_img(platf::img_t *img) {
|
||||
int display_ram_t::dummy_img(platf::img_t *img) {
|
||||
if (complete_img(img, true)) {
|
||||
return -1;
|
||||
}
|
||||
@@ -383,13 +369,11 @@ namespace platf::dxgi {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<DXGI_FORMAT>
|
||||
display_ram_t::get_supported_capture_formats() {
|
||||
return { DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM };
|
||||
std::vector<DXGI_FORMAT> display_ram_t::get_supported_capture_formats() {
|
||||
return {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM};
|
||||
}
|
||||
|
||||
int
|
||||
display_ddup_ram_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
int display_ddup_ram_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
if (display_base_t::init(config, display_name) || dup.init(this, config)) {
|
||||
return -1;
|
||||
}
|
||||
@@ -397,8 +381,7 @@ namespace platf::dxgi {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<avcodec_encode_device_t>
|
||||
display_ram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) {
|
||||
std::unique_ptr<avcodec_encode_device_t> display_ram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) {
|
||||
return std::make_unique<avcodec_encode_device_t>();
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,336 +1,335 @@
|
||||
/**
|
||||
* @file src/platform/windows/display_wgc.cpp
|
||||
* @brief Definitions for WinRT Windows.Graphics.Capture API
|
||||
*/
|
||||
#include <dxgi1_2.h>
|
||||
|
||||
#include "display.h"
|
||||
|
||||
#include "misc.h"
|
||||
#include "src/logging.h"
|
||||
|
||||
// Gross hack to work around MINGW-packages#22160
|
||||
#define ____FIReference_1_boolean_INTERFACE_DEFINED__
|
||||
|
||||
#include <windows.graphics.capture.interop.h>
|
||||
#include <winrt/windows.foundation.h>
|
||||
#include <winrt/windows.foundation.metadata.h>
|
||||
#include <winrt/windows.graphics.directx.direct3d11.h>
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
}
|
||||
|
||||
namespace winrt {
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Metadata;
|
||||
using namespace Windows::Graphics::Capture;
|
||||
using namespace Windows::Graphics::DirectX::Direct3D11;
|
||||
|
||||
extern "C" {
|
||||
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Windows structures sometimes have compile-time GUIDs. GCC supports this, but in a roundabout way.
|
||||
* If WINRT_IMPL_HAS_DECLSPEC_UUID is true, then the compiler supports adding this attribute to a struct. For example, Visual Studio.
|
||||
* If not, then MinGW GCC has a workaround to assign a GUID to a structure.
|
||||
*/
|
||||
struct
|
||||
#if WINRT_IMPL_HAS_DECLSPEC_UUID
|
||||
__declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
|
||||
#endif
|
||||
IDirect3DDxgiInterfaceAccess: ::IUnknown {
|
||||
virtual HRESULT __stdcall GetInterface(REFIID id, void **object) = 0;
|
||||
};
|
||||
} // namespace winrt
|
||||
#if !WINRT_IMPL_HAS_DECLSPEC_UUID
|
||||
static constexpr GUID GUID__IDirect3DDxgiInterfaceAccess = {
|
||||
0xA9B3D012, 0x3DF2, 0x4EE3, { 0xB8, 0xD1, 0x86, 0x95, 0xF4, 0x57, 0xD3, 0xC1 }
|
||||
// compare with __declspec(uuid(...)) for the struct above.
|
||||
};
|
||||
template <>
|
||||
constexpr auto
|
||||
__mingw_uuidof<winrt::IDirect3DDxgiInterfaceAccess>() -> GUID const & {
|
||||
return GUID__IDirect3DDxgiInterfaceAccess;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace platf::dxgi {
|
||||
wgc_capture_t::wgc_capture_t() {
|
||||
InitializeConditionVariable(&frame_present_cv);
|
||||
}
|
||||
|
||||
wgc_capture_t::~wgc_capture_t() {
|
||||
if (capture_session)
|
||||
capture_session.Close();
|
||||
if (frame_pool)
|
||||
frame_pool.Close();
|
||||
item = nullptr;
|
||||
capture_session = nullptr;
|
||||
frame_pool = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the Windows.Graphics.Capture backend.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
wgc_capture_t::init(display_base_t *display, const ::video::config_t &config) {
|
||||
HRESULT status;
|
||||
dxgi::dxgi_t dxgi;
|
||||
winrt::com_ptr<::IInspectable> d3d_comhandle;
|
||||
try {
|
||||
if (!winrt::GraphicsCaptureSession::IsSupported()) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows!"sv;
|
||||
return -1;
|
||||
}
|
||||
if (FAILED(status = display->device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi))) {
|
||||
BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
if (FAILED(status = winrt::CreateDirect3D11DeviceFromDXGIDevice(*&dxgi, d3d_comhandle.put()))) {
|
||||
BOOST_LOG(error) << "Failed to query WinRT DirectX interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to acquire device: [0x"sv << util::hex(e.code()).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
DXGI_OUTPUT_DESC output_desc;
|
||||
uwp_device = d3d_comhandle.as<winrt::IDirect3DDevice>();
|
||||
display->output->GetDesc(&output_desc);
|
||||
|
||||
auto monitor_factory = winrt::get_activation_factory<winrt::GraphicsCaptureItem, IGraphicsCaptureItemInterop>();
|
||||
if (monitor_factory == nullptr ||
|
||||
FAILED(status = monitor_factory->CreateForMonitor(output_desc.Monitor, winrt::guid_of<winrt::IGraphicsCaptureItem>(), winrt::put_abi(item)))) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to acquire display: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (config.dynamicRange)
|
||||
display->capture_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||
else
|
||||
display->capture_format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
try {
|
||||
frame_pool = winrt::Direct3D11CaptureFramePool::CreateFreeThreaded(uwp_device, static_cast<winrt::Windows::Graphics::DirectX::DirectXPixelFormat>(display->capture_format), 2, item.Size());
|
||||
capture_session = frame_pool.CreateCaptureSession(item);
|
||||
frame_pool.FrameArrived({ this, &wgc_capture_t::on_frame_arrived });
|
||||
}
|
||||
catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to create capture session: [0x"sv << util::hex(e.code()).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
if (winrt::ApiInformation::IsPropertyPresent(L"Windows.Graphics.Capture.GraphicsCaptureSession", L"IsBorderRequired")) {
|
||||
capture_session.IsBorderRequired(false);
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Can't disable colored border around capture area on this version of Windows";
|
||||
}
|
||||
}
|
||||
catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(warning) << "Screen capture may not be fully supported on this device for this release of Windows: failed to disable border around capture area: [0x"sv << util::hex(e.code()).to_string_view() << ']';
|
||||
}
|
||||
try {
|
||||
capture_session.StartCapture();
|
||||
}
|
||||
catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to start capture: [0x"sv << util::hex(e.code()).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function runs in a separate thread spawned by the frame pool and is a producer of frames.
|
||||
* To maintain parity with the original display interface, this frame will be consumed by the capture thread.
|
||||
* Acquire a read-write lock, make the produced frame available to the capture thread, then wake the capture thread.
|
||||
*/
|
||||
void
|
||||
wgc_capture_t::on_frame_arrived(winrt::Direct3D11CaptureFramePool const &sender, winrt::IInspectable const &) {
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame { nullptr };
|
||||
try {
|
||||
frame = sender.TryGetNextFrame();
|
||||
}
|
||||
catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(warning) << "Failed to capture frame: "sv << e.code();
|
||||
return;
|
||||
}
|
||||
if (frame != nullptr) {
|
||||
AcquireSRWLockExclusive(&frame_lock);
|
||||
if (produced_frame)
|
||||
produced_frame.Close();
|
||||
|
||||
produced_frame = frame;
|
||||
ReleaseSRWLockExclusive(&frame_lock);
|
||||
WakeConditionVariable(&frame_present_cv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next frame from the producer thread.
|
||||
* If not available, the capture thread blocks until one is, or the wait times out.
|
||||
* @param timeout how long to wait for the next frame
|
||||
* @param out a texture containing the frame just captured
|
||||
* @param out_time the timestamp of the frame just captured
|
||||
*/
|
||||
capture_e
|
||||
wgc_capture_t::next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time) {
|
||||
// this CONSUMER runs in the capture thread
|
||||
release_frame();
|
||||
|
||||
AcquireSRWLockExclusive(&frame_lock);
|
||||
if (produced_frame == nullptr && SleepConditionVariableSRW(&frame_present_cv, &frame_lock, timeout.count(), 0) == 0) {
|
||||
ReleaseSRWLockExclusive(&frame_lock);
|
||||
if (GetLastError() == ERROR_TIMEOUT)
|
||||
return capture_e::timeout;
|
||||
else
|
||||
return capture_e::error;
|
||||
}
|
||||
if (produced_frame) {
|
||||
consumed_frame = produced_frame;
|
||||
produced_frame = nullptr;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&frame_lock);
|
||||
if (consumed_frame == nullptr) // spurious wakeup
|
||||
return capture_e::timeout;
|
||||
|
||||
auto capture_access = consumed_frame.Surface().as<winrt::IDirect3DDxgiInterfaceAccess>();
|
||||
if (capture_access == nullptr)
|
||||
return capture_e::error;
|
||||
capture_access->GetInterface(IID_ID3D11Texture2D, (void **) out);
|
||||
out_time = consumed_frame.SystemRelativeTime().count(); // raw ticks from query performance counter
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e
|
||||
wgc_capture_t::release_frame() {
|
||||
if (consumed_frame != nullptr) {
|
||||
consumed_frame.Close();
|
||||
consumed_frame = nullptr;
|
||||
}
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
int
|
||||
wgc_capture_t::set_cursor_visible(bool x) {
|
||||
try {
|
||||
if (capture_session.IsCursorCaptureEnabled() != x)
|
||||
capture_session.IsCursorCaptureEnabled(x);
|
||||
return 0;
|
||||
}
|
||||
catch (winrt::hresult_error &) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
display_wgc_ram_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
if (display_base_t::init(config, display_name) || dup.init(this, config))
|
||||
return -1;
|
||||
|
||||
texture.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next frame from the Windows.Graphics.Capture API and copy it into a new snapshot texture.
|
||||
* @param pull_free_image_cb call this to get a new free image from the video subsystem.
|
||||
* @param img_out the captured frame is returned here
|
||||
* @param timeout how long to wait for the next frame
|
||||
* @param cursor_visible whether to capture the cursor
|
||||
*/
|
||||
capture_e
|
||||
display_wgc_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) {
|
||||
HRESULT status;
|
||||
texture2d_t src;
|
||||
uint64_t frame_qpc;
|
||||
dup.set_cursor_visible(cursor_visible);
|
||||
auto capture_status = dup.next_frame(timeout, &src, frame_qpc);
|
||||
if (capture_status != capture_e::ok)
|
||||
return capture_status;
|
||||
|
||||
auto frame_timestamp = std::chrono::steady_clock::now() - qpc_time_difference(qpc_counter(), frame_qpc);
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
src->GetDesc(&desc);
|
||||
|
||||
// Create the staging texture if it doesn't exist. It should match the source in size and format.
|
||||
if (texture == nullptr) {
|
||||
capture_format = desc.Format;
|
||||
BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_STAGING;
|
||||
t.Format = capture_format;
|
||||
t.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &texture);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
|
||||
// It's possible for our display enumeration to race with mode changes and result in
|
||||
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
|
||||
if (desc.Width != width || desc.Height != height) {
|
||||
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
// It's also possible for the capture format to change on the fly. If that happens,
|
||||
// reinitialize capture to try format detection again and create new images.
|
||||
if (capture_format != desc.Format) {
|
||||
BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
// Copy from GPU to CPU
|
||||
device_ctx->CopyResource(texture.get(), src.get());
|
||||
|
||||
if (!pull_free_image_cb(img_out)) {
|
||||
return capture_e::interrupted;
|
||||
}
|
||||
auto img = (img_t *) img_out.get();
|
||||
|
||||
// Map the staging texture for CPU access (making it inaccessible for the GPU)
|
||||
if (FAILED(status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info))) {
|
||||
BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
// Now that we know the capture format, we can finish creating the image
|
||||
if (complete_img(img, false)) {
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
std::copy_n((std::uint8_t *) img_info.pData, height * img_info.RowPitch, (std::uint8_t *) img->data);
|
||||
|
||||
// Unmap the staging texture to allow GPU access again
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
|
||||
if (img) {
|
||||
img->frame_timestamp = frame_timestamp;
|
||||
}
|
||||
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e
|
||||
display_wgc_ram_t::release_snapshot() {
|
||||
return dup.release_frame();
|
||||
}
|
||||
} // namespace platf::dxgi
|
||||
/**
|
||||
* @file src/platform/windows/display_wgc.cpp
|
||||
* @brief Definitions for WinRT Windows.Graphics.Capture API
|
||||
*/
|
||||
// platform includes
|
||||
#include <dxgi1_2.h>
|
||||
|
||||
// local includes
|
||||
#include "display.h"
|
||||
#include "misc.h"
|
||||
#include "src/logging.h"
|
||||
|
||||
// Gross hack to work around MINGW-packages#22160
|
||||
#define ____FIReference_1_boolean_INTERFACE_DEFINED__
|
||||
|
||||
#include <windows.graphics.capture.interop.h>
|
||||
#include <winrt/windows.foundation.h>
|
||||
#include <winrt/windows.foundation.metadata.h>
|
||||
#include <winrt/windows.graphics.directx.direct3d11.h>
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
}
|
||||
|
||||
namespace winrt {
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Metadata;
|
||||
using namespace Windows::Graphics::Capture;
|
||||
using namespace Windows::Graphics::DirectX::Direct3D11;
|
||||
|
||||
extern "C" {
|
||||
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Windows structures sometimes have compile-time GUIDs. GCC supports this, but in a roundabout way.
|
||||
* If WINRT_IMPL_HAS_DECLSPEC_UUID is true, then the compiler supports adding this attribute to a struct. For example, Visual Studio.
|
||||
* If not, then MinGW GCC has a workaround to assign a GUID to a structure.
|
||||
*/
|
||||
struct
|
||||
#if WINRT_IMPL_HAS_DECLSPEC_UUID
|
||||
__declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
|
||||
#endif
|
||||
IDirect3DDxgiInterfaceAccess: ::IUnknown {
|
||||
virtual HRESULT __stdcall GetInterface(REFIID id, void **object) = 0;
|
||||
};
|
||||
} // namespace winrt
|
||||
#if !WINRT_IMPL_HAS_DECLSPEC_UUID
|
||||
static constexpr GUID GUID__IDirect3DDxgiInterfaceAccess = {
|
||||
0xA9B3D012,
|
||||
0x3DF2,
|
||||
0x4EE3,
|
||||
{0xB8, 0xD1, 0x86, 0x95, 0xF4, 0x57, 0xD3, 0xC1}
|
||||
// compare with __declspec(uuid(...)) for the struct above.
|
||||
};
|
||||
|
||||
template<>
|
||||
constexpr auto __mingw_uuidof<winrt::IDirect3DDxgiInterfaceAccess>() -> GUID const & {
|
||||
return GUID__IDirect3DDxgiInterfaceAccess;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace platf::dxgi {
|
||||
wgc_capture_t::wgc_capture_t() {
|
||||
InitializeConditionVariable(&frame_present_cv);
|
||||
}
|
||||
|
||||
wgc_capture_t::~wgc_capture_t() {
|
||||
if (capture_session) {
|
||||
capture_session.Close();
|
||||
}
|
||||
if (frame_pool) {
|
||||
frame_pool.Close();
|
||||
}
|
||||
item = nullptr;
|
||||
capture_session = nullptr;
|
||||
frame_pool = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the Windows.Graphics.Capture backend.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int wgc_capture_t::init(display_base_t *display, const ::video::config_t &config) {
|
||||
HRESULT status;
|
||||
dxgi::dxgi_t dxgi;
|
||||
winrt::com_ptr<::IInspectable> d3d_comhandle;
|
||||
try {
|
||||
if (!winrt::GraphicsCaptureSession::IsSupported()) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows!"sv;
|
||||
return -1;
|
||||
}
|
||||
if (FAILED(status = display->device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi))) {
|
||||
BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
if (FAILED(status = winrt::CreateDirect3D11DeviceFromDXGIDevice(*&dxgi, d3d_comhandle.put()))) {
|
||||
BOOST_LOG(error) << "Failed to query WinRT DirectX interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
} catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to acquire device: [0x"sv << util::hex(e.code()).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
DXGI_OUTPUT_DESC output_desc;
|
||||
uwp_device = d3d_comhandle.as<winrt::IDirect3DDevice>();
|
||||
display->output->GetDesc(&output_desc);
|
||||
|
||||
auto monitor_factory = winrt::get_activation_factory<winrt::GraphicsCaptureItem, IGraphicsCaptureItemInterop>();
|
||||
if (monitor_factory == nullptr ||
|
||||
FAILED(status = monitor_factory->CreateForMonitor(output_desc.Monitor, winrt::guid_of<winrt::IGraphicsCaptureItem>(), winrt::put_abi(item)))) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to acquire display: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (config.dynamicRange) {
|
||||
display->capture_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||
} else {
|
||||
display->capture_format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
}
|
||||
|
||||
try {
|
||||
frame_pool = winrt::Direct3D11CaptureFramePool::CreateFreeThreaded(uwp_device, static_cast<winrt::Windows::Graphics::DirectX::DirectXPixelFormat>(display->capture_format), 2, item.Size());
|
||||
capture_session = frame_pool.CreateCaptureSession(item);
|
||||
frame_pool.FrameArrived({this, &wgc_capture_t::on_frame_arrived});
|
||||
} catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to create capture session: [0x"sv << util::hex(e.code()).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
if (winrt::ApiInformation::IsPropertyPresent(L"Windows.Graphics.Capture.GraphicsCaptureSession", L"IsBorderRequired")) {
|
||||
capture_session.IsBorderRequired(false);
|
||||
} else {
|
||||
BOOST_LOG(warning) << "Can't disable colored border around capture area on this version of Windows";
|
||||
}
|
||||
} catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(warning) << "Screen capture may not be fully supported on this device for this release of Windows: failed to disable border around capture area: [0x"sv << util::hex(e.code()).to_string_view() << ']';
|
||||
}
|
||||
try {
|
||||
capture_session.StartCapture();
|
||||
} catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(error) << "Screen capture is not supported on this device for this release of Windows: failed to start capture: [0x"sv << util::hex(e.code()).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function runs in a separate thread spawned by the frame pool and is a producer of frames.
|
||||
* To maintain parity with the original display interface, this frame will be consumed by the capture thread.
|
||||
* Acquire a read-write lock, make the produced frame available to the capture thread, then wake the capture thread.
|
||||
*/
|
||||
void wgc_capture_t::on_frame_arrived(winrt::Direct3D11CaptureFramePool const &sender, winrt::IInspectable const &) {
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame {nullptr};
|
||||
try {
|
||||
frame = sender.TryGetNextFrame();
|
||||
} catch (winrt::hresult_error &e) {
|
||||
BOOST_LOG(warning) << "Failed to capture frame: "sv << e.code();
|
||||
return;
|
||||
}
|
||||
if (frame != nullptr) {
|
||||
AcquireSRWLockExclusive(&frame_lock);
|
||||
if (produced_frame) {
|
||||
produced_frame.Close();
|
||||
}
|
||||
|
||||
produced_frame = frame;
|
||||
ReleaseSRWLockExclusive(&frame_lock);
|
||||
WakeConditionVariable(&frame_present_cv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next frame from the producer thread.
|
||||
* If not available, the capture thread blocks until one is, or the wait times out.
|
||||
* @param timeout how long to wait for the next frame
|
||||
* @param out a texture containing the frame just captured
|
||||
* @param out_time the timestamp of the frame just captured
|
||||
*/
|
||||
capture_e wgc_capture_t::next_frame(std::chrono::milliseconds timeout, ID3D11Texture2D **out, uint64_t &out_time) {
|
||||
// this CONSUMER runs in the capture thread
|
||||
release_frame();
|
||||
|
||||
AcquireSRWLockExclusive(&frame_lock);
|
||||
if (produced_frame == nullptr && SleepConditionVariableSRW(&frame_present_cv, &frame_lock, timeout.count(), 0) == 0) {
|
||||
ReleaseSRWLockExclusive(&frame_lock);
|
||||
if (GetLastError() == ERROR_TIMEOUT) {
|
||||
return capture_e::timeout;
|
||||
} else {
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
if (produced_frame) {
|
||||
consumed_frame = produced_frame;
|
||||
produced_frame = nullptr;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&frame_lock);
|
||||
if (consumed_frame == nullptr) { // spurious wakeup
|
||||
return capture_e::timeout;
|
||||
}
|
||||
|
||||
auto capture_access = consumed_frame.Surface().as<winrt::IDirect3DDxgiInterfaceAccess>();
|
||||
if (capture_access == nullptr) {
|
||||
return capture_e::error;
|
||||
}
|
||||
capture_access->GetInterface(IID_ID3D11Texture2D, (void **) out);
|
||||
out_time = consumed_frame.SystemRelativeTime().count(); // raw ticks from query performance counter
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e wgc_capture_t::release_frame() {
|
||||
if (consumed_frame != nullptr) {
|
||||
consumed_frame.Close();
|
||||
consumed_frame = nullptr;
|
||||
}
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
int wgc_capture_t::set_cursor_visible(bool x) {
|
||||
try {
|
||||
if (capture_session.IsCursorCaptureEnabled() != x) {
|
||||
capture_session.IsCursorCaptureEnabled(x);
|
||||
}
|
||||
return 0;
|
||||
} catch (winrt::hresult_error &) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int display_wgc_ram_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
if (display_base_t::init(config, display_name) || dup.init(this, config)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
texture.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next frame from the Windows.Graphics.Capture API and copy it into a new snapshot texture.
|
||||
* @param pull_free_image_cb call this to get a new free image from the video subsystem.
|
||||
* @param img_out the captured frame is returned here
|
||||
* @param timeout how long to wait for the next frame
|
||||
* @param cursor_visible whether to capture the cursor
|
||||
*/
|
||||
capture_e display_wgc_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor_visible) {
|
||||
HRESULT status;
|
||||
texture2d_t src;
|
||||
uint64_t frame_qpc;
|
||||
dup.set_cursor_visible(cursor_visible);
|
||||
auto capture_status = dup.next_frame(timeout, &src, frame_qpc);
|
||||
if (capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
}
|
||||
|
||||
auto frame_timestamp = std::chrono::steady_clock::now() - qpc_time_difference(qpc_counter(), frame_qpc);
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
src->GetDesc(&desc);
|
||||
|
||||
// Create the staging texture if it doesn't exist. It should match the source in size and format.
|
||||
if (texture == nullptr) {
|
||||
capture_format = desc.Format;
|
||||
BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_STAGING;
|
||||
t.Format = capture_format;
|
||||
t.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &texture);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
|
||||
// It's possible for our display enumeration to race with mode changes and result in
|
||||
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
|
||||
if (desc.Width != width || desc.Height != height) {
|
||||
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
// It's also possible for the capture format to change on the fly. If that happens,
|
||||
// reinitialize capture to try format detection again and create new images.
|
||||
if (capture_format != desc.Format) {
|
||||
BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
// Copy from GPU to CPU
|
||||
device_ctx->CopyResource(texture.get(), src.get());
|
||||
|
||||
if (!pull_free_image_cb(img_out)) {
|
||||
return capture_e::interrupted;
|
||||
}
|
||||
auto img = (img_t *) img_out.get();
|
||||
|
||||
// Map the staging texture for CPU access (making it inaccessible for the GPU)
|
||||
if (FAILED(status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info))) {
|
||||
BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
// Now that we know the capture format, we can finish creating the image
|
||||
if (complete_img(img, false)) {
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
std::copy_n((std::uint8_t *) img_info.pData, height * img_info.RowPitch, (std::uint8_t *) img->data);
|
||||
|
||||
// Unmap the staging texture to allow GPU access again
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
|
||||
if (img) {
|
||||
img->frame_timestamp = frame_timestamp;
|
||||
}
|
||||
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e display_wgc_ram_t::release_snapshot() {
|
||||
return dup.release_frame();
|
||||
}
|
||||
} // namespace platf::dxgi
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
* @file src/platform/windows/misc.cpp
|
||||
* @brief Miscellaneous definitions for Windows.
|
||||
*/
|
||||
// standard includes
|
||||
#include <csignal>
|
||||
#include <filesystem>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
// lib includes
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/process/v1.hpp>
|
||||
@@ -34,16 +37,14 @@
|
||||
#define NTDDI_VERSION NTDDI_WIN10
|
||||
#include <Shlwapi.h>
|
||||
|
||||
// local includes
|
||||
#include "misc.h"
|
||||
|
||||
#include "nvprefs/nvprefs_interface.h"
|
||||
#include "src/entry_handler.h"
|
||||
#include "src/globals.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
#include <iterator>
|
||||
|
||||
#include "nvprefs/nvprefs_interface.h"
|
||||
|
||||
// UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK
|
||||
#ifndef UDP_SEND_MSG_SIZE
|
||||
@@ -63,16 +64,14 @@
|
||||
|
||||
#include <winternl.h>
|
||||
extern "C" {
|
||||
NTSTATUS NTAPI
|
||||
NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
|
||||
NTSTATUS NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::atomic<bool> used_nt_set_timer_resolution = false;
|
||||
|
||||
bool
|
||||
nt_set_timer_resolution_max() {
|
||||
bool nt_set_timer_resolution_max() {
|
||||
ULONG minimum, maximum, current;
|
||||
if (!NT_SUCCESS(NtQueryTimerResolution(&minimum, &maximum, ¤t)) ||
|
||||
!NT_SUCCESS(NtSetTimerResolution(maximum, TRUE, ¤t))) {
|
||||
@@ -81,8 +80,7 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nt_set_timer_resolution_min() {
|
||||
bool nt_set_timer_resolution_min() {
|
||||
ULONG minimum, maximum, current;
|
||||
if (!NT_SUCCESS(NtQueryTimerResolution(&minimum, &maximum, ¤t)) ||
|
||||
!NT_SUCCESS(NtSetTimerResolution(minimum, TRUE, ¤t))) {
|
||||
@@ -96,6 +94,7 @@ namespace {
|
||||
namespace bp = boost::process;
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf {
|
||||
using adapteraddrs_t = util::c_ptr<IP_ADAPTER_ADDRESSES>;
|
||||
|
||||
@@ -116,30 +115,26 @@ namespace platf {
|
||||
decltype(WlanEnumInterfaces) *fn_WlanEnumInterfaces = nullptr;
|
||||
decltype(WlanSetInterface) *fn_WlanSetInterface = nullptr;
|
||||
|
||||
std::filesystem::path
|
||||
appdata() {
|
||||
std::filesystem::path appdata() {
|
||||
WCHAR sunshine_path[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, sunshine_path, _countof(sunshine_path));
|
||||
return std::filesystem::path { sunshine_path }.remove_filename() / L"config"sv;
|
||||
return std::filesystem::path {sunshine_path}.remove_filename() / L"config"sv;
|
||||
}
|
||||
|
||||
std::string
|
||||
from_sockaddr(const sockaddr *const socket_address) {
|
||||
std::string from_sockaddr(const sockaddr *const socket_address) {
|
||||
char data[INET6_ADDRSTRLEN] = {};
|
||||
|
||||
auto family = socket_address->sa_family;
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) socket_address)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
}
|
||||
else if (family == AF_INET) {
|
||||
} else if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) socket_address)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
return std::string { data };
|
||||
return std::string {data};
|
||||
}
|
||||
|
||||
std::pair<std::uint16_t, std::string>
|
||||
from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
char data[INET6_ADDRSTRLEN] = {};
|
||||
|
||||
auto family = ip_addr->sa_family;
|
||||
@@ -147,18 +142,16 @@ namespace platf {
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6 *) ip_addr)->sin6_port;
|
||||
}
|
||||
else if (family == AF_INET) {
|
||||
} else if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in *) ip_addr)->sin_port;
|
||||
}
|
||||
|
||||
return { port, std::string { data } };
|
||||
return {port, std::string {data}};
|
||||
}
|
||||
|
||||
adapteraddrs_t
|
||||
get_adapteraddrs() {
|
||||
adapteraddrs_t info { nullptr };
|
||||
adapteraddrs_t get_adapteraddrs() {
|
||||
adapteraddrs_t info {nullptr};
|
||||
ULONG size = 0;
|
||||
|
||||
while (GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, info.get(), &size) == ERROR_BUFFER_OVERFLOW) {
|
||||
@@ -168,8 +161,7 @@ namespace platf {
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string
|
||||
get_mac_address(const std::string_view &address) {
|
||||
std::string get_mac_address(const std::string_view &address) {
|
||||
adapteraddrs_t info = get_adapteraddrs();
|
||||
for (auto adapter_pos = info.get(); adapter_pos != nullptr; adapter_pos = adapter_pos->Next) {
|
||||
for (auto addr_pos = adapter_pos->FirstUnicastAddress; addr_pos != nullptr; addr_pos = addr_pos->Next) {
|
||||
@@ -190,8 +182,7 @@ namespace platf {
|
||||
return "00:00:00:00:00:00"s;
|
||||
}
|
||||
|
||||
HDESK
|
||||
syncThreadDesktop() {
|
||||
HDESK syncThreadDesktop() {
|
||||
auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL);
|
||||
if (!hDesk) {
|
||||
auto err = GetLastError();
|
||||
@@ -210,23 +201,15 @@ namespace platf {
|
||||
return hDesk;
|
||||
}
|
||||
|
||||
void
|
||||
print_status(const std::string_view &prefix, HRESULT status) {
|
||||
void print_status(const std::string_view &prefix, HRESULT status) {
|
||||
char err_string[1024];
|
||||
|
||||
DWORD bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr,
|
||||
status,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
err_string,
|
||||
sizeof(err_string),
|
||||
nullptr);
|
||||
DWORD bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_string, sizeof(err_string), nullptr);
|
||||
|
||||
BOOST_LOG(error) << prefix << ": "sv << std::string_view { err_string, bytes };
|
||||
BOOST_LOG(error) << prefix << ": "sv << std::string_view {err_string, bytes};
|
||||
}
|
||||
|
||||
bool
|
||||
IsUserAdmin(HANDLE user_token) {
|
||||
bool IsUserAdmin(HANDLE user_token) {
|
||||
WINBOOL ret;
|
||||
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
||||
PSID AdministratorsGroup;
|
||||
@@ -235,16 +218,21 @@ namespace platf {
|
||||
2,
|
||||
SECURITY_BUILTIN_DOMAIN_RID,
|
||||
DOMAIN_ALIAS_RID_ADMINS,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
&AdministratorsGroup);
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&AdministratorsGroup
|
||||
);
|
||||
if (ret) {
|
||||
if (!CheckTokenMembership(user_token, AdministratorsGroup, &ret)) {
|
||||
ret = false;
|
||||
BOOST_LOG(error) << "Failed to verify token membership for administrative access: " << GetLastError();
|
||||
}
|
||||
FreeSid(AdministratorsGroup);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(error) << "Unable to allocate SID to check administrative access: " << GetLastError();
|
||||
}
|
||||
|
||||
@@ -255,8 +243,7 @@ namespace platf {
|
||||
* @brief Obtain the current sessions user's primary token with elevated privileges.
|
||||
* @return The user's token. If user has admin capability it will be elevated, otherwise it will be a limited token. On error, `nullptr`.
|
||||
*/
|
||||
HANDLE
|
||||
retrieve_users_token(bool elevated) {
|
||||
HANDLE retrieve_users_token(bool elevated) {
|
||||
DWORD consoleSessionId;
|
||||
HANDLE userToken;
|
||||
TOKEN_ELEVATION_TYPE elevationType;
|
||||
@@ -317,8 +304,7 @@ namespace platf {
|
||||
return userToken;
|
||||
}
|
||||
|
||||
bool
|
||||
merge_user_environment_block(bp::environment &env, HANDLE shell_token) {
|
||||
bool merge_user_environment_block(bp::environment &env, HANDLE shell_token) {
|
||||
// Get the target user's environment block
|
||||
PVOID env_block;
|
||||
if (!CreateEnvironmentBlock(&env_block, shell_token, FALSE)) {
|
||||
@@ -328,13 +314,14 @@ namespace platf {
|
||||
// Parse the environment block and populate env
|
||||
for (auto c = (PWCHAR) env_block; *c != UNICODE_NULL; c += wcslen(c) + 1) {
|
||||
// Environment variable entries end with a null-terminator, so std::wstring() will get an entire entry.
|
||||
std::string env_tuple = to_utf8(std::wstring { c });
|
||||
std::string env_tuple = to_utf8(std::wstring {c});
|
||||
std::string env_name = env_tuple.substr(0, env_tuple.find('='));
|
||||
std::string env_val = env_tuple.substr(env_tuple.find('=') + 1);
|
||||
|
||||
// Perform a case-insensitive search to see if this variable name already exists
|
||||
auto itr = std::find_if(env.cbegin(), env.cend(),
|
||||
[&](const auto &e) { return boost::iequals(e.get_name(), env_name); });
|
||||
auto itr = std::find_if(env.cbegin(), env.cend(), [&](const auto &e) {
|
||||
return boost::iequals(e.get_name(), env_name);
|
||||
});
|
||||
if (itr != env.cend()) {
|
||||
// Use this existing name if it is already present to ensure we merge properly
|
||||
env_name = itr->get_name();
|
||||
@@ -343,8 +330,7 @@ namespace platf {
|
||||
// For the PATH variable, we will merge the values together
|
||||
if (boost::iequals(env_name, "PATH")) {
|
||||
env[env_name] = env_val + ";" + env[env_name].to_string();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Other variables will be superseded by those in the user's environment block
|
||||
env[env_name] = env_val;
|
||||
}
|
||||
@@ -358,8 +344,7 @@ namespace platf {
|
||||
* @brief Check if the current process is running with system-level privileges.
|
||||
* @return `true` if the current process has system-level privileges, `false` otherwise.
|
||||
*/
|
||||
bool
|
||||
is_running_as_system() {
|
||||
bool is_running_as_system() {
|
||||
BOOL ret;
|
||||
PSID SystemSid;
|
||||
DWORD dwSize = SECURITY_MAX_SID_SIZE;
|
||||
@@ -379,8 +364,7 @@ namespace platf {
|
||||
BOOST_LOG(error) << "Failed to check token membership: " << GetLastError();
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(error) << "Failed to create a SID for the local system account. This may happen if the system is out of memory or if the SID buffer is too small: " << GetLastError();
|
||||
}
|
||||
|
||||
@@ -390,14 +374,12 @@ namespace platf {
|
||||
}
|
||||
|
||||
// Note: This does NOT append a null terminator
|
||||
void
|
||||
append_string_to_environment_block(wchar_t *env_block, int &offset, const std::wstring &wstr) {
|
||||
void append_string_to_environment_block(wchar_t *env_block, int &offset, const std::wstring &wstr) {
|
||||
std::memcpy(&env_block[offset], wstr.data(), wstr.length() * sizeof(wchar_t));
|
||||
offset += wstr.length();
|
||||
}
|
||||
|
||||
std::wstring
|
||||
create_environment_block(bp::environment &env) {
|
||||
std::wstring create_environment_block(bp::environment &env) {
|
||||
int size = 0;
|
||||
for (const auto &entry : env) {
|
||||
auto name = entry.get_name();
|
||||
@@ -426,8 +408,7 @@ namespace platf {
|
||||
return std::wstring(env_block, offset);
|
||||
}
|
||||
|
||||
LPPROC_THREAD_ATTRIBUTE_LIST
|
||||
allocate_proc_thread_attr_list(DWORD attribute_count) {
|
||||
LPPROC_THREAD_ATTRIBUTE_LIST allocate_proc_thread_attr_list(DWORD attribute_count) {
|
||||
SIZE_T size;
|
||||
InitializeProcThreadAttributeList(NULL, attribute_count, 0, &size);
|
||||
|
||||
@@ -444,8 +425,7 @@ namespace platf {
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
free_proc_thread_attr_list(LPPROC_THREAD_ATTRIBUTE_LIST list) {
|
||||
void free_proc_thread_attr_list(LPPROC_THREAD_ATTRIBUTE_LIST list) {
|
||||
DeleteProcThreadAttributeList(list);
|
||||
HeapFree(GetProcessHeap(), 0, list);
|
||||
}
|
||||
@@ -458,8 +438,7 @@ namespace platf {
|
||||
* @param process_info A reference to a `PROCESS_INFORMATION` structure that contains information about the new process.
|
||||
* @return A `bp::child` object representing the new process, or an empty `bp::child` object if the launch failed.
|
||||
*/
|
||||
bp::child
|
||||
create_boost_child_from_results(bool process_launched, const std::string &cmd, std::error_code &ec, PROCESS_INFORMATION &process_info) {
|
||||
bp::child create_boost_child_from_results(bool process_launched, const std::string &cmd, std::error_code &ec, PROCESS_INFORMATION &process_info) {
|
||||
// Use RAII to ensure the process is closed when we're done with it, even if there was an error.
|
||||
auto close_process_handles = util::fail_guard([process_launched, process_info]() {
|
||||
if (process_launched) {
|
||||
@@ -478,8 +457,7 @@ namespace platf {
|
||||
auto child = bp::child((bp::pid_t) process_info.dwProcessId);
|
||||
BOOST_LOG(info) << cmd << " running with PID "sv << child.id();
|
||||
return child;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto winerror = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to launch process: "sv << winerror;
|
||||
ec = std::make_error_code(std::errc::invalid_argument);
|
||||
@@ -496,8 +474,7 @@ namespace platf {
|
||||
* @param callback A function that will be executed while impersonating the user.
|
||||
* @return Object that will store any error that occurred during the impersonation
|
||||
*/
|
||||
std::error_code
|
||||
impersonate_current_user(HANDLE user_token, std::function<void()> callback) {
|
||||
std::error_code impersonate_current_user(HANDLE user_token, std::function<void()> callback) {
|
||||
std::error_code ec;
|
||||
// Impersonate the user when launching the process. This will ensure that appropriate access
|
||||
// checks are done against the user token, not our SYSTEM token. It will also allow network
|
||||
@@ -534,8 +511,7 @@ namespace platf {
|
||||
* @param ec A reference to a `std::error_code` object that will store any error that occurred during the creation of the structure.
|
||||
* @return A structure that contains information about how to launch the new process.
|
||||
*/
|
||||
STARTUPINFOEXW
|
||||
create_startup_info(FILE *file, HANDLE *job, std::error_code &ec) {
|
||||
STARTUPINFOEXW create_startup_info(FILE *file, HANDLE *job, std::error_code &ec) {
|
||||
// Initialize a zeroed-out STARTUPINFOEXW structure and set its size
|
||||
STARTUPINFOEXW startup_info = {};
|
||||
startup_info.StartupInfo.cb = sizeof(startup_info);
|
||||
@@ -563,13 +539,7 @@ namespace platf {
|
||||
//
|
||||
// Note: The value we point to here must be valid for the lifetime of the attribute list,
|
||||
// so we need to point into the STARTUPINFO instead of our log_file_variable on the stack.
|
||||
UpdateProcThreadAttribute(startup_info.lpAttributeList,
|
||||
0,
|
||||
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
||||
&startup_info.StartupInfo.hStdOutput,
|
||||
sizeof(startup_info.StartupInfo.hStdOutput),
|
||||
NULL,
|
||||
NULL);
|
||||
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &startup_info.StartupInfo.hStdOutput, sizeof(startup_info.StartupInfo.hStdOutput), NULL, NULL);
|
||||
}
|
||||
|
||||
if (job) {
|
||||
@@ -577,13 +547,7 @@ namespace platf {
|
||||
//
|
||||
// Note: The value we point to here must be valid for the lifetime of the attribute list,
|
||||
// so we take a HANDLE* instead of just a HANDLE to use the caller's stack storage.
|
||||
UpdateProcThreadAttribute(startup_info.lpAttributeList,
|
||||
0,
|
||||
PROC_THREAD_ATTRIBUTE_JOB_LIST,
|
||||
job,
|
||||
sizeof(*job),
|
||||
NULL,
|
||||
NULL);
|
||||
UpdateProcThreadAttribute(startup_info.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_JOB_LIST, job, sizeof(*job), NULL, NULL);
|
||||
}
|
||||
|
||||
return startup_info;
|
||||
@@ -594,8 +558,7 @@ namespace platf {
|
||||
* @param token The primary token identifying the user to use, or `NULL` to restore original keys.
|
||||
* @return `true` if the override or restore operation was successful.
|
||||
*/
|
||||
bool
|
||||
override_per_user_predefined_keys(HANDLE token) {
|
||||
bool override_per_user_predefined_keys(HANDLE token) {
|
||||
HKEY user_classes_root = NULL;
|
||||
if (token) {
|
||||
auto err = RegOpenUserClassesRoot(token, 0, GENERIC_ALL, &user_classes_root);
|
||||
@@ -651,8 +614,7 @@ namespace platf {
|
||||
* @param argument The raw argument to process.
|
||||
* @return An argument string suitable for use by CreateProcess().
|
||||
*/
|
||||
std::wstring
|
||||
escape_argument(const std::wstring &argument) {
|
||||
std::wstring escape_argument(const std::wstring &argument) {
|
||||
// If there are no characters requiring quoting/escaping, we're done
|
||||
if (argument.find_first_of(L" \t\n\v\"") == argument.npos) {
|
||||
return argument;
|
||||
@@ -672,11 +634,9 @@ namespace platf {
|
||||
if (it == argument.end()) {
|
||||
escaped_arg.append(backslash_count * 2, L'\\');
|
||||
break;
|
||||
}
|
||||
else if (*it == L'"') {
|
||||
} else if (*it == L'"') {
|
||||
escaped_arg.append(backslash_count * 2 + 1, L'\\');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
escaped_arg.append(backslash_count, L'\\');
|
||||
}
|
||||
|
||||
@@ -691,8 +651,7 @@ namespace platf {
|
||||
* @param argument An argument already escaped by `escape_argument()`.
|
||||
* @return An argument string suitable for use by cmd.exe.
|
||||
*/
|
||||
std::wstring
|
||||
escape_argument_for_cmd(const std::wstring &argument) {
|
||||
std::wstring escape_argument_for_cmd(const std::wstring &argument) {
|
||||
// Start with the original string and modify from there
|
||||
std::wstring escaped_arg = argument;
|
||||
|
||||
@@ -716,8 +675,7 @@ namespace platf {
|
||||
* @param creation_flags The creation flags for CreateProcess(), which may be modified by this function.
|
||||
* @return A command string suitable for use by CreateProcess().
|
||||
*/
|
||||
std::wstring
|
||||
resolve_command_string(const std::string &raw_cmd, const std::wstring &working_dir, HANDLE token, DWORD &creation_flags) {
|
||||
std::wstring resolve_command_string(const std::string &raw_cmd, const std::wstring &working_dir, HANDLE token, DWORD &creation_flags) {
|
||||
std::wstring raw_cmd_w = from_utf8(raw_cmd);
|
||||
|
||||
// First, convert the given command into parts so we can get the executable/file/URL without parameters
|
||||
@@ -744,16 +702,14 @@ namespace platf {
|
||||
|
||||
// If the target is a URL, the class is found using the URL scheme (prior to and not including the ':')
|
||||
lookup_string = scheme.data();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// If the target is not a URL, assume it's a regular file path
|
||||
auto extension = PathFindExtensionW(raw_target.c_str());
|
||||
if (extension == nullptr || *extension == 0) {
|
||||
// If the file has no extension, assume it's a command and allow CreateProcess()
|
||||
// to try to find it via PATH
|
||||
return from_utf8(raw_cmd);
|
||||
}
|
||||
else if (boost::iequals(extension, L".exe")) {
|
||||
} else if (boost::iequals(extension, L".exe")) {
|
||||
// If the file has an .exe extension, we will bypass the resolution here and
|
||||
// directly pass the unmodified command string to CreateProcess(). The argument
|
||||
// escaping rules are subtly different between CreateProcess() and ShellExecute(),
|
||||
@@ -814,7 +770,7 @@ namespace platf {
|
||||
// uncommon ones that are unsupported here.
|
||||
//
|
||||
// https://web.archive.org/web/20111002101214/http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101(v=vs.85).aspx
|
||||
std::wstring cmd_string { shell_command_string.data() };
|
||||
std::wstring cmd_string {shell_command_string.data()};
|
||||
size_t match_pos = 0;
|
||||
while ((match_pos = cmd_string.find_first_of(L'%', match_pos)) != std::wstring::npos) {
|
||||
std::wstring match_replacement;
|
||||
@@ -843,19 +799,20 @@ namespace platf {
|
||||
case L'6':
|
||||
case L'7':
|
||||
case L'8':
|
||||
case L'9': {
|
||||
// Arguments numbers are 1-based, except for %0 which is equivalent to %1
|
||||
int index = next_char - L'0';
|
||||
if (next_char != L'0') {
|
||||
index--;
|
||||
}
|
||||
case L'9':
|
||||
{
|
||||
// Arguments numbers are 1-based, except for %0 which is equivalent to %1
|
||||
int index = next_char - L'0';
|
||||
if (next_char != L'0') {
|
||||
index--;
|
||||
}
|
||||
|
||||
// Replace with the matching argument, or nothing if the index is invalid
|
||||
if (index < raw_cmd_parts.size()) {
|
||||
match_replacement = raw_cmd_parts.at(index);
|
||||
// Replace with the matching argument, or nothing if the index is invalid
|
||||
if (index < raw_cmd_parts.size()) {
|
||||
match_replacement = raw_cmd_parts.at(index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// All arguments following the target
|
||||
case L'*':
|
||||
@@ -878,29 +835,29 @@ namespace platf {
|
||||
// Long file path of target
|
||||
case L'l':
|
||||
case L'd':
|
||||
case L'v': {
|
||||
std::array<WCHAR, MAX_PATH> path;
|
||||
std::array<PCWCHAR, 2> other_dirs { working_dir.c_str(), nullptr };
|
||||
case L'v':
|
||||
{
|
||||
std::array<WCHAR, MAX_PATH> path;
|
||||
std::array<PCWCHAR, 2> other_dirs {working_dir.c_str(), nullptr};
|
||||
|
||||
// PathFindOnPath() is a little gross because it uses the same
|
||||
// buffer for input and output, so we need to copy our input
|
||||
// into the path array.
|
||||
std::wcsncpy(path.data(), raw_target.c_str(), path.size());
|
||||
if (path[path.size() - 1] != 0) {
|
||||
// The path was so long it was truncated by this copy. We'll
|
||||
// assume it was an absolute path (likely) and use it unmodified.
|
||||
match_replacement = raw_target;
|
||||
// PathFindOnPath() is a little gross because it uses the same
|
||||
// buffer for input and output, so we need to copy our input
|
||||
// into the path array.
|
||||
std::wcsncpy(path.data(), raw_target.c_str(), path.size());
|
||||
if (path[path.size() - 1] != 0) {
|
||||
// The path was so long it was truncated by this copy. We'll
|
||||
// assume it was an absolute path (likely) and use it unmodified.
|
||||
match_replacement = raw_target;
|
||||
}
|
||||
// See if we can find the path on our search path or working directory
|
||||
else if (PathFindOnPathW(path.data(), other_dirs.data())) {
|
||||
match_replacement = std::wstring {path.data()};
|
||||
} else {
|
||||
// We couldn't find the target, so we'll just hope for the best
|
||||
match_replacement = raw_target;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// See if we can find the path on our search path or working directory
|
||||
else if (PathFindOnPathW(path.data(), other_dirs.data())) {
|
||||
match_replacement = std::wstring { path.data() };
|
||||
}
|
||||
else {
|
||||
// We couldn't find the target, so we'll just hope for the best
|
||||
match_replacement = raw_target;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Working directory
|
||||
case L'w':
|
||||
@@ -938,8 +895,7 @@ namespace platf {
|
||||
* @param group A pointer to a `bp::group` object to which the new process should belong (may be `nullptr`).
|
||||
* @return A `bp::child` object representing the new process, or an empty `bp::child` object if the launch fails.
|
||||
*/
|
||||
bp::child
|
||||
run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
bp::child run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
std::wstring start_dir = from_utf8(working_dir.string());
|
||||
HANDLE job = group ? group->native_handle() : nullptr;
|
||||
STARTUPINFOEXW startup_info = create_startup_info(file, job ? &job : nullptr, ec);
|
||||
@@ -967,9 +923,11 @@ namespace platf {
|
||||
|
||||
// Find the PATH variable in our environment block using a case-insensitive search
|
||||
auto sunshine_wenv = boost::this_process::wenvironment();
|
||||
std::wstring path_var_name { L"PATH" };
|
||||
std::wstring path_var_name {L"PATH"};
|
||||
std::wstring old_path_val;
|
||||
auto itr = std::find_if(sunshine_wenv.cbegin(), sunshine_wenv.cend(), [&](const auto &e) { return boost::iequals(e.get_name(), path_var_name); });
|
||||
auto itr = std::find_if(sunshine_wenv.cbegin(), sunshine_wenv.cend(), [&](const auto &e) {
|
||||
return boost::iequals(e.get_name(), path_var_name);
|
||||
});
|
||||
if (itr != sunshine_wenv.cend()) {
|
||||
// Use the existing variable if it exists, since Boost treats these as case-sensitive.
|
||||
path_var_name = itr->get_name();
|
||||
@@ -984,8 +942,7 @@ namespace platf {
|
||||
auto restore_path = util::fail_guard([&]() {
|
||||
if (old_path_val.empty()) {
|
||||
sunshine_wenv[path_var_name].clear();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sunshine_wenv[path_var_name].assign(old_path_val);
|
||||
}
|
||||
});
|
||||
@@ -1015,17 +972,7 @@ namespace platf {
|
||||
ec = impersonate_current_user(user_token, [&]() {
|
||||
std::wstring env_block = create_environment_block(cloned_env);
|
||||
std::wstring wcmd = resolve_command_string(cmd, start_dir, user_token, creation_flags);
|
||||
ret = CreateProcessAsUserW(user_token,
|
||||
NULL,
|
||||
(LPWSTR) wcmd.c_str(),
|
||||
NULL,
|
||||
NULL,
|
||||
!!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES),
|
||||
creation_flags,
|
||||
env_block.data(),
|
||||
start_dir.empty() ? NULL : start_dir.c_str(),
|
||||
(LPSTARTUPINFOW) &startup_info,
|
||||
&process_info);
|
||||
ret = CreateProcessAsUserW(user_token, NULL, (LPWSTR) wcmd.c_str(), NULL, NULL, !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), creation_flags, env_block.data(), start_dir.empty() ? NULL : start_dir.c_str(), (LPSTARTUPINFOW) &startup_info, &process_info);
|
||||
});
|
||||
}
|
||||
// Otherwise, launch the process using CreateProcessW()
|
||||
@@ -1049,16 +996,7 @@ namespace platf {
|
||||
|
||||
std::wstring env_block = create_environment_block(cloned_env);
|
||||
std::wstring wcmd = resolve_command_string(cmd, start_dir, NULL, creation_flags);
|
||||
ret = CreateProcessW(NULL,
|
||||
(LPWSTR) wcmd.c_str(),
|
||||
NULL,
|
||||
NULL,
|
||||
!!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES),
|
||||
creation_flags,
|
||||
env_block.data(),
|
||||
start_dir.empty() ? NULL : start_dir.c_str(),
|
||||
(LPSTARTUPINFOW) &startup_info,
|
||||
&process_info);
|
||||
ret = CreateProcessW(NULL, (LPWSTR) wcmd.c_str(), NULL, NULL, !!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES), creation_flags, env_block.data(), start_dir.empty() ? NULL : start_dir.c_str(), (LPSTARTUPINFOW) &startup_info, &process_info);
|
||||
}
|
||||
|
||||
// Use the results of the launch to create a bp::child object
|
||||
@@ -1069,8 +1007,7 @@ namespace platf {
|
||||
* @brief Open a url in the default web browser.
|
||||
* @param url The url to open.
|
||||
*/
|
||||
void
|
||||
open_url(const std::string &url) {
|
||||
void open_url(const std::string &url) {
|
||||
boost::process::v1::environment _env = boost::this_process::environment();
|
||||
auto working_dir = boost::filesystem::path();
|
||||
std::error_code ec;
|
||||
@@ -1078,15 +1015,13 @@ namespace platf {
|
||||
auto child = run_command(false, false, url, working_dir, _env, nullptr, ec, nullptr);
|
||||
if (ec) {
|
||||
BOOST_LOG(warning) << "Couldn't open url ["sv << url << "]: System: "sv << ec.message();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(info) << "Opened url ["sv << url << "]"sv;
|
||||
child.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority) {
|
||||
void adjust_thread_priority(thread_priority_e priority) {
|
||||
int win32_priority;
|
||||
|
||||
switch (priority) {
|
||||
@@ -1113,8 +1048,7 @@ namespace platf {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
streaming_will_start() {
|
||||
void streaming_will_start() {
|
||||
static std::once_flag load_wlanapi_once_flag;
|
||||
std::call_once(load_wlanapi_once_flag, []() {
|
||||
// wlanapi.dll is not installed by default on Windows Server, so we load it dynamically
|
||||
@@ -1150,8 +1084,7 @@ namespace platf {
|
||||
// Reduce timer period to 0.5ms
|
||||
if (nt_set_timer_resolution_max()) {
|
||||
used_nt_set_timer_resolution = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(error) << "NtSetTimerResolution() failed, falling back to timeBeginPeriod()";
|
||||
timeBeginPeriod(1);
|
||||
used_nt_set_timer_resolution = false;
|
||||
@@ -1186,8 +1119,7 @@ namespace platf {
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-wdi-set-connection-quality
|
||||
// https://docs.microsoft.com/en-us/previous-versions/windows/hardware/wireless/native-802-11-media-streaming
|
||||
BOOL value = TRUE;
|
||||
auto error = fn_WlanSetInterface(wlan_handle, &wlan_interface_list->InterfaceInfo[i].InterfaceGuid,
|
||||
wlan_intf_opcode_media_streaming_mode, sizeof(value), &value, nullptr);
|
||||
auto error = fn_WlanSetInterface(wlan_handle, &wlan_interface_list->InterfaceInfo[i].InterfaceGuid, wlan_intf_opcode_media_streaming_mode, sizeof(value), &value, nullptr);
|
||||
if (error == ERROR_SUCCESS) {
|
||||
BOOST_LOG(info) << "WLAN interface "sv << i << " is now in low latency mode"sv;
|
||||
}
|
||||
@@ -1195,8 +1127,7 @@ namespace platf {
|
||||
}
|
||||
|
||||
fn_WlanFreeMemory(wlan_interface_list);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
fn_WlanCloseHandle(wlan_handle, nullptr);
|
||||
wlan_handle = NULL;
|
||||
}
|
||||
@@ -1220,21 +1151,18 @@ namespace platf {
|
||||
if (SystemParametersInfoW(SPI_SETMOUSEKEYS, 0, &new_mouse_keys_state, 0)) {
|
||||
// Remember to restore the previous settings when we stop streaming
|
||||
enabled_mouse_keys = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(warning) << "Unable to enable Mouse Keys: "sv << winerr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(warning) << "Unable to get current state of Mouse Keys: "sv << winerr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
streaming_will_stop() {
|
||||
void streaming_will_stop() {
|
||||
// Demote ourselves back to normal priority class
|
||||
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
||||
|
||||
@@ -1244,8 +1172,7 @@ namespace platf {
|
||||
if (!nt_set_timer_resolution_min()) {
|
||||
BOOST_LOG(error) << "nt_set_timer_resolution_min() failed even though nt_set_timer_resolution_max() succeeded";
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
timeEndPeriod(1);
|
||||
}
|
||||
|
||||
@@ -1268,8 +1195,7 @@ namespace platf {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
restart_on_exit() {
|
||||
void restart_on_exit() {
|
||||
STARTUPINFOEXW startup_info {};
|
||||
startup_info.StartupInfo.cb = sizeof(startup_info);
|
||||
|
||||
@@ -1281,16 +1207,7 @@ namespace platf {
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION process_info;
|
||||
if (!CreateProcessW(executable,
|
||||
GetCommandLineW(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
false,
|
||||
CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT,
|
||||
nullptr,
|
||||
nullptr,
|
||||
(LPSTARTUPINFOW) &startup_info,
|
||||
&process_info)) {
|
||||
if (!CreateProcessW(executable, GetCommandLineW(), nullptr, nullptr, false, CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr, (LPSTARTUPINFOW) &startup_info, &process_info)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(fatal) << "Unable to restart Sunshine: "sv << winerr;
|
||||
return;
|
||||
@@ -1300,8 +1217,7 @@ namespace platf {
|
||||
CloseHandle(process_info.hThread);
|
||||
}
|
||||
|
||||
void
|
||||
restart() {
|
||||
void restart() {
|
||||
// If we're running standalone, we have to respawn ourselves via CreateProcess().
|
||||
// If we're running from the service, we should just exit and let it respawn us.
|
||||
if (GetConsoleWindow() != NULL) {
|
||||
@@ -1313,13 +1229,11 @@ namespace platf {
|
||||
lifetime::exit_sunshine(0, true);
|
||||
}
|
||||
|
||||
int
|
||||
set_env(const std::string &name, const std::string &value) {
|
||||
int set_env(const std::string &name, const std::string &value) {
|
||||
return _putenv_s(name.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
int
|
||||
unset_env(const std::string &name) {
|
||||
int unset_env(const std::string &name) {
|
||||
return _putenv_s(name.c_str(), "");
|
||||
}
|
||||
|
||||
@@ -1328,8 +1242,7 @@ namespace platf {
|
||||
bool requested_exit;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK
|
||||
prgrp_enum_windows(HWND hwnd, LPARAM lParam) {
|
||||
static BOOL CALLBACK prgrp_enum_windows(HWND hwnd, LPARAM lParam) {
|
||||
auto enum_ctx = (enum_wnd_context_t *) lParam;
|
||||
|
||||
// Find the owner PID of this window
|
||||
@@ -1345,8 +1258,7 @@ namespace platf {
|
||||
if (SendNotifyMessageW(hwnd, WM_CLOSE, 0, 0)) {
|
||||
BOOST_LOG(debug) << "Sent WM_CLOSE to PID: "sv << wnd_process_id;
|
||||
enum_ctx->requested_exit = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto error = GetLastError();
|
||||
BOOST_LOG(warning) << "Failed to send WM_CLOSE to PID ["sv << wnd_process_id << "]: " << error;
|
||||
}
|
||||
@@ -1356,8 +1268,7 @@ namespace platf {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool
|
||||
request_process_group_exit(std::uintptr_t native_handle) {
|
||||
bool request_process_group_exit(std::uintptr_t native_handle) {
|
||||
auto job_handle = (HANDLE) native_handle;
|
||||
|
||||
// Get list of all processes in our job object
|
||||
@@ -1367,8 +1278,7 @@ namespace platf {
|
||||
auto fg = util::fail_guard([&process_id_list]() {
|
||||
free(process_id_list);
|
||||
});
|
||||
while (!(success = QueryInformationJobObject(job_handle, JobObjectBasicProcessIdList,
|
||||
process_id_list, required_length, &required_length)) &&
|
||||
while (!(success = QueryInformationJobObject(job_handle, JobObjectBasicProcessIdList, process_id_list, required_length, &required_length)) &&
|
||||
GetLastError() == ERROR_MORE_DATA) {
|
||||
free(process_id_list);
|
||||
process_id_list = (PJOBOBJECT_BASIC_PROCESS_ID_LIST) calloc(1, required_length);
|
||||
@@ -1381,8 +1291,7 @@ namespace platf {
|
||||
auto err = GetLastError();
|
||||
BOOST_LOG(warning) << "Failed to enumerate processes in group: "sv << err;
|
||||
return false;
|
||||
}
|
||||
else if (process_id_list->NumberOfProcessIdsInList == 0) {
|
||||
} else if (process_id_list->NumberOfProcessIdsInList == 0) {
|
||||
// If all processes are already dead, treat it as a success
|
||||
return true;
|
||||
}
|
||||
@@ -1400,8 +1309,7 @@ namespace platf {
|
||||
return enum_ctx.requested_exit;
|
||||
}
|
||||
|
||||
bool
|
||||
process_group_running(std::uintptr_t native_handle) {
|
||||
bool process_group_running(std::uintptr_t native_handle) {
|
||||
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION accounting_info;
|
||||
|
||||
if (!QueryInformationJobObject((HANDLE) native_handle, JobObjectBasicAccountingInformation, &accounting_info, sizeof(accounting_info), nullptr)) {
|
||||
@@ -1413,8 +1321,7 @@ namespace platf {
|
||||
return accounting_info.ActiveProcesses != 0;
|
||||
}
|
||||
|
||||
SOCKADDR_IN
|
||||
to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
||||
SOCKADDR_IN to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
||||
SOCKADDR_IN saddr_v4 = {};
|
||||
|
||||
saddr_v4.sin_family = AF_INET;
|
||||
@@ -1426,8 +1333,7 @@ namespace platf {
|
||||
return saddr_v4;
|
||||
}
|
||||
|
||||
SOCKADDR_IN6
|
||||
to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
||||
SOCKADDR_IN6 to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
||||
SOCKADDR_IN6 saddr_v6 = {};
|
||||
|
||||
saddr_v6.sin6_family = AF_INET6;
|
||||
@@ -1442,8 +1348,7 @@ namespace platf {
|
||||
|
||||
// Use UDP segmentation offload if it is supported by the OS. If the NIC is capable, this will use
|
||||
// hardware acceleration to reduce CPU usage. Support for USO was introduced in Windows 10 20H1.
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info) {
|
||||
bool send_batch(batched_send_info_t &send_info) {
|
||||
WSAMSG msg;
|
||||
|
||||
// Convert the target address into a SOCKADDR
|
||||
@@ -1454,8 +1359,7 @@ namespace platf {
|
||||
|
||||
msg.name = (PSOCKADDR) &taddr_v6;
|
||||
msg.namelen = sizeof(taddr_v6);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
|
||||
|
||||
msg.name = (PSOCKADDR) &taddr_v4;
|
||||
@@ -1477,8 +1381,7 @@ namespace platf {
|
||||
bufs[bufcount].len = send_info.payload_size;
|
||||
bufcount++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Translate buffer descriptors into WSABUFs
|
||||
auto payload_offset = send_info.block_offset * send_info.payload_size;
|
||||
auto payload_length = payload_offset + (send_info.block_count * send_info.payload_size);
|
||||
@@ -1496,8 +1399,7 @@ namespace platf {
|
||||
msg.dwFlags = 0;
|
||||
|
||||
// At most, one DWORD option and one PKTINFO option
|
||||
char cmbuf[WSA_CMSG_SPACE(sizeof(DWORD)) +
|
||||
std::max(WSA_CMSG_SPACE(sizeof(IN6_PKTINFO)), WSA_CMSG_SPACE(sizeof(IN_PKTINFO)))] = {};
|
||||
char cmbuf[WSA_CMSG_SPACE(sizeof(DWORD)) + std::max(WSA_CMSG_SPACE(sizeof(IN6_PKTINFO)), WSA_CMSG_SPACE(sizeof(IN_PKTINFO)))] = {};
|
||||
ULONG cmbuflen = 0;
|
||||
|
||||
msg.Control.buf = cmbuf;
|
||||
@@ -1517,8 +1419,7 @@ namespace platf {
|
||||
cm->cmsg_type = IPV6_PKTINFO;
|
||||
cm->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
IN_PKTINFO pktInfo;
|
||||
|
||||
SOCKADDR_IN saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
|
||||
@@ -1550,8 +1451,7 @@ namespace platf {
|
||||
return WSASendMsg((SOCKET) send_info.native_socket, &msg, 0, &bytes_sent, nullptr, nullptr) != SOCKET_ERROR;
|
||||
}
|
||||
|
||||
bool
|
||||
send(send_info_t &send_info) {
|
||||
bool send(send_info_t &send_info) {
|
||||
WSAMSG msg;
|
||||
|
||||
// Convert the target address into a SOCKADDR
|
||||
@@ -1562,8 +1462,7 @@ namespace platf {
|
||||
|
||||
msg.name = (PSOCKADDR) &taddr_v6;
|
||||
msg.namelen = sizeof(taddr_v6);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
|
||||
|
||||
msg.name = (PSOCKADDR) &taddr_v4;
|
||||
@@ -1605,8 +1504,7 @@ namespace platf {
|
||||
cm->cmsg_type = IPV6_PKTINFO;
|
||||
cm->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
IN_PKTINFO pktInfo;
|
||||
|
||||
SOCKADDR_IN saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
|
||||
@@ -1636,7 +1534,8 @@ namespace platf {
|
||||
class qos_t: public deinit_t {
|
||||
public:
|
||||
qos_t(QOS_FLOWID flow_id):
|
||||
flow_id(flow_id) {}
|
||||
flow_id(flow_id) {
|
||||
}
|
||||
|
||||
virtual ~qos_t() {
|
||||
if (!fn_QOSRemoveSocketFromFlow(qos_handle, (SOCKET) NULL, flow_id, 0)) {
|
||||
@@ -1657,8 +1556,7 @@ namespace platf {
|
||||
* @param data_type The type of traffic sent on this socket.
|
||||
* @param dscp_tagging Specifies whether to enable DSCP tagging on outgoing traffic.
|
||||
*/
|
||||
std::unique_ptr<deinit_t>
|
||||
enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) {
|
||||
std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) {
|
||||
SOCKADDR_IN saddr_v4;
|
||||
SOCKADDR_IN6 saddr_v6;
|
||||
PSOCKADDR dest_addr;
|
||||
@@ -1693,7 +1591,7 @@ namespace platf {
|
||||
return;
|
||||
}
|
||||
|
||||
QOS_VERSION qos_version { 1, 0 };
|
||||
QOS_VERSION qos_version {1, 0};
|
||||
if (!fn_QOSCreateHandle(&qos_version, &qos_handle)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(warning) << "QOSCreateHandle() failed: "sv << winerr;
|
||||
@@ -1733,15 +1631,13 @@ namespace platf {
|
||||
if (connect((SOCKET) native_socket, (PSOCKADDR) &saddr_v6, sizeof(saddr_v6)) < 0) {
|
||||
auto wsaerr = WSAGetLastError();
|
||||
BOOST_LOG(error) << "qWAVE dual-stack workaround failed: "sv << wsaerr;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(debug) << "Using qWAVE connect() workaround for QoS tagging"sv;
|
||||
using_connect_hack = true;
|
||||
dest_addr = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
saddr_v4 = to_sockaddr(address.to_v4(), port);
|
||||
dest_addr = (PSOCKADDR) &saddr_v4;
|
||||
}
|
||||
@@ -1768,15 +1664,16 @@ namespace platf {
|
||||
|
||||
return std::make_unique<qos_t>(flow_id);
|
||||
}
|
||||
int64_t
|
||||
qpc_counter() {
|
||||
|
||||
int64_t qpc_counter() {
|
||||
LARGE_INTEGER performance_counter;
|
||||
if (QueryPerformanceCounter(&performance_counter)) return performance_counter.QuadPart;
|
||||
if (QueryPerformanceCounter(&performance_counter)) {
|
||||
return performance_counter.QuadPart;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds
|
||||
qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2) {
|
||||
std::chrono::nanoseconds qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2) {
|
||||
auto get_frequency = []() {
|
||||
LARGE_INTEGER frequency;
|
||||
frequency.QuadPart = 0;
|
||||
@@ -1790,8 +1687,7 @@ namespace platf {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::wstring
|
||||
from_utf8(const std::string &string) {
|
||||
std::wstring from_utf8(const std::string &string) {
|
||||
// No conversion needed if the string is empty
|
||||
if (string.empty()) {
|
||||
return {};
|
||||
@@ -1817,16 +1713,14 @@ namespace platf {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string
|
||||
to_utf8(const std::wstring &string) {
|
||||
std::string to_utf8(const std::wstring &string) {
|
||||
// No conversion needed if the string is empty
|
||||
if (string.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the output size required to store the string
|
||||
auto output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
auto output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to get UTF-8 buffer size: "sv << winerr;
|
||||
@@ -1835,8 +1729,7 @@ namespace platf {
|
||||
|
||||
// Perform the conversion
|
||||
std::string output(output_size, '\0');
|
||||
output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(),
|
||||
output.data(), output.size(), nullptr, nullptr);
|
||||
output_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string.data(), string.size(), output.data(), output.size(), nullptr, nullptr);
|
||||
if (output_size == 0) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to convert string to UTF-8: "sv << winerr;
|
||||
@@ -1846,8 +1739,7 @@ namespace platf {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string
|
||||
get_host_name() {
|
||||
std::string get_host_name() {
|
||||
WCHAR hostname[256];
|
||||
if (GetHostNameW(hostname, ARRAYSIZE(hostname)) == SOCKET_ERROR) {
|
||||
BOOST_LOG(error) << "GetHostNameW() failed: "sv << WSAGetLastError();
|
||||
@@ -1870,11 +1762,12 @@ namespace platf {
|
||||
}
|
||||
|
||||
~win32_high_precision_timer() {
|
||||
if (timer) CloseHandle(timer);
|
||||
if (timer) {
|
||||
CloseHandle(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sleep_for(const std::chrono::nanoseconds &duration) override {
|
||||
void sleep_for(const std::chrono::nanoseconds &duration) override {
|
||||
if (!timer) {
|
||||
BOOST_LOG(error) << "Attempting high_precision_timer::sleep_for() with uninitialized timer";
|
||||
return;
|
||||
@@ -1902,8 +1795,7 @@ namespace platf {
|
||||
HANDLE timer = NULL;
|
||||
};
|
||||
|
||||
std::unique_ptr<high_precision_timer>
|
||||
create_high_precision_timer() {
|
||||
std::unique_ptr<high_precision_timer> create_high_precision_timer() {
|
||||
return std::make_unique<win32_high_precision_timer>();
|
||||
}
|
||||
} // namespace platf
|
||||
|
||||
@@ -4,36 +4,33 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <chrono>
|
||||
#include <string_view>
|
||||
|
||||
// platform includes
|
||||
#include <windows.h>
|
||||
#include <winnt.h>
|
||||
|
||||
namespace platf {
|
||||
void
|
||||
print_status(const std::string_view &prefix, HRESULT status);
|
||||
HDESK
|
||||
syncThreadDesktop();
|
||||
void print_status(const std::string_view &prefix, HRESULT status);
|
||||
HDESK syncThreadDesktop();
|
||||
|
||||
int64_t
|
||||
qpc_counter();
|
||||
int64_t qpc_counter();
|
||||
|
||||
std::chrono::nanoseconds
|
||||
qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2);
|
||||
std::chrono::nanoseconds qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2);
|
||||
|
||||
/**
|
||||
* @brief Convert a UTF-8 string into a UTF-16 wide string.
|
||||
* @param string The UTF-8 string.
|
||||
* @return The converted UTF-16 wide string.
|
||||
*/
|
||||
std::wstring
|
||||
from_utf8(const std::string &string);
|
||||
std::wstring from_utf8(const std::string &string);
|
||||
|
||||
/**
|
||||
* @brief Convert a UTF-16 wide string into a UTF-8 string.
|
||||
* @param string The UTF-16 wide string.
|
||||
* @return The converted UTF-8 string.
|
||||
*/
|
||||
std::string
|
||||
to_utf8(const std::wstring &string);
|
||||
std::string to_utf8(const std::wstring &string);
|
||||
} // namespace platf
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
* @file src/platform/windows/nvprefs/driver_settings.cpp
|
||||
* @brief Definitions for nvidia driver settings.
|
||||
*/
|
||||
// local includes
|
||||
// this include
|
||||
#include "driver_settings.h"
|
||||
|
||||
// local includes
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
namespace {
|
||||
@@ -11,15 +13,13 @@ namespace {
|
||||
const auto sunshine_application_profile_name = L"SunshineStream";
|
||||
const auto sunshine_application_path = L"sunshine.exe";
|
||||
|
||||
void
|
||||
nvapi_error_message(NvAPI_Status status) {
|
||||
void nvapi_error_message(NvAPI_Status status) {
|
||||
NvAPI_ShortString message = {};
|
||||
NvAPI_GetErrorMessage(status, message);
|
||||
nvprefs::error_message(std::string("NvAPI error: ") + message);
|
||||
}
|
||||
|
||||
void
|
||||
fill_nvapi_string(NvAPI_UnicodeString &dest, const wchar_t *src) {
|
||||
void fill_nvapi_string(NvAPI_UnicodeString &dest, const wchar_t *src) {
|
||||
static_assert(sizeof(NvU16) == sizeof(wchar_t));
|
||||
memcpy_s(dest, NVAPI_UNICODE_STRING_MAX * sizeof(NvU16), src, (wcslen(src) + 1) * sizeof(wchar_t));
|
||||
}
|
||||
@@ -34,9 +34,10 @@ namespace nvprefs {
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::init() {
|
||||
if (session_handle) return true;
|
||||
bool driver_settings_t::init() {
|
||||
if (session_handle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
NvAPI_Status status;
|
||||
|
||||
@@ -56,8 +57,7 @@ namespace nvprefs {
|
||||
return load_settings();
|
||||
}
|
||||
|
||||
void
|
||||
driver_settings_t::destroy() {
|
||||
void driver_settings_t::destroy() {
|
||||
if (session_handle) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
session_handle = 0;
|
||||
@@ -65,9 +65,10 @@ namespace nvprefs {
|
||||
NvAPI_Unload();
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::load_settings() {
|
||||
if (!session_handle) return false;
|
||||
bool driver_settings_t::load_settings() {
|
||||
if (!session_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NvAPI_Status status = NvAPI_DRS_LoadSettings(session_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
@@ -80,9 +81,10 @@ namespace nvprefs {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::save_settings() {
|
||||
if (!session_handle) return false;
|
||||
bool driver_settings_t::save_settings() {
|
||||
if (!session_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NvAPI_Status status = NvAPI_DRS_SaveSettings(session_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
@@ -94,9 +96,10 @@ namespace nvprefs {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::restore_global_profile_to_undo(const undo_data_t &undo_data) {
|
||||
if (!session_handle) return false;
|
||||
bool driver_settings_t::restore_global_profile_to_undo(const undo_data_t &undo_data) {
|
||||
if (!session_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &swapchain_data = undo_data.get_opengl_swapchain();
|
||||
if (swapchain_data) {
|
||||
@@ -130,8 +133,7 @@ namespace nvprefs {
|
||||
error_message("NvAPI_DRS_SetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
status = NvAPI_DRS_DeleteProfileSetting(session_handle, profile_handle, OGL_CPL_PREFER_DXPRESENT_ID);
|
||||
|
||||
if (status != NVAPI_OK && status != NVAPI_SETTING_NOT_FOUND) {
|
||||
@@ -142,11 +144,9 @@ namespace nvprefs {
|
||||
}
|
||||
|
||||
info_message("Restored OGL_CPL_PREFER_DXPRESENT for base profile");
|
||||
}
|
||||
else if (status == NVAPI_OK || status == NVAPI_SETTING_NOT_FOUND) {
|
||||
} else if (status == NVAPI_OK || status == NVAPI_SETTING_NOT_FOUND) {
|
||||
info_message("OGL_CPL_PREFER_DXPRESENT has been changed from our value in base profile, not restoring");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
error_message("NvAPI_DRS_GetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
@@ -155,9 +155,10 @@ namespace nvprefs {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::check_and_modify_global_profile(std::optional<undo_data_t> &undo_data) {
|
||||
if (!session_handle) return false;
|
||||
bool driver_settings_t::check_and_modify_global_profile(std::optional<undo_data_t> &undo_data) {
|
||||
if (!session_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
undo_data.reset();
|
||||
NvAPI_Status status;
|
||||
@@ -184,8 +185,7 @@ namespace nvprefs {
|
||||
undo_data = undo_data_t();
|
||||
if (status == NVAPI_OK) {
|
||||
undo_data->set_opengl_swapchain(OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED, setting.u32CurrentValue);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
undo_data->set_opengl_swapchain(OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED, std::nullopt);
|
||||
}
|
||||
|
||||
@@ -204,8 +204,7 @@ namespace nvprefs {
|
||||
}
|
||||
|
||||
info_message("Changed OGL_CPL_PREFER_DXPRESENT to OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED for base profile");
|
||||
}
|
||||
else if (status != NVAPI_OK) {
|
||||
} else if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_GetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
@@ -214,9 +213,10 @@ namespace nvprefs {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::check_and_modify_application_profile(bool &modified) {
|
||||
if (!session_handle) return false;
|
||||
bool driver_settings_t::check_and_modify_application_profile(bool &modified) {
|
||||
if (!session_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
modified = false;
|
||||
NvAPI_Status status;
|
||||
@@ -285,10 +285,9 @@ namespace nvprefs {
|
||||
|
||||
info_message(std::wstring(L"Removed PREFERRED_PSTATE for ") + sunshine_application_path);
|
||||
}
|
||||
}
|
||||
else if (status != NVAPI_OK ||
|
||||
setting.settingLocation != NVDRS_CURRENT_PROFILE_LOCATION ||
|
||||
setting.u32CurrentValue != PREFERRED_PSTATE_PREFER_MAX) {
|
||||
} else if (status != NVAPI_OK ||
|
||||
setting.settingLocation != NVDRS_CURRENT_PROFILE_LOCATION ||
|
||||
setting.u32CurrentValue != PREFERRED_PSTATE_PREFER_MAX) {
|
||||
// Set power setting if needed
|
||||
setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
|
||||
@@ -21,26 +21,19 @@ namespace nvprefs {
|
||||
public:
|
||||
~driver_settings_t();
|
||||
|
||||
bool
|
||||
init();
|
||||
bool init();
|
||||
|
||||
void
|
||||
destroy();
|
||||
void destroy();
|
||||
|
||||
bool
|
||||
load_settings();
|
||||
bool load_settings();
|
||||
|
||||
bool
|
||||
save_settings();
|
||||
bool save_settings();
|
||||
|
||||
bool
|
||||
restore_global_profile_to_undo(const undo_data_t &undo_data);
|
||||
bool restore_global_profile_to_undo(const undo_data_t &undo_data);
|
||||
|
||||
bool
|
||||
check_and_modify_global_profile(std::optional<undo_data_t> &undo_data);
|
||||
bool check_and_modify_global_profile(std::optional<undo_data_t> &undo_data);
|
||||
|
||||
bool
|
||||
check_and_modify_application_profile(bool &modified);
|
||||
bool check_and_modify_application_profile(bool &modified);
|
||||
|
||||
private:
|
||||
NvDRSSessionHandle session_handle = 0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @file src/platform/windows/nvprefs/nvapi_opensource_wrapper.cpp
|
||||
* @brief Definitions for the NVAPI wrapper.
|
||||
*/
|
||||
// standard library headers
|
||||
// standard includes
|
||||
#include <map>
|
||||
|
||||
// local includes
|
||||
@@ -17,9 +17,8 @@ namespace {
|
||||
std::map<const char *, void *> interfaces;
|
||||
HMODULE dll = NULL;
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
NvAPI_Status
|
||||
call_interface(const char *name, Args... args) {
|
||||
template<typename Func, typename... Args>
|
||||
NvAPI_Status call_interface(const char *name, Args... args) {
|
||||
auto func = (Func *) interfaces[name];
|
||||
|
||||
if (!func) {
|
||||
@@ -38,7 +37,9 @@ extern void *__cdecl nvapi_QueryInterface(NvU32 id);
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_Initialize() {
|
||||
if (dll) return NVAPI_OK;
|
||||
if (dll) {
|
||||
return NVAPI_OK;
|
||||
}
|
||||
|
||||
#ifdef _WIN64
|
||||
auto dll_name = "nvapi64.dll";
|
||||
@@ -59,8 +60,7 @@ NvAPI_Initialize() {
|
||||
return NVAPI_LIBRARY_NOT_FOUND;
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_Unload() {
|
||||
NVAPI_INTERFACE NvAPI_Unload() {
|
||||
if (dll) {
|
||||
interfaces.clear();
|
||||
FreeLibrary(dll);
|
||||
@@ -69,69 +69,56 @@ NvAPI_Unload() {
|
||||
return NVAPI_OK;
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_GetErrorMessage(NvAPI_Status nr, NvAPI_ShortString szDesc) {
|
||||
NVAPI_INTERFACE NvAPI_GetErrorMessage(NvAPI_Status nr, NvAPI_ShortString szDesc) {
|
||||
return call_interface<decltype(NvAPI_GetErrorMessage)>("NvAPI_GetErrorMessage", nr, szDesc);
|
||||
}
|
||||
|
||||
// This is only a subset of NvAPI_DRS_* functions, more can be added if needed
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateSession(NvDRSSessionHandle *phSession) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_CreateSession(NvDRSSessionHandle *phSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateSession)>("NvAPI_DRS_CreateSession", phSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_DestroySession(NvDRSSessionHandle hSession) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_DestroySession(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_DestroySession)>("NvAPI_DRS_DestroySession", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_LoadSettings(NvDRSSessionHandle hSession) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_LoadSettings(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_LoadSettings)>("NvAPI_DRS_LoadSettings", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_SaveSettings(NvDRSSessionHandle hSession) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_SaveSettings(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_SaveSettings)>("NvAPI_DRS_SaveSettings", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateProfile(NvDRSSessionHandle hSession, NVDRS_PROFILE *pProfileInfo, NvDRSProfileHandle *phProfile) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_CreateProfile(NvDRSSessionHandle hSession, NVDRS_PROFILE *pProfileInfo, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateProfile)>("NvAPI_DRS_CreateProfile", hSession, pProfileInfo, phProfile);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_FindProfileByName(NvDRSSessionHandle hSession, NvAPI_UnicodeString profileName, NvDRSProfileHandle *phProfile) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_FindProfileByName(NvDRSSessionHandle hSession, NvAPI_UnicodeString profileName, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_FindProfileByName)>("NvAPI_DRS_FindProfileByName", hSession, profileName, phProfile);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateApplication(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_APPLICATION *pApplication) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_CreateApplication(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_APPLICATION *pApplication) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateApplication)>("NvAPI_DRS_CreateApplication", hSession, hProfile, pApplication);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetApplicationInfo(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvAPI_UnicodeString appName, NVDRS_APPLICATION *pApplication) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_GetApplicationInfo(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvAPI_UnicodeString appName, NVDRS_APPLICATION *pApplication) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetApplicationInfo)>("NvAPI_DRS_GetApplicationInfo", hSession, hProfile, appName, pApplication);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_SetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_SETTING *pSetting) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_SetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_SETTING *pSetting) {
|
||||
return call_interface<decltype(NvAPI_DRS_SetSetting)>("NvAPI_DRS_SetSetting", hSession, hProfile, pSetting);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId, NVDRS_SETTING *pSetting) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_GetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId, NVDRS_SETTING *pSetting) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetSetting)>("NvAPI_DRS_GetSetting", hSession, hProfile, settingId, pSetting);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_DeleteProfileSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_DeleteProfileSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId) {
|
||||
return call_interface<decltype(NvAPI_DRS_DeleteProfileSetting)>("NvAPI_DRS_DeleteProfileSetting", hSession, hProfile, settingId);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetBaseProfile(NvDRSSessionHandle hSession, NvDRSProfileHandle *phProfile) {
|
||||
NVAPI_INTERFACE NvAPI_DRS_GetBaseProfile(NvDRSSessionHandle hSession, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetBaseProfile)>("NvAPI_DRS_GetBaseProfile", hSession, phProfile);
|
||||
}
|
||||
|
||||
@@ -2,37 +2,32 @@
|
||||
* @file src/platform/windows/nvprefs/nvprefs_common.cpp
|
||||
* @brief Definitions for common nvidia preferences.
|
||||
*/
|
||||
// local includes
|
||||
// this include
|
||||
#include "nvprefs_common.h"
|
||||
#include "src/logging.h"
|
||||
|
||||
// read user override preferences from global sunshine config
|
||||
// local includes
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
void
|
||||
info_message(const std::wstring &message) {
|
||||
void info_message(const std::wstring &message) {
|
||||
BOOST_LOG(info) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
info_message(const std::string &message) {
|
||||
void info_message(const std::string &message) {
|
||||
BOOST_LOG(info) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
error_message(const std::wstring &message) {
|
||||
void error_message(const std::wstring &message) {
|
||||
BOOST_LOG(error) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
error_message(const std::string &message) {
|
||||
void error_message(const std::string &message) {
|
||||
BOOST_LOG(error) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
nvprefs_options
|
||||
get_nvprefs_options() {
|
||||
nvprefs_options get_nvprefs_options() {
|
||||
nvprefs_options options;
|
||||
options.opengl_vulkan_on_dxgi = config::video.nv_opengl_vulkan_on_dxgi;
|
||||
options.sunshine_high_power_mode = config::video.nv_sunshine_high_power_mode;
|
||||
|
||||
@@ -4,57 +4,51 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// sunshine utility header for generic smart pointers
|
||||
#include "src/utility.h"
|
||||
|
||||
// winapi headers
|
||||
// platform includes
|
||||
// disable clang-format header reordering
|
||||
// clang-format off
|
||||
#include <windows.h>
|
||||
#include <aclapi.h>
|
||||
// clang-format on
|
||||
|
||||
// local includes
|
||||
#include "src/utility.h"
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
struct safe_handle: public util::safe_ptr_v2<void, BOOL, CloseHandle> {
|
||||
using util::safe_ptr_v2<void, BOOL, CloseHandle>::safe_ptr_v2;
|
||||
explicit
|
||||
operator bool() const {
|
||||
|
||||
explicit operator bool() const {
|
||||
auto handle = get();
|
||||
return handle != NULL && handle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
};
|
||||
|
||||
struct safe_hlocal_deleter {
|
||||
void
|
||||
operator()(void *p) {
|
||||
void operator()(void *p) {
|
||||
LocalFree(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
using safe_hlocal = util::uniq_ptr<std::remove_pointer_t<T>, safe_hlocal_deleter>;
|
||||
|
||||
using safe_sid = util::safe_ptr_v2<void, PVOID, FreeSid>;
|
||||
|
||||
void
|
||||
info_message(const std::wstring &message);
|
||||
void info_message(const std::wstring &message);
|
||||
|
||||
void
|
||||
info_message(const std::string &message);
|
||||
void info_message(const std::string &message);
|
||||
|
||||
void
|
||||
error_message(const std::wstring &message);
|
||||
void error_message(const std::wstring &message);
|
||||
|
||||
void
|
||||
error_message(const std::string &message);
|
||||
void error_message(const std::string &message);
|
||||
|
||||
struct nvprefs_options {
|
||||
bool opengl_vulkan_on_dxgi = true;
|
||||
bool sunshine_high_power_mode = true;
|
||||
};
|
||||
|
||||
nvprefs_options
|
||||
get_nvprefs_options();
|
||||
nvprefs_options get_nvprefs_options();
|
||||
|
||||
} // namespace nvprefs
|
||||
|
||||
@@ -39,8 +39,7 @@ namespace nvprefs {
|
||||
unload();
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::load() {
|
||||
bool nvprefs_interface::load() {
|
||||
if (!pimpl->loaded) {
|
||||
// Check %ProgramData% variable, need it for storing undo file
|
||||
wchar_t program_data_env[MAX_PATH];
|
||||
@@ -61,8 +60,7 @@ namespace nvprefs {
|
||||
return pimpl->loaded;
|
||||
}
|
||||
|
||||
void
|
||||
nvprefs_interface::unload() {
|
||||
void nvprefs_interface::unload() {
|
||||
if (pimpl->loaded) {
|
||||
// Unload dynamically loaded nvapi library
|
||||
pimpl->driver_settings.destroy();
|
||||
@@ -70,9 +68,10 @@ namespace nvprefs {
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::restore_from_and_delete_undo_file_if_exists() {
|
||||
if (!pimpl->loaded) return false;
|
||||
bool nvprefs_interface::restore_from_and_delete_undo_file_if_exists() {
|
||||
if (!pimpl->loaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for undo file from previous improper termination
|
||||
bool access_denied = false;
|
||||
@@ -82,12 +81,10 @@ namespace nvprefs {
|
||||
if (auto undo_data = undo_file->read_undo_data()) {
|
||||
if (pimpl->driver_settings.restore_global_profile_to_undo(*undo_data) && pimpl->driver_settings.save_settings()) {
|
||||
info_message("Restored global profile settings from undo file - deleting the file");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
error_message("Failed to restore global profile settings from undo file, deleting the file anyway");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
error_message("Coulnd't read undo file, deleting the file anyway");
|
||||
}
|
||||
|
||||
@@ -95,8 +92,7 @@ namespace nvprefs {
|
||||
error_message("Couldn't delete undo file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (access_denied) {
|
||||
} else if (access_denied) {
|
||||
error_message("Couldn't open undo file from previous improper termination, or confirm that there's no such file");
|
||||
return false;
|
||||
}
|
||||
@@ -104,43 +100,41 @@ namespace nvprefs {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::modify_application_profile() {
|
||||
if (!pimpl->loaded) return false;
|
||||
bool nvprefs_interface::modify_application_profile() {
|
||||
if (!pimpl->loaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Modify and save sunshine.exe application profile settings, if needed
|
||||
bool modified = false;
|
||||
if (!pimpl->driver_settings.check_and_modify_application_profile(modified)) {
|
||||
error_message("Failed to modify application profile settings");
|
||||
return false;
|
||||
}
|
||||
else if (modified) {
|
||||
} else if (modified) {
|
||||
if (pimpl->driver_settings.save_settings()) {
|
||||
info_message("Modified application profile settings");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
error_message("Couldn't save application profile settings");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
info_message("No need to modify application profile settings");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::modify_global_profile() {
|
||||
if (!pimpl->loaded) return false;
|
||||
bool nvprefs_interface::modify_global_profile() {
|
||||
if (!pimpl->loaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Modify but not save global profile settings, if needed
|
||||
std::optional<undo_data_t> undo_data;
|
||||
if (!pimpl->driver_settings.check_and_modify_global_profile(undo_data)) {
|
||||
error_message("Couldn't modify global profile settings");
|
||||
return false;
|
||||
}
|
||||
else if (!undo_data) {
|
||||
} else if (!undo_data) {
|
||||
info_message("No need to modify global profile settings");
|
||||
return true;
|
||||
}
|
||||
@@ -166,8 +160,7 @@ namespace nvprefs {
|
||||
if (pimpl->undo_data) {
|
||||
// Merge undo data if settings has been modified externally since our last modification
|
||||
pimpl->undo_data->merge(*undo_data);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pimpl->undo_data = undo_data;
|
||||
}
|
||||
|
||||
@@ -198,14 +191,14 @@ namespace nvprefs {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::owning_undo_file() {
|
||||
bool nvprefs_interface::owning_undo_file() {
|
||||
return pimpl->undo_file.has_value();
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::restore_global_profile() {
|
||||
if (!pimpl->loaded || !pimpl->undo_data || !pimpl->undo_file) return false;
|
||||
bool nvprefs_interface::restore_global_profile() {
|
||||
if (!pimpl->loaded || !pimpl->undo_data || !pimpl->undo_file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore global profile settings with undo data
|
||||
if (pimpl->driver_settings.restore_global_profile_to_undo(*pimpl->undo_data) &&
|
||||
@@ -217,8 +210,7 @@ namespace nvprefs {
|
||||
}
|
||||
pimpl->undo_data = std::nullopt;
|
||||
pimpl->undo_file = std::nullopt;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
error_message("Couldn't restore global profile settings");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// standard library headers
|
||||
// standard includes
|
||||
#include <memory>
|
||||
|
||||
namespace nvprefs {
|
||||
@@ -14,26 +14,19 @@ namespace nvprefs {
|
||||
nvprefs_interface();
|
||||
~nvprefs_interface();
|
||||
|
||||
bool
|
||||
load();
|
||||
bool load();
|
||||
|
||||
void
|
||||
unload();
|
||||
void unload();
|
||||
|
||||
bool
|
||||
restore_from_and_delete_undo_file_if_exists();
|
||||
bool restore_from_and_delete_undo_file_if_exists();
|
||||
|
||||
bool
|
||||
modify_application_profile();
|
||||
bool modify_application_profile();
|
||||
|
||||
bool
|
||||
modify_global_profile();
|
||||
bool modify_global_profile();
|
||||
|
||||
bool
|
||||
owning_undo_file();
|
||||
bool owning_undo_file();
|
||||
|
||||
bool
|
||||
restore_global_profile();
|
||||
bool restore_global_profile();
|
||||
|
||||
private:
|
||||
struct impl;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @file src/platform/windows/nvprefs/undo_data.cpp
|
||||
* @brief Definitions for undoing changes to nvidia preferences.
|
||||
*/
|
||||
// external includes
|
||||
// lib includes
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
// local includes
|
||||
@@ -17,54 +17,46 @@ namespace nlohmann {
|
||||
using data_t = nvprefs::undo_data_t::data_t;
|
||||
using opengl_swapchain_t = data_t::opengl_swapchain_t;
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct adl_serializer<std::optional<T>> {
|
||||
static void
|
||||
to_json(json &j, const std::optional<T> &opt) {
|
||||
static void to_json(json &j, const std::optional<T> &opt) {
|
||||
if (opt == std::nullopt) {
|
||||
j = nullptr;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
j = *opt;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
from_json(const json &j, std::optional<T> &opt) {
|
||||
static void from_json(const json &j, std::optional<T> &opt) {
|
||||
if (j.is_null()) {
|
||||
opt = std::nullopt;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
opt = j.template get<T>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct adl_serializer<data_t> {
|
||||
static void
|
||||
to_json(json &j, const data_t &data) {
|
||||
j = json { { "opengl_swapchain", data.opengl_swapchain } };
|
||||
static void to_json(json &j, const data_t &data) {
|
||||
j = json {{"opengl_swapchain", data.opengl_swapchain}};
|
||||
}
|
||||
|
||||
static void
|
||||
from_json(const json &j, data_t &data) {
|
||||
static void from_json(const json &j, data_t &data) {
|
||||
j.at("opengl_swapchain").get_to(data.opengl_swapchain);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct adl_serializer<opengl_swapchain_t> {
|
||||
static void
|
||||
to_json(json &j, const opengl_swapchain_t &opengl_swapchain) {
|
||||
static void to_json(json &j, const opengl_swapchain_t &opengl_swapchain) {
|
||||
j = json {
|
||||
{ "our_value", opengl_swapchain.our_value },
|
||||
{ "undo_value", opengl_swapchain.undo_value }
|
||||
{"our_value", opengl_swapchain.our_value},
|
||||
{"undo_value", opengl_swapchain.undo_value}
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
from_json(const json &j, opengl_swapchain_t &opengl_swapchain) {
|
||||
static void from_json(const json &j, opengl_swapchain_t &opengl_swapchain) {
|
||||
j.at("our_value").get_to(opengl_swapchain.our_value);
|
||||
j.at("undo_value").get_to(opengl_swapchain.undo_value);
|
||||
}
|
||||
@@ -73,46 +65,39 @@ namespace nlohmann {
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
void
|
||||
undo_data_t::set_opengl_swapchain(uint32_t our_value, std::optional<uint32_t> undo_value) {
|
||||
void undo_data_t::set_opengl_swapchain(uint32_t our_value, std::optional<uint32_t> undo_value) {
|
||||
data.opengl_swapchain = data_t::opengl_swapchain_t {
|
||||
our_value,
|
||||
undo_value
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<undo_data_t::data_t::opengl_swapchain_t>
|
||||
undo_data_t::get_opengl_swapchain() const {
|
||||
std::optional<undo_data_t::data_t::opengl_swapchain_t> undo_data_t::get_opengl_swapchain() const {
|
||||
return data.opengl_swapchain;
|
||||
}
|
||||
|
||||
std::string
|
||||
undo_data_t::write() const {
|
||||
std::string undo_data_t::write() const {
|
||||
try {
|
||||
// Keep this assignment otherwise data will be treated as an array due to
|
||||
// initializer list shenanigangs.
|
||||
const json json_data = data;
|
||||
return json_data.dump();
|
||||
}
|
||||
catch (const std::exception &err) {
|
||||
error_message(std::string { "failed to serialize json data" });
|
||||
} catch (const std::exception &err) {
|
||||
error_message(std::string {"failed to serialize json data"});
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
undo_data_t::read(const std::vector<char> &buffer) {
|
||||
void undo_data_t::read(const std::vector<char> &buffer) {
|
||||
try {
|
||||
data = json::parse(std::begin(buffer), std::end(buffer));
|
||||
}
|
||||
catch (const std::exception &err) {
|
||||
error_message(std::string { "failed to parse json data: " } + err.what());
|
||||
} catch (const std::exception &err) {
|
||||
error_message(std::string {"failed to parse json data: "} + err.what());
|
||||
data = {};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
undo_data_t::merge(const undo_data_t &newer_data) {
|
||||
void undo_data_t::merge(const undo_data_t &newer_data) {
|
||||
const auto &swapchain_data = newer_data.get_opengl_swapchain();
|
||||
if (swapchain_data) {
|
||||
set_opengl_swapchain(swapchain_data->our_value, swapchain_data->undo_value);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// standard library headers
|
||||
// standard includes
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -23,20 +23,15 @@ namespace nvprefs {
|
||||
std::optional<opengl_swapchain_t> opengl_swapchain;
|
||||
};
|
||||
|
||||
void
|
||||
set_opengl_swapchain(uint32_t our_value, std::optional<uint32_t> undo_value);
|
||||
void set_opengl_swapchain(uint32_t our_value, std::optional<uint32_t> undo_value);
|
||||
|
||||
std::optional<data_t::opengl_swapchain_t>
|
||||
get_opengl_swapchain() const;
|
||||
std::optional<data_t::opengl_swapchain_t> get_opengl_swapchain() const;
|
||||
|
||||
std::string
|
||||
write() const;
|
||||
std::string write() const;
|
||||
|
||||
void
|
||||
read(const std::vector<char> &buffer);
|
||||
void read(const std::vector<char> &buffer);
|
||||
|
||||
void
|
||||
merge(const undo_data_t &newer_data);
|
||||
void merge(const undo_data_t &newer_data);
|
||||
|
||||
private:
|
||||
data_t data;
|
||||
|
||||
@@ -9,13 +9,14 @@ namespace {
|
||||
|
||||
using namespace nvprefs;
|
||||
|
||||
DWORD
|
||||
relax_permissions(HANDLE file_handle) {
|
||||
DWORD relax_permissions(HANDLE file_handle) {
|
||||
PACL old_dacl = nullptr;
|
||||
|
||||
safe_hlocal<PSECURITY_DESCRIPTOR> sd;
|
||||
DWORD status = GetSecurityInfo(file_handle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &old_dacl, nullptr, &sd);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
if (status != ERROR_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
safe_sid users_sid;
|
||||
SID_IDENTIFIER_AUTHORITY nt_authorithy = SECURITY_NT_AUTHORITY;
|
||||
@@ -32,10 +33,14 @@ namespace {
|
||||
|
||||
safe_hlocal<PACL> new_dacl;
|
||||
status = SetEntriesInAcl(1, &ea, old_dacl, &new_dacl);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
if (status != ERROR_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = SetSecurityInfo(file_handle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, new_dacl.get(), nullptr);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
if (status != ERROR_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -44,23 +49,20 @@ namespace {
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
std::optional<undo_file_t>
|
||||
undo_file_t::open_existing_file(std::filesystem::path file_path, bool &access_denied) {
|
||||
std::optional<undo_file_t> undo_file_t::open_existing_file(std::filesystem::path file_path, bool &access_denied) {
|
||||
undo_file_t file;
|
||||
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_READ | DELETE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
|
||||
if (file.file_handle) {
|
||||
access_denied = false;
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto last_error = GetLastError();
|
||||
access_denied = (last_error != ERROR_FILE_NOT_FOUND && last_error != ERROR_PATH_NOT_FOUND);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<undo_file_t>
|
||||
undo_file_t::create_new_file(std::filesystem::path file_path) {
|
||||
std::optional<undo_file_t> undo_file_t::create_new_file(std::filesystem::path file_path) {
|
||||
undo_file_t file;
|
||||
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_WRITE | STANDARD_RIGHTS_ALL, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL));
|
||||
|
||||
@@ -70,35 +72,34 @@ namespace nvprefs {
|
||||
error_message("Failed to relax permissions on undo file");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
undo_file_t::delete_file() {
|
||||
if (!file_handle) return false;
|
||||
bool undo_file_t::delete_file() {
|
||||
if (!file_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE_DISPOSITION_INFO delete_file_info = { TRUE };
|
||||
FILE_DISPOSITION_INFO delete_file_info = {TRUE};
|
||||
if (SetFileInformationByHandle(file_handle.get(), FileDispositionInfo, &delete_file_info, sizeof(delete_file_info))) {
|
||||
file_handle.reset();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
undo_file_t::write_undo_data(const undo_data_t &undo_data) {
|
||||
if (!file_handle) return false;
|
||||
bool undo_file_t::write_undo_data(const undo_data_t &undo_data) {
|
||||
if (!file_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string buffer;
|
||||
try {
|
||||
buffer = undo_data.write();
|
||||
}
|
||||
catch (...) {
|
||||
} catch (...) {
|
||||
error_message("Couldn't serialize undo data");
|
||||
return false;
|
||||
}
|
||||
@@ -121,9 +122,10 @@ namespace nvprefs {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<undo_data_t>
|
||||
undo_file_t::read_undo_data() {
|
||||
if (!file_handle) return std::nullopt;
|
||||
std::optional<undo_data_t> undo_file_t::read_undo_data() {
|
||||
if (!file_handle) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
LARGE_INTEGER file_size;
|
||||
if (!GetFileSizeEx(file_handle.get(), &file_size)) {
|
||||
@@ -146,8 +148,7 @@ namespace nvprefs {
|
||||
undo_data_t undo_data;
|
||||
try {
|
||||
undo_data.read(buffer);
|
||||
}
|
||||
catch (...) {
|
||||
} catch (...) {
|
||||
error_message("Couldn't parse undo file");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// standard library headers
|
||||
// standard includes
|
||||
#include <filesystem>
|
||||
|
||||
// local includes
|
||||
@@ -15,20 +15,15 @@ namespace nvprefs {
|
||||
|
||||
class undo_file_t {
|
||||
public:
|
||||
static std::optional<undo_file_t>
|
||||
open_existing_file(std::filesystem::path file_path, bool &access_denied);
|
||||
static std::optional<undo_file_t> open_existing_file(std::filesystem::path file_path, bool &access_denied);
|
||||
|
||||
static std::optional<undo_file_t>
|
||||
create_new_file(std::filesystem::path file_path);
|
||||
static std::optional<undo_file_t> create_new_file(std::filesystem::path file_path);
|
||||
|
||||
bool
|
||||
delete_file();
|
||||
bool delete_file();
|
||||
|
||||
bool
|
||||
write_undo_data(const undo_data_t &undo_data);
|
||||
bool write_undo_data(const undo_data_t &undo_data);
|
||||
|
||||
std::optional<undo_data_t>
|
||||
read_undo_data();
|
||||
std::optional<undo_data_t> read_undo_data();
|
||||
|
||||
private:
|
||||
undo_file_t() = default;
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
* @file src/platform/windows/publish.cpp
|
||||
* @brief Definitions for Windows mDNS service registration.
|
||||
*/
|
||||
// platform includes
|
||||
// winsock2.h must be included before windows.h
|
||||
// clang-format off
|
||||
#include <winsock2.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
// clang-format on
|
||||
#include <windns.h>
|
||||
#include <winerror.h>
|
||||
|
||||
// local includes
|
||||
#include "misc.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
@@ -17,7 +20,7 @@
|
||||
#include "src/platform/common.h"
|
||||
#include "src/thread_safe.h"
|
||||
|
||||
#define _FN(x, ret, args) \
|
||||
#define _FN(x, ret, args) \
|
||||
typedef ret(*x##_fn) args; \
|
||||
static x##_fn x
|
||||
|
||||
@@ -28,69 +31,69 @@ using namespace std::literals;
|
||||
|
||||
extern "C" {
|
||||
#ifndef __MINGW32__
|
||||
constexpr auto DNS_REQUEST_PENDING = 9506L;
|
||||
constexpr auto DNS_QUERY_REQUEST_VERSION1 = 0x1;
|
||||
constexpr auto DNS_QUERY_RESULTS_VERSION1 = 0x1;
|
||||
constexpr auto DNS_REQUEST_PENDING = 9506L;
|
||||
constexpr auto DNS_QUERY_REQUEST_VERSION1 = 0x1;
|
||||
constexpr auto DNS_QUERY_RESULTS_VERSION1 = 0x1;
|
||||
#endif
|
||||
|
||||
#define SERVICE_DOMAIN "local"
|
||||
|
||||
constexpr auto SERVICE_TYPE_DOMAIN = SV(SERVICE_TYPE "." SERVICE_DOMAIN);
|
||||
constexpr auto SERVICE_TYPE_DOMAIN = SV(SERVICE_TYPE "." SERVICE_DOMAIN);
|
||||
|
||||
#ifndef __MINGW32__
|
||||
typedef struct _DNS_SERVICE_INSTANCE {
|
||||
LPWSTR pszInstanceName;
|
||||
LPWSTR pszHostName;
|
||||
typedef struct _DNS_SERVICE_INSTANCE {
|
||||
LPWSTR pszInstanceName;
|
||||
LPWSTR pszHostName;
|
||||
|
||||
IP4_ADDRESS *ip4Address;
|
||||
IP6_ADDRESS *ip6Address;
|
||||
IP4_ADDRESS *ip4Address;
|
||||
IP6_ADDRESS *ip6Address;
|
||||
|
||||
WORD wPort;
|
||||
WORD wPriority;
|
||||
WORD wWeight;
|
||||
WORD wPort;
|
||||
WORD wPriority;
|
||||
WORD wWeight;
|
||||
|
||||
// Property list
|
||||
DWORD dwPropertyCount;
|
||||
// Property list
|
||||
DWORD dwPropertyCount;
|
||||
|
||||
PWSTR *keys;
|
||||
PWSTR *values;
|
||||
PWSTR *keys;
|
||||
PWSTR *values;
|
||||
|
||||
DWORD dwInterfaceIndex;
|
||||
} DNS_SERVICE_INSTANCE, *PDNS_SERVICE_INSTANCE;
|
||||
DWORD dwInterfaceIndex;
|
||||
} DNS_SERVICE_INSTANCE, *PDNS_SERVICE_INSTANCE;
|
||||
#endif
|
||||
|
||||
typedef VOID WINAPI
|
||||
DNS_SERVICE_REGISTER_COMPLETE(
|
||||
_In_ DWORD Status,
|
||||
_In_ PVOID pQueryContext,
|
||||
_In_ PDNS_SERVICE_INSTANCE pInstance);
|
||||
typedef VOID WINAPI
|
||||
DNS_SERVICE_REGISTER_COMPLETE(
|
||||
_In_ DWORD Status,
|
||||
_In_ PVOID pQueryContext,
|
||||
_In_ PDNS_SERVICE_INSTANCE pInstance
|
||||
);
|
||||
|
||||
typedef DNS_SERVICE_REGISTER_COMPLETE *PDNS_SERVICE_REGISTER_COMPLETE;
|
||||
typedef DNS_SERVICE_REGISTER_COMPLETE *PDNS_SERVICE_REGISTER_COMPLETE;
|
||||
|
||||
#ifndef __MINGW32__
|
||||
typedef struct _DNS_SERVICE_CANCEL {
|
||||
PVOID reserved;
|
||||
} DNS_SERVICE_CANCEL, *PDNS_SERVICE_CANCEL;
|
||||
typedef struct _DNS_SERVICE_CANCEL {
|
||||
PVOID reserved;
|
||||
} DNS_SERVICE_CANCEL, *PDNS_SERVICE_CANCEL;
|
||||
|
||||
typedef struct _DNS_SERVICE_REGISTER_REQUEST {
|
||||
ULONG Version;
|
||||
ULONG InterfaceIndex;
|
||||
PDNS_SERVICE_INSTANCE pServiceInstance;
|
||||
PDNS_SERVICE_REGISTER_COMPLETE pRegisterCompletionCallback;
|
||||
PVOID pQueryContext;
|
||||
HANDLE hCredentials;
|
||||
BOOL unicastEnabled;
|
||||
} DNS_SERVICE_REGISTER_REQUEST, *PDNS_SERVICE_REGISTER_REQUEST;
|
||||
typedef struct _DNS_SERVICE_REGISTER_REQUEST {
|
||||
ULONG Version;
|
||||
ULONG InterfaceIndex;
|
||||
PDNS_SERVICE_INSTANCE pServiceInstance;
|
||||
PDNS_SERVICE_REGISTER_COMPLETE pRegisterCompletionCallback;
|
||||
PVOID pQueryContext;
|
||||
HANDLE hCredentials;
|
||||
BOOL unicastEnabled;
|
||||
} DNS_SERVICE_REGISTER_REQUEST, *PDNS_SERVICE_REGISTER_REQUEST;
|
||||
#endif
|
||||
|
||||
_FN(_DnsServiceFreeInstance, VOID, (_In_ PDNS_SERVICE_INSTANCE pInstance));
|
||||
_FN(_DnsServiceDeRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _Inout_opt_ PDNS_SERVICE_CANCEL pCancel));
|
||||
_FN(_DnsServiceRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _Inout_opt_ PDNS_SERVICE_CANCEL pCancel));
|
||||
_FN(_DnsServiceFreeInstance, VOID, (_In_ PDNS_SERVICE_INSTANCE pInstance));
|
||||
_FN(_DnsServiceDeRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _Inout_opt_ PDNS_SERVICE_CANCEL pCancel));
|
||||
_FN(_DnsServiceRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _Inout_opt_ PDNS_SERVICE_CANCEL pCancel));
|
||||
} /* extern "C" */
|
||||
|
||||
namespace platf::publish {
|
||||
VOID WINAPI
|
||||
register_cb(DWORD status, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) {
|
||||
VOID WINAPI register_cb(DWORD status, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) {
|
||||
auto alarm = (safe::alarm_t<PDNS_SERVICE_INSTANCE>::element_type *) pQueryContext;
|
||||
|
||||
if (status) {
|
||||
@@ -100,11 +103,10 @@ namespace platf::publish {
|
||||
alarm->ring(pInstance);
|
||||
}
|
||||
|
||||
static int
|
||||
service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) {
|
||||
static int service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) {
|
||||
auto alarm = safe::make_alarm<PDNS_SERVICE_INSTANCE>();
|
||||
|
||||
std::wstring domain { SERVICE_TYPE_DOMAIN.data(), SERVICE_TYPE_DOMAIN.size() };
|
||||
std::wstring domain {SERVICE_TYPE_DOMAIN.data(), SERVICE_TYPE_DOMAIN.size()};
|
||||
|
||||
auto hostname = platf::get_host_name();
|
||||
auto name = from_utf8(net::mdns_instance_name(hostname) + '.') + domain;
|
||||
@@ -124,8 +126,8 @@ namespace platf::publish {
|
||||
// Most clients aren't strictly checking TXT record compliance with RFC 1035,
|
||||
// but Apple's mDNS resolver does and rejects the entire answer if an invalid
|
||||
// TXT record is present.
|
||||
PWCHAR keys[] = { nullptr };
|
||||
PWCHAR values[] = { nullptr };
|
||||
PWCHAR keys[] = {nullptr};
|
||||
PWCHAR values[] = {nullptr};
|
||||
instance.dwPropertyCount = 1;
|
||||
instance.keys = keys;
|
||||
instance.values = values;
|
||||
@@ -144,8 +146,7 @@ namespace platf::publish {
|
||||
print_status("DnsServiceRegister()"sv, status);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
status = _DnsServiceDeRegister(&req, nullptr);
|
||||
if (status != DNS_REQUEST_PENDING) {
|
||||
print_status("DnsServiceDeRegister()"sv, status);
|
||||
@@ -159,8 +160,7 @@ namespace platf::publish {
|
||||
if (enable) {
|
||||
// Store this instance for later deregistration
|
||||
existing_instance = registered_instance;
|
||||
}
|
||||
else if (registered_instance) {
|
||||
} else if (registered_instance) {
|
||||
// Deregistration was successful
|
||||
_DnsServiceFreeInstance(registered_instance);
|
||||
existing_instance = nullptr;
|
||||
@@ -196,8 +196,7 @@ namespace platf::publish {
|
||||
PDNS_SERVICE_INSTANCE existing_instance;
|
||||
};
|
||||
|
||||
int
|
||||
load_funcs(HMODULE handle) {
|
||||
int load_funcs(HMODULE handle) {
|
||||
auto fg = util::fail_guard([handle]() {
|
||||
FreeLibrary(handle);
|
||||
});
|
||||
@@ -215,8 +214,7 @@ namespace platf::publish {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<::platf::deinit_t>
|
||||
start() {
|
||||
std::unique_ptr<::platf::deinit_t> start() {
|
||||
HMODULE handle = LoadLibrary("dnsapi.dll");
|
||||
|
||||
if (!handle || load_funcs(handle)) {
|
||||
|
||||
Reference in New Issue
Block a user