style: adjust clang-format rules (#2186)
Co-authored-by: Vithorio Polten <reach@vithor.io>
This commit is contained in:
parent
f57aee9025
commit
c2420427b1
158 changed files with 8704 additions and 9944 deletions
|
|
@ -6,27 +6,34 @@
|
|||
# Generated from CLion C/C++ Code Style settings
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AlignOperands: Align
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlignTrailingComments: false
|
||||
AlwaysBreakAfterReturnType: All
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
BreakBeforeBraces: Custom
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BracedInitializerIndentWidth: 2
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterExternBlock: true
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
|
|
@ -36,39 +43,75 @@ BraceWrapping:
|
|||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: true
|
||||
BreakArrays: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 2
|
||||
Cpp11BracedListStyle: true
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: Always
|
||||
ExperimentalAutoDetectBinPacking: true
|
||||
FixNamespaceComments: true
|
||||
IncludeBlocks: Regroup
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: true
|
||||
IndentCaseLabels: true
|
||||
IndentExternBlock: Indent
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: BeforeHash
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: true
|
||||
InsertBraces: true
|
||||
InsertNewlineAtEOF: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
LineEnding: LF
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PackConstructorInitializers: Never
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 1
|
||||
PenaltyBreakString: 1
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 100000000
|
||||
PointerAlignment: Right
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
RemoveSemicolon: false
|
||||
SeparateDefinitionBlocks: Always
|
||||
SortIncludes: CaseInsensitive
|
||||
SortUsingDeclarations: Lexicographic
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeCtorInitializerColon: false
|
||||
SpaceBeforeInheritanceColon: false
|
||||
SpaceBeforeJsonColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Maximum: 3
|
||||
Minimum: 1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 2
|
||||
Cpp11BracedListStyle: false
|
||||
UseTab: Never
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ directories = [
|
|||
'src',
|
||||
'tests',
|
||||
'tools',
|
||||
os.path.join('third-party', 'glad'),
|
||||
os.path.join('third-party', 'nvfbc'),
|
||||
]
|
||||
file_types = [
|
||||
'cpp',
|
||||
'cu',
|
||||
'h',
|
||||
'hpp',
|
||||
'm',
|
||||
'mm'
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,16 +2,18 @@
|
|||
* @file src/audio.cpp
|
||||
* @brief Definitions for audio capture and encoding.
|
||||
*/
|
||||
// standard includes
|
||||
#include <thread>
|
||||
|
||||
// lib includes
|
||||
#include <opus/opus_multistream.h>
|
||||
|
||||
#include "platform/common.h"
|
||||
|
||||
// local includes
|
||||
#include "audio.h"
|
||||
#include "config.h"
|
||||
#include "globals.h"
|
||||
#include "logging.h"
|
||||
#include "platform/common.h"
|
||||
#include "thread_safe.h"
|
||||
#include "utility.h"
|
||||
|
||||
|
|
@ -20,15 +22,11 @@ namespace audio {
|
|||
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
|
||||
using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<float>>>;
|
||||
|
||||
static int
|
||||
start_audio_control(audio_ctx_t &ctx);
|
||||
static void
|
||||
stop_audio_control(audio_ctx_t &);
|
||||
static void
|
||||
apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms);
|
||||
static int start_audio_control(audio_ctx_t &ctx);
|
||||
static void stop_audio_control(audio_ctx_t &);
|
||||
static void apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms);
|
||||
|
||||
int
|
||||
map_stream(int channels, bool quality);
|
||||
int map_stream(int channels, bool quality);
|
||||
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
|
||||
|
|
@ -85,8 +83,7 @@ namespace audio {
|
|||
},
|
||||
};
|
||||
|
||||
void
|
||||
encodeThread(sample_queue_t samples, config_t config, void *channel_data) {
|
||||
void encodeThread(sample_queue_t samples, config_t config, void *channel_data) {
|
||||
auto packets = mail::man->queue<packet_t>(mail::audio_packets);
|
||||
auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])];
|
||||
if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) {
|
||||
|
|
@ -96,14 +93,15 @@ namespace audio {
|
|||
// Encoding takes place on this thread
|
||||
platf::adjust_thread_priority(platf::thread_priority_e::high);
|
||||
|
||||
opus_t opus { opus_multistream_encoder_create(
|
||||
opus_t opus {opus_multistream_encoder_create(
|
||||
stream.sampleRate,
|
||||
stream.channelCount,
|
||||
stream.streams,
|
||||
stream.coupledStreams,
|
||||
stream.mapping,
|
||||
OPUS_APPLICATION_RESTRICTED_LOWDELAY,
|
||||
nullptr) };
|
||||
nullptr
|
||||
)};
|
||||
|
||||
opus_multistream_encoder_ctl(opus.get(), OPUS_SET_BITRATE(stream.bitrate));
|
||||
opus_multistream_encoder_ctl(opus.get(), OPUS_SET_VBR(0));
|
||||
|
|
@ -114,7 +112,7 @@ namespace audio {
|
|||
|
||||
auto frame_size = config.packetDuration * stream.sampleRate / 1000;
|
||||
while (auto sample = samples->pop()) {
|
||||
buffer_t packet { 1400 };
|
||||
buffer_t packet {1400};
|
||||
|
||||
int bytes = opus_multistream_encode_float(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size());
|
||||
if (bytes < 0) {
|
||||
|
|
@ -129,8 +127,7 @@ namespace audio {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
capture(safe::mail_t mail, config_t config, void *channel_data) {
|
||||
void capture(safe::mail_t mail, config_t config, void *channel_data) {
|
||||
auto shutdown_event = mail->event<bool>(mail::shutdown);
|
||||
auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])];
|
||||
if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) {
|
||||
|
|
@ -204,7 +201,7 @@ namespace audio {
|
|||
platf::adjust_thread_priority(platf::thread_priority_e::critical);
|
||||
|
||||
auto samples = std::make_shared<sample_queue_t::element_type>(30);
|
||||
std::thread thread { encodeThread, samples, config, channel_data };
|
||||
std::thread thread {encodeThread, samples, config, channel_data};
|
||||
|
||||
auto fg = util::fail_guard([&]() {
|
||||
samples->stop();
|
||||
|
|
@ -243,14 +240,12 @@ namespace audio {
|
|||
}
|
||||
}
|
||||
|
||||
audio_ctx_ref_t
|
||||
get_audio_ctx_ref() {
|
||||
static auto control_shared { safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control) };
|
||||
audio_ctx_ref_t get_audio_ctx_ref() {
|
||||
static auto control_shared {safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control)};
|
||||
return control_shared.ref();
|
||||
}
|
||||
|
||||
bool
|
||||
is_audio_ctx_sink_available(const audio_ctx_t &ctx) {
|
||||
bool is_audio_ctx_sink_available(const audio_ctx_t &ctx) {
|
||||
if (!ctx.control) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -263,8 +258,7 @@ namespace audio {
|
|||
return ctx.control->is_sink_available(sink);
|
||||
}
|
||||
|
||||
int
|
||||
map_stream(int channels, bool quality) {
|
||||
int map_stream(int channels, bool quality) {
|
||||
int shift = quality ? 1 : 0;
|
||||
switch (channels) {
|
||||
case 2:
|
||||
|
|
@ -277,8 +271,7 @@ namespace audio {
|
|||
return STEREO;
|
||||
}
|
||||
|
||||
int
|
||||
start_audio_control(audio_ctx_t &ctx) {
|
||||
int start_audio_control(audio_ctx_t &ctx) {
|
||||
auto fg = util::fail_guard([]() {
|
||||
BOOST_LOG(warning) << "There will be no audio"sv;
|
||||
});
|
||||
|
|
@ -305,8 +298,7 @@ namespace audio {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
stop_audio_control(audio_ctx_t &ctx) {
|
||||
void stop_audio_control(audio_ctx_t &ctx) {
|
||||
// restore audio-sink if applicable
|
||||
if (!ctx.restore_sink) {
|
||||
return;
|
||||
|
|
@ -320,8 +312,7 @@ namespace audio {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms) {
|
||||
void apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms) {
|
||||
stream.channelCount = params.channelCount;
|
||||
stream.streams = params.streams;
|
||||
stream.coupledStreams = params.coupledStreams;
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ namespace audio {
|
|||
using packet_t = std::pair<void *, buffer_t>;
|
||||
using audio_ctx_ref_t = safe::shared_t<audio_ctx_t>::ptr_t;
|
||||
|
||||
void
|
||||
capture(safe::mail_t mail, config_t config, void *channel_data);
|
||||
void capture(safe::mail_t mail, config_t config, void *channel_data);
|
||||
|
||||
/**
|
||||
* @brief Get the reference to the audio context.
|
||||
|
|
@ -84,8 +83,7 @@ namespace audio {
|
|||
* audio_ctx_ref_t audio = get_audio_ctx_ref()
|
||||
* @examples_end
|
||||
*/
|
||||
audio_ctx_ref_t
|
||||
get_audio_ctx_ref();
|
||||
audio_ctx_ref_t get_audio_ctx_ref();
|
||||
|
||||
/**
|
||||
* @brief Check if the audio sink held by audio context is available.
|
||||
|
|
@ -101,6 +99,5 @@ namespace audio {
|
|||
* return false;
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
is_audio_ctx_sink_available(const audio_ctx_t &ctx);
|
||||
bool is_audio_ctx_sink_available(const audio_ctx_t &ctx);
|
||||
} // namespace audio
|
||||
|
|
|
|||
36
src/cbs.cpp
36
src/cbs.cpp
|
|
@ -3,6 +3,7 @@
|
|||
* @brief Definitions for FFmpeg Coded Bitstream API.
|
||||
*/
|
||||
extern "C" {
|
||||
// lib includes
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavcodec/cbs_h264.h>
|
||||
#include <libavcodec/cbs_h265.h>
|
||||
|
|
@ -10,14 +11,15 @@ extern "C" {
|
|||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
// local includes
|
||||
#include "cbs.h"
|
||||
#include "logging.h"
|
||||
#include "utility.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace cbs {
|
||||
void
|
||||
close(CodedBitstreamContext *c) {
|
||||
void close(CodedBitstreamContext *c) {
|
||||
ff_cbs_close(&c);
|
||||
}
|
||||
|
||||
|
|
@ -36,8 +38,7 @@ namespace cbs {
|
|||
std::fill_n((std::uint8_t *) this, sizeof(*this), 0);
|
||||
}
|
||||
|
||||
frag_t &
|
||||
operator=(frag_t &&o) {
|
||||
frag_t &operator=(frag_t &&o) {
|
||||
std::copy((std::uint8_t *) &o, (std::uint8_t *) (&o + 1), (std::uint8_t *) this);
|
||||
|
||||
o.data = nullptr;
|
||||
|
|
@ -53,12 +54,11 @@ namespace cbs {
|
|||
}
|
||||
};
|
||||
|
||||
util::buffer_t<std::uint8_t>
|
||||
write(cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
util::buffer_t<std::uint8_t> write(cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
cbs::frag_t frag;
|
||||
auto err = ff_cbs_insert_unit_content(&frag, -1, nal, uh, nullptr);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
|
||||
BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
|
|
@ -66,29 +66,27 @@ namespace cbs {
|
|||
|
||||
err = ff_cbs_write_fragment_data(cbs_ctx.get(), &frag);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
|
||||
BOOST_LOG(error) << "Could not write fragment data: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// frag.data_size * 8 - frag.data_bit_padding == bits in fragment
|
||||
util::buffer_t<std::uint8_t> data { frag.data_size };
|
||||
util::buffer_t<std::uint8_t> data {frag.data_size};
|
||||
std::copy_n(frag.data, frag.data_size, std::begin(data));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
util::buffer_t<std::uint8_t>
|
||||
write(std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
util::buffer_t<std::uint8_t> write(std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
cbs::ctx_t cbs_ctx;
|
||||
ff_cbs_init(&cbs_ctx, codec_id, nullptr);
|
||||
|
||||
return write(cbs_ctx, nal, uh, codec_id);
|
||||
}
|
||||
|
||||
h264_t
|
||||
make_sps_h264(const AVCodecContext *avctx, const AVPacket *packet) {
|
||||
h264_t make_sps_h264(const AVCodecContext *avctx, const AVPacket *packet) {
|
||||
cbs::ctx_t ctx;
|
||||
if (ff_cbs_init(&ctx, AV_CODEC_ID_H264, nullptr)) {
|
||||
return {};
|
||||
|
|
@ -98,7 +96,7 @@ namespace cbs {
|
|||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
|
|
@ -144,8 +142,7 @@ namespace cbs {
|
|||
};
|
||||
}
|
||||
|
||||
hevc_t
|
||||
make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) {
|
||||
hevc_t make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) {
|
||||
cbs::ctx_t ctx;
|
||||
if (ff_cbs_init(&ctx, AV_CODEC_ID_H265, nullptr)) {
|
||||
return {};
|
||||
|
|
@ -155,7 +152,7 @@ namespace cbs {
|
|||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
|
|
@ -222,8 +219,7 @@ namespace cbs {
|
|||
* It then checks if the SPS->VUI (Video Usability Information) is present in the active SPS of the packet.
|
||||
* This is done for both H264 and H265 codecs.
|
||||
*/
|
||||
bool
|
||||
validate_sps(const AVPacket *packet, int codec_id) {
|
||||
bool validate_sps(const AVPacket *packet, int codec_id) {
|
||||
cbs::ctx_t ctx;
|
||||
if (ff_cbs_init(&ctx, (AVCodecID) codec_id, nullptr)) {
|
||||
return false;
|
||||
|
|
@ -233,7 +229,7 @@ namespace cbs {
|
|||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return false;
|
||||
|
|
|
|||
10
src/cbs.h
10
src/cbs.h
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// local includes
|
||||
#include "utility.h"
|
||||
|
||||
struct AVPacket;
|
||||
|
|
@ -25,10 +26,8 @@ namespace cbs {
|
|||
nal_t sps;
|
||||
};
|
||||
|
||||
hevc_t
|
||||
make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet);
|
||||
h264_t
|
||||
make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet);
|
||||
hevc_t make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet);
|
||||
h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet);
|
||||
|
||||
/**
|
||||
* @brief Validates the Sequence Parameter Set (SPS) of a given packet.
|
||||
|
|
@ -36,6 +35,5 @@ namespace cbs {
|
|||
* @param codec_id The ID of the codec used (either AV_CODEC_ID_H264 or AV_CODEC_ID_H265).
|
||||
* @return True if the SPS->VUI is present in the active SPS of the packet, false otherwise.
|
||||
*/
|
||||
bool
|
||||
validate_sps(const AVPacket *packet, int codec_id);
|
||||
bool validate_sps(const AVPacket *packet, int codec_id);
|
||||
} // namespace cbs
|
||||
|
|
|
|||
363
src/config.cpp
363
src/config.cpp
|
|
@ -2,6 +2,7 @@
|
|||
* @file src/config.cpp
|
||||
* @brief Definitions for the configuration of Sunshine.
|
||||
*/
|
||||
// standard includes
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
|
@ -11,21 +12,22 @@
|
|||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
// lib includes
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
// local includes
|
||||
#include "config.h"
|
||||
#include "entry_handler.h"
|
||||
#include "file_handler.h"
|
||||
#include "logging.h"
|
||||
#include "nvhttp.h"
|
||||
#include "platform/common.h"
|
||||
#include "rtsp.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include "platform/common.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
|
@ -43,15 +45,21 @@ using namespace std::literals;
|
|||
#define CERTIFICATE_FILE CA_DIR "/cacert.pem"
|
||||
|
||||
#define APPS_JSON_PATH platf::appdata().string() + "/apps.json"
|
||||
|
||||
namespace config {
|
||||
|
||||
namespace nv {
|
||||
|
||||
nvenc::nvenc_two_pass
|
||||
twopass_from_view(const std::string_view &preset) {
|
||||
if (preset == "disabled") return nvenc::nvenc_two_pass::disabled;
|
||||
if (preset == "quarter_res") return nvenc::nvenc_two_pass::quarter_resolution;
|
||||
if (preset == "full_res") return nvenc::nvenc_two_pass::full_resolution;
|
||||
nvenc::nvenc_two_pass twopass_from_view(const std::string_view &preset) {
|
||||
if (preset == "disabled") {
|
||||
return nvenc::nvenc_two_pass::disabled;
|
||||
}
|
||||
if (preset == "quarter_res") {
|
||||
return nvenc::nvenc_two_pass::quarter_resolution;
|
||||
}
|
||||
if (preset == "full_res") {
|
||||
return nvenc::nvenc_two_pass::full_resolution;
|
||||
}
|
||||
BOOST_LOG(warning) << "config: unknown nvenc_twopass value: " << preset;
|
||||
return nvenc::nvenc_two_pass::quarter_resolution;
|
||||
}
|
||||
|
|
@ -178,11 +186,11 @@ namespace config {
|
|||
cavlc = AMF_VIDEO_ENCODER_CALV ///< CAVLC
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
quality_from_view(const std::string_view &quality_type, const std::optional<int>(&original)) {
|
||||
template<class T>
|
||||
std::optional<int> quality_from_view(const std::string_view &quality_type, const std::optional<int>(&original)) {
|
||||
#define _CONVERT_(x) \
|
||||
if (quality_type == #x##sv) return (int) T::x
|
||||
if (quality_type == #x##sv) \
|
||||
return (int) T::x
|
||||
_CONVERT_(balanced);
|
||||
_CONVERT_(quality);
|
||||
_CONVERT_(speed);
|
||||
|
|
@ -190,11 +198,11 @@ namespace config {
|
|||
return original;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
rc_from_view(const std::string_view &rc, const std::optional<int>(&original)) {
|
||||
template<class T>
|
||||
std::optional<int> rc_from_view(const std::string_view &rc, const std::optional<int>(&original)) {
|
||||
#define _CONVERT_(x) \
|
||||
if (rc == #x##sv) return (int) T::x
|
||||
if (rc == #x##sv) \
|
||||
return (int) T::x
|
||||
_CONVERT_(cbr);
|
||||
_CONVERT_(cqp);
|
||||
_CONVERT_(vbr_latency);
|
||||
|
|
@ -203,11 +211,11 @@ namespace config {
|
|||
return original;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::optional<int>
|
||||
usage_from_view(const std::string_view &usage, const std::optional<int>(&original)) {
|
||||
template<class T>
|
||||
std::optional<int> usage_from_view(const std::string_view &usage, const std::optional<int>(&original)) {
|
||||
#define _CONVERT_(x) \
|
||||
if (usage == #x##sv) return (int) T::x
|
||||
if (usage == #x##sv) \
|
||||
return (int) T::x
|
||||
_CONVERT_(lowlatency);
|
||||
_CONVERT_(lowlatency_high_quality);
|
||||
_CONVERT_(transcoding);
|
||||
|
|
@ -217,11 +225,16 @@ namespace config {
|
|||
return original;
|
||||
}
|
||||
|
||||
int
|
||||
coder_from_view(const std::string_view &coder) {
|
||||
if (coder == "auto"sv) return _auto;
|
||||
if (coder == "cabac"sv || coder == "ac"sv) return cabac;
|
||||
if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
|
||||
int coder_from_view(const std::string_view &coder) {
|
||||
if (coder == "auto"sv) {
|
||||
return _auto;
|
||||
}
|
||||
if (coder == "cabac"sv || coder == "ac"sv) {
|
||||
return cabac;
|
||||
}
|
||||
if (coder == "cavlc"sv || coder == "vlc"sv) {
|
||||
return cavlc;
|
||||
}
|
||||
|
||||
return _auto;
|
||||
}
|
||||
|
|
@ -244,10 +257,10 @@ namespace config {
|
|||
disabled = false ///< Disabled
|
||||
};
|
||||
|
||||
std::optional<int>
|
||||
preset_from_view(const std::string_view &preset) {
|
||||
std::optional<int> preset_from_view(const std::string_view &preset) {
|
||||
#define _CONVERT_(x) \
|
||||
if (preset == #x##sv) return x
|
||||
if (preset == #x##sv) \
|
||||
return x
|
||||
_CONVERT_(veryslow);
|
||||
_CONVERT_(slower);
|
||||
_CONVERT_(slow);
|
||||
|
|
@ -259,11 +272,16 @@ namespace config {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<int>
|
||||
coder_from_view(const std::string_view &coder) {
|
||||
if (coder == "auto"sv) return _auto;
|
||||
if (coder == "cabac"sv || coder == "ac"sv) return disabled;
|
||||
if (coder == "cavlc"sv || coder == "vlc"sv) return enabled;
|
||||
std::optional<int> coder_from_view(const std::string_view &coder) {
|
||||
if (coder == "auto"sv) {
|
||||
return _auto;
|
||||
}
|
||||
if (coder == "cabac"sv || coder == "ac"sv) {
|
||||
return disabled;
|
||||
}
|
||||
if (coder == "cavlc"sv || coder == "vlc"sv) {
|
||||
return enabled;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -277,32 +295,40 @@ namespace config {
|
|||
cavlc ///< CAVLC
|
||||
};
|
||||
|
||||
int
|
||||
coder_from_view(const std::string_view &coder) {
|
||||
if (coder == "auto"sv) return _auto;
|
||||
if (coder == "cabac"sv || coder == "ac"sv) return cabac;
|
||||
if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc;
|
||||
int coder_from_view(const std::string_view &coder) {
|
||||
if (coder == "auto"sv) {
|
||||
return _auto;
|
||||
}
|
||||
if (coder == "cabac"sv || coder == "ac"sv) {
|
||||
return cabac;
|
||||
}
|
||||
if (coder == "cavlc"sv || coder == "vlc"sv) {
|
||||
return cavlc;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
allow_software_from_view(const std::string_view &software) {
|
||||
if (software == "allowed"sv || software == "forced") return 1;
|
||||
int allow_software_from_view(const std::string_view &software) {
|
||||
if (software == "allowed"sv || software == "forced") {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
force_software_from_view(const std::string_view &software) {
|
||||
if (software == "forced") return 1;
|
||||
int force_software_from_view(const std::string_view &software) {
|
||||
if (software == "forced") {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
rt_from_view(const std::string_view &rt) {
|
||||
if (rt == "disabled" || rt == "off" || rt == "0") return 0;
|
||||
int rt_from_view(const std::string_view &rt) {
|
||||
if (rt == "disabled" || rt == "off" || rt == "0") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -310,10 +336,10 @@ namespace config {
|
|||
} // namespace vt
|
||||
|
||||
namespace sw {
|
||||
int
|
||||
svtav1_preset_from_view(const std::string_view &preset) {
|
||||
int svtav1_preset_from_view(const std::string_view &preset) {
|
||||
#define _CONVERT_(x, y) \
|
||||
if (preset == #x##sv) return y
|
||||
if (preset == #x##sv) \
|
||||
return y
|
||||
_CONVERT_(veryslow, 1);
|
||||
_CONVERT_(slower, 2);
|
||||
_CONVERT_(slow, 4);
|
||||
|
|
@ -329,10 +355,10 @@ namespace config {
|
|||
} // namespace sw
|
||||
|
||||
namespace dd {
|
||||
video_t::dd_t::config_option_e
|
||||
config_option_from_view(const std::string_view value) {
|
||||
video_t::dd_t::config_option_e config_option_from_view(const std::string_view value) {
|
||||
#define _CONVERT_(x) \
|
||||
if (value == #x##sv) return video_t::dd_t::config_option_e::x
|
||||
if (value == #x##sv) \
|
||||
return video_t::dd_t::config_option_e::x
|
||||
_CONVERT_(disabled);
|
||||
_CONVERT_(verify_only);
|
||||
_CONVERT_(ensure_active);
|
||||
|
|
@ -342,10 +368,10 @@ namespace config {
|
|||
return video_t::dd_t::config_option_e::disabled; // Default to this if value is invalid
|
||||
}
|
||||
|
||||
video_t::dd_t::resolution_option_e
|
||||
resolution_option_from_view(const std::string_view value) {
|
||||
video_t::dd_t::resolution_option_e resolution_option_from_view(const std::string_view value) {
|
||||
#define _CONVERT_2_ARG_(str, val) \
|
||||
if (value == #str##sv) return video_t::dd_t::resolution_option_e::val
|
||||
if (value == #str##sv) \
|
||||
return video_t::dd_t::resolution_option_e::val
|
||||
#define _CONVERT_(x) _CONVERT_2_ARG_(x, x)
|
||||
_CONVERT_(disabled);
|
||||
_CONVERT_2_ARG_(auto, automatic);
|
||||
|
|
@ -355,10 +381,10 @@ namespace config {
|
|||
return video_t::dd_t::resolution_option_e::disabled; // Default to this if value is invalid
|
||||
}
|
||||
|
||||
video_t::dd_t::refresh_rate_option_e
|
||||
refresh_rate_option_from_view(const std::string_view value) {
|
||||
video_t::dd_t::refresh_rate_option_e refresh_rate_option_from_view(const std::string_view value) {
|
||||
#define _CONVERT_2_ARG_(str, val) \
|
||||
if (value == #str##sv) return video_t::dd_t::refresh_rate_option_e::val
|
||||
if (value == #str##sv) \
|
||||
return video_t::dd_t::refresh_rate_option_e::val
|
||||
#define _CONVERT_(x) _CONVERT_2_ARG_(x, x)
|
||||
_CONVERT_(disabled);
|
||||
_CONVERT_2_ARG_(auto, automatic);
|
||||
|
|
@ -368,10 +394,10 @@ namespace config {
|
|||
return video_t::dd_t::refresh_rate_option_e::disabled; // Default to this if value is invalid
|
||||
}
|
||||
|
||||
video_t::dd_t::hdr_option_e
|
||||
hdr_option_from_view(const std::string_view value) {
|
||||
video_t::dd_t::hdr_option_e hdr_option_from_view(const std::string_view value) {
|
||||
#define _CONVERT_2_ARG_(str, val) \
|
||||
if (value == #str##sv) return video_t::dd_t::hdr_option_e::val
|
||||
if (value == #str##sv) \
|
||||
return video_t::dd_t::hdr_option_e::val
|
||||
#define _CONVERT_(x) _CONVERT_2_ARG_(x, x)
|
||||
_CONVERT_(disabled);
|
||||
_CONVERT_2_ARG_(auto, automatic);
|
||||
|
|
@ -380,9 +406,8 @@ namespace config {
|
|||
return video_t::dd_t::hdr_option_e::disabled; // Default to this if value is invalid
|
||||
}
|
||||
|
||||
video_t::dd_t::mode_remapping_t
|
||||
mode_remapping_from_view(const std::string_view value) {
|
||||
const auto parse_entry_list { [](const auto &entry_list, auto &output_field) {
|
||||
video_t::dd_t::mode_remapping_t mode_remapping_from_view(const std::string_view value) {
|
||||
const auto parse_entry_list {[](const auto &entry_list, auto &output_field) {
|
||||
for (auto &[_, entry] : entry_list) {
|
||||
auto requested_resolution = entry.template get_optional<std::string>("requested_resolution"s);
|
||||
auto requested_fps = entry.template get_optional<std::string>("requested_fps"s);
|
||||
|
|
@ -393,9 +418,10 @@ namespace config {
|
|||
requested_resolution.value_or(""),
|
||||
requested_fps.value_or(""),
|
||||
final_resolution.value_or(""),
|
||||
final_refresh_rate.value_or("") });
|
||||
final_refresh_rate.value_or("")
|
||||
});
|
||||
}
|
||||
} };
|
||||
}};
|
||||
|
||||
// We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it.
|
||||
std::stringstream json_stream;
|
||||
|
|
@ -515,13 +541,13 @@ namespace config {
|
|||
|
||||
input_t input {
|
||||
{
|
||||
{ 0x10, 0xA0 },
|
||||
{ 0x11, 0xA2 },
|
||||
{ 0x12, 0xA4 },
|
||||
{0x10, 0xA0},
|
||||
{0x11, 0xA2},
|
||||
{0x12, 0xA4},
|
||||
},
|
||||
-1ms, // back_button_timeout
|
||||
500ms, // key_repeat_delay
|
||||
std::chrono::duration<double> { 1 / 24.9 }, // key_repeat_period
|
||||
std::chrono::duration<double> {1 / 24.9}, // key_repeat_period
|
||||
|
||||
{
|
||||
platf::supported_gamepads(nullptr).front().name.data(),
|
||||
|
|
@ -556,23 +582,19 @@ namespace config {
|
|||
{}, // prep commands
|
||||
};
|
||||
|
||||
bool
|
||||
endline(char ch) {
|
||||
bool endline(char ch) {
|
||||
return ch == '\r' || ch == '\n';
|
||||
}
|
||||
|
||||
bool
|
||||
space_tab(char ch) {
|
||||
bool space_tab(char ch) {
|
||||
return ch == ' ' || ch == '\t';
|
||||
}
|
||||
|
||||
bool
|
||||
whitespace(char ch) {
|
||||
bool whitespace(char ch) {
|
||||
return space_tab(ch) || endline(ch);
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(const char *begin, const char *end) {
|
||||
std::string to_string(const char *begin, const char *end) {
|
||||
std::string result;
|
||||
|
||||
KITTY_WHILE_LOOP(auto pos = begin, pos != end, {
|
||||
|
|
@ -587,9 +609,8 @@ namespace config {
|
|||
return result;
|
||||
}
|
||||
|
||||
template <class It>
|
||||
It
|
||||
skip_list(It skipper, It end) {
|
||||
template<class It>
|
||||
It skip_list(It skipper, It end) {
|
||||
int stack = 1;
|
||||
while (skipper != end && stack) {
|
||||
if (*skipper == '[') {
|
||||
|
|
@ -608,7 +629,7 @@ namespace config {
|
|||
std::pair<
|
||||
std::string_view::const_iterator,
|
||||
std::optional<std::pair<std::string, std::string>>>
|
||||
parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) {
|
||||
parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) {
|
||||
begin = std::find_if_not(begin, end, whitespace);
|
||||
auto endl = std::find_if(begin, end, endline);
|
||||
auto endc = std::find(begin, endl, '#');
|
||||
|
|
@ -638,11 +659,11 @@ namespace config {
|
|||
|
||||
return std::make_pair(
|
||||
endl,
|
||||
std::make_pair(to_string(begin, end_name), to_string(begin_val, endl)));
|
||||
std::make_pair(to_string(begin, end_name), to_string(begin_val, endl))
|
||||
);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string>
|
||||
parse_config(const std::string_view &file_content) {
|
||||
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content) {
|
||||
std::unordered_map<std::string, std::string> vars;
|
||||
|
||||
auto pos = std::begin(file_content);
|
||||
|
|
@ -667,8 +688,7 @@ namespace config {
|
|||
return vars;
|
||||
}
|
||||
|
||||
void
|
||||
string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input) {
|
||||
void string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input) {
|
||||
auto it = vars.find(name);
|
||||
if (it == std::end(vars)) {
|
||||
return;
|
||||
|
|
@ -679,9 +699,8 @@ namespace config {
|
|||
vars.erase(it);
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
void
|
||||
generic_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, T &input, F &&f) {
|
||||
template<typename T, typename F>
|
||||
void generic_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, T &input, F &&f) {
|
||||
std::string tmp;
|
||||
string_f(vars, name, tmp);
|
||||
if (!tmp.empty()) {
|
||||
|
|
@ -689,8 +708,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
string_restricted_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input, const std::vector<std::string_view> &allowed_vals) {
|
||||
void string_restricted_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input, const std::vector<std::string_view> &allowed_vals) {
|
||||
std::string temp;
|
||||
string_f(vars, name, temp);
|
||||
|
||||
|
|
@ -702,8 +720,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
path_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, fs::path &input) {
|
||||
void path_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, fs::path &input) {
|
||||
// appdata needs to be retrieved once only
|
||||
static auto appdata = platf::appdata();
|
||||
|
||||
|
|
@ -727,8 +744,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
path_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input) {
|
||||
void path_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input) {
|
||||
fs::path temp = input;
|
||||
|
||||
path_f(vars, name, temp);
|
||||
|
|
@ -736,8 +752,7 @@ namespace config {
|
|||
input = temp.string();
|
||||
}
|
||||
|
||||
void
|
||||
int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &input) {
|
||||
void int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &input) {
|
||||
auto it = vars.find(name);
|
||||
|
||||
if (it == std::end(vars)) {
|
||||
|
|
@ -754,16 +769,14 @@ namespace config {
|
|||
// If that integer is in hexadecimal
|
||||
if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) {
|
||||
input = util::from_hex<int>(val.substr(2));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
input = util::from_view(val);
|
||||
}
|
||||
|
||||
vars.erase(it);
|
||||
}
|
||||
|
||||
void
|
||||
int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::optional<int> &input) {
|
||||
void int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::optional<int> &input) {
|
||||
auto it = vars.find(name);
|
||||
|
||||
if (it == std::end(vars)) {
|
||||
|
|
@ -780,17 +793,15 @@ namespace config {
|
|||
// If that integer is in hexadecimal
|
||||
if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) {
|
||||
input = util::from_hex<int>(val.substr(2));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
input = util::from_view(val);
|
||||
}
|
||||
|
||||
vars.erase(it);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void
|
||||
int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &input, F &&f) {
|
||||
template<class F>
|
||||
void int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &input, F &&f) {
|
||||
std::string tmp;
|
||||
string_f(vars, name, tmp);
|
||||
if (!tmp.empty()) {
|
||||
|
|
@ -798,9 +809,8 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void
|
||||
int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::optional<int> &input, F &&f) {
|
||||
template<class F>
|
||||
void int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::optional<int> &input, F &&f) {
|
||||
std::string tmp;
|
||||
string_f(vars, name, tmp);
|
||||
if (!tmp.empty()) {
|
||||
|
|
@ -808,8 +818,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
int_between_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &input, const std::pair<int, int> &range) {
|
||||
void int_between_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, int &input, const std::pair<int, int> &range) {
|
||||
int temp = input;
|
||||
|
||||
int_f(vars, name, temp);
|
||||
|
|
@ -820,9 +829,10 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
to_bool(std::string &boolean) {
|
||||
std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char) std::tolower(ch); });
|
||||
bool to_bool(std::string &boolean) {
|
||||
std::for_each(std::begin(boolean), std::end(boolean), [](char ch) {
|
||||
return (char) std::tolower(ch);
|
||||
});
|
||||
|
||||
return boolean == "true"sv ||
|
||||
boolean == "yes"sv ||
|
||||
|
|
@ -832,8 +842,7 @@ namespace config {
|
|||
(std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean));
|
||||
}
|
||||
|
||||
void
|
||||
bool_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, bool &input) {
|
||||
void bool_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, bool &input) {
|
||||
std::string tmp;
|
||||
string_f(vars, name, tmp);
|
||||
|
||||
|
|
@ -844,8 +853,7 @@ namespace config {
|
|||
input = to_bool(tmp);
|
||||
}
|
||||
|
||||
void
|
||||
double_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &input) {
|
||||
void double_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &input) {
|
||||
std::string tmp;
|
||||
string_f(vars, name, tmp);
|
||||
|
||||
|
|
@ -863,8 +871,7 @@ namespace config {
|
|||
input = val;
|
||||
}
|
||||
|
||||
void
|
||||
double_between_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &input, const std::pair<double, double> &range) {
|
||||
void double_between_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, double &input, const std::pair<double, double> &range) {
|
||||
double temp = input;
|
||||
|
||||
double_f(vars, name, temp);
|
||||
|
|
@ -875,8 +882,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
list_string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<std::string> &input) {
|
||||
void list_string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<std::string> &input) {
|
||||
std::string string;
|
||||
string_f(vars, name, string);
|
||||
|
||||
|
|
@ -900,15 +906,12 @@ namespace config {
|
|||
while (pos < std::cend(string)) {
|
||||
if (*pos == '[') {
|
||||
pos = skip_list(pos + 1, std::cend(string)) + 1;
|
||||
}
|
||||
else if (*pos == ']') {
|
||||
} else if (*pos == ']') {
|
||||
break;
|
||||
}
|
||||
else if (*pos == ',') {
|
||||
} else if (*pos == ',') {
|
||||
input.emplace_back(begin, pos);
|
||||
pos = begin = std::find_if_not(pos + 1, std::cend(string), whitespace);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
|
@ -918,8 +921,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
list_prep_cmd_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<prep_cmd_t> &input) {
|
||||
void list_prep_cmd_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<prep_cmd_t> &input) {
|
||||
std::string string;
|
||||
string_f(vars, name, string);
|
||||
|
||||
|
|
@ -945,8 +947,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
list_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<int> &input) {
|
||||
void list_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<int> &input) {
|
||||
std::vector<std::string> list;
|
||||
list_string_f(vars, name, list);
|
||||
|
||||
|
|
@ -972,16 +973,14 @@ namespace config {
|
|||
// If the integer is a hexadecimal
|
||||
if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) {
|
||||
tmp = util::from_hex<int>(val.substr(2));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
tmp = util::from_view(val);
|
||||
}
|
||||
input.emplace_back(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
map_int_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::unordered_map<int, int> &input) {
|
||||
void map_int_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::unordered_map<int, int> &input) {
|
||||
std::vector<int> list;
|
||||
list_int_f(vars, name, list);
|
||||
|
||||
|
|
@ -1000,8 +999,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
apply_flags(const char *line) {
|
||||
int apply_flags(const char *line) {
|
||||
int ret = 0;
|
||||
while (*line != '\0') {
|
||||
switch (*line) {
|
||||
|
|
@ -1028,8 +1026,7 @@ namespace config {
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> &
|
||||
get_supported_gamepad_options() {
|
||||
std::vector<std::string_view> &get_supported_gamepad_options() {
|
||||
const auto options = platf::supported_gamepads(nullptr);
|
||||
static std::vector<std::string_view> opts {};
|
||||
opts.reserve(options.size());
|
||||
|
|
@ -1039,8 +1036,7 @@ namespace config {
|
|||
return opts;
|
||||
}
|
||||
|
||||
void
|
||||
apply_config(std::unordered_map<std::string, std::string> &&vars) {
|
||||
void apply_config(std::unordered_map<std::string, std::string> &&vars) {
|
||||
if (!fs::exists(stream.file_apps.c_str())) {
|
||||
fs::copy_file(SUNSHINE_ASSETS_DIR "/apps.json", stream.file_apps);
|
||||
}
|
||||
|
|
@ -1050,8 +1046,8 @@ namespace config {
|
|||
}
|
||||
|
||||
int_f(vars, "qp", video.qp);
|
||||
int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 });
|
||||
int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 });
|
||||
int_between_f(vars, "hevc_mode", video.hevc_mode, {0, 3});
|
||||
int_between_f(vars, "av1_mode", video.av1_mode, {0, 3});
|
||||
int_f(vars, "min_threads", video.min_threads);
|
||||
string_f(vars, "sw_preset", video.sw.sw_preset);
|
||||
if (!video.sw.sw_preset.empty()) {
|
||||
|
|
@ -1059,8 +1055,8 @@ namespace config {
|
|||
}
|
||||
string_f(vars, "sw_tune", video.sw.sw_tune);
|
||||
|
||||
int_between_f(vars, "nvenc_preset", video.nv.quality_preset, { 1, 7 });
|
||||
int_between_f(vars, "nvenc_vbv_increase", video.nv.vbv_percentage_increase, { 0, 400 });
|
||||
int_between_f(vars, "nvenc_preset", video.nv.quality_preset, {1, 7});
|
||||
int_between_f(vars, "nvenc_vbv_increase", video.nv.vbv_percentage_increase, {0, 400});
|
||||
bool_f(vars, "nvenc_spatial_aq", video.nv.adaptive_quantization);
|
||||
generic_f(vars, "nvenc_twopass", video.nv.two_pass, nv::twopass_from_view);
|
||||
bool_f(vars, "nvenc_h264_cavlc", video.nv.h264_cavlc);
|
||||
|
|
@ -1131,15 +1127,15 @@ namespace config {
|
|||
generic_f(vars, "dd_hdr_option", video.dd.hdr_option, dd::hdr_option_from_view);
|
||||
{
|
||||
int value = -1;
|
||||
int_between_f(vars, "dd_config_revert_delay", value, { 0, std::numeric_limits<int>::max() });
|
||||
int_between_f(vars, "dd_config_revert_delay", value, {0, std::numeric_limits<int>::max()});
|
||||
if (value >= 0) {
|
||||
video.dd.config_revert_delay = std::chrono::milliseconds { value };
|
||||
video.dd.config_revert_delay = std::chrono::milliseconds {value};
|
||||
}
|
||||
}
|
||||
generic_f(vars, "dd_mode_remapping", video.dd.mode_remapping, dd::mode_remapping_from_view);
|
||||
bool_f(vars, "dd_wa_hdr_toggle", video.dd.wa.hdr_toggle);
|
||||
|
||||
int_between_f(vars, "min_fps_factor", video.min_fps_factor, { 1, 3 });
|
||||
int_between_f(vars, "min_fps_factor", video.min_fps_factor, {1, 3});
|
||||
|
||||
path_f(vars, "pkey", nvhttp.pkey);
|
||||
path_f(vars, "cert", nvhttp.cert);
|
||||
|
|
@ -1158,19 +1154,19 @@ namespace config {
|
|||
string_f(vars, "virtual_sink", audio.virtual_sink);
|
||||
bool_f(vars, "install_steam_audio_drivers", audio.install_steam_drivers);
|
||||
|
||||
string_restricted_f(vars, "origin_web_ui_allowed", nvhttp.origin_web_ui_allowed, { "pc"sv, "lan"sv, "wan"sv });
|
||||
string_restricted_f(vars, "origin_web_ui_allowed", nvhttp.origin_web_ui_allowed, {"pc"sv, "lan"sv, "wan"sv});
|
||||
|
||||
int to = -1;
|
||||
int_between_f(vars, "ping_timeout", to, { -1, std::numeric_limits<int>::max() });
|
||||
int_between_f(vars, "ping_timeout", to, {-1, std::numeric_limits<int>::max()});
|
||||
if (to != -1) {
|
||||
stream.ping_timeout = std::chrono::milliseconds(to);
|
||||
}
|
||||
|
||||
int_between_f(vars, "lan_encryption_mode", stream.lan_encryption_mode, { 0, 2 });
|
||||
int_between_f(vars, "wan_encryption_mode", stream.wan_encryption_mode, { 0, 2 });
|
||||
int_between_f(vars, "lan_encryption_mode", stream.lan_encryption_mode, {0, 2});
|
||||
int_between_f(vars, "wan_encryption_mode", stream.wan_encryption_mode, {0, 2});
|
||||
|
||||
path_f(vars, "file_apps", stream.file_apps);
|
||||
int_between_f(vars, "fec_percentage", stream.fec_percentage, { 1, 255 });
|
||||
int_between_f(vars, "fec_percentage", stream.fec_percentage, {1, 255});
|
||||
|
||||
map_int_int_f(vars, "keybindings"s, input.keybindings);
|
||||
|
||||
|
|
@ -1187,20 +1183,20 @@ namespace config {
|
|||
int_f(vars, "back_button_timeout", to);
|
||||
|
||||
if (to > std::numeric_limits<int>::min()) {
|
||||
input.back_button_timeout = std::chrono::milliseconds { to };
|
||||
input.back_button_timeout = std::chrono::milliseconds {to};
|
||||
}
|
||||
|
||||
double repeat_frequency { 0 };
|
||||
double_between_f(vars, "key_repeat_frequency", repeat_frequency, { 0, std::numeric_limits<double>::max() });
|
||||
double repeat_frequency {0};
|
||||
double_between_f(vars, "key_repeat_frequency", repeat_frequency, {0, std::numeric_limits<double>::max()});
|
||||
|
||||
if (repeat_frequency > 0) {
|
||||
config::input.key_repeat_period = std::chrono::duration<double> { 1 / repeat_frequency };
|
||||
config::input.key_repeat_period = std::chrono::duration<double> {1 / repeat_frequency};
|
||||
}
|
||||
|
||||
to = -1;
|
||||
int_f(vars, "key_repeat_delay", to);
|
||||
if (to >= 0) {
|
||||
input.key_repeat_delay = std::chrono::milliseconds { to };
|
||||
input.key_repeat_delay = std::chrono::milliseconds {to};
|
||||
}
|
||||
|
||||
string_restricted_f(vars, "gamepad"s, input.gamepad, get_supported_gamepad_options());
|
||||
|
|
@ -1220,10 +1216,10 @@ namespace config {
|
|||
bool_f(vars, "notify_pre_releases", sunshine.notify_pre_releases);
|
||||
|
||||
int port = sunshine.port;
|
||||
int_between_f(vars, "port"s, port, { 1024 + nvhttp::PORT_HTTPS, 65535 - rtsp_stream::RTSP_SETUP_PORT });
|
||||
int_between_f(vars, "port"s, port, {1024 + nvhttp::PORT_HTTPS, 65535 - rtsp_stream::RTSP_SETUP_PORT});
|
||||
sunshine.port = (std::uint16_t) port;
|
||||
|
||||
string_restricted_f(vars, "address_family", sunshine.address_family, { "ipv4"sv, "both"sv });
|
||||
string_restricted_f(vars, "address_family", sunshine.address_family, {"ipv4"sv, "both"sv});
|
||||
|
||||
bool upnp = false;
|
||||
bool_f(vars, "upnp"s, upnp);
|
||||
|
|
@ -1259,26 +1255,19 @@ namespace config {
|
|||
if (!log_level_string.empty()) {
|
||||
if (log_level_string == "verbose"sv) {
|
||||
sunshine.min_log_level = 0;
|
||||
}
|
||||
else if (log_level_string == "debug"sv) {
|
||||
} else if (log_level_string == "debug"sv) {
|
||||
sunshine.min_log_level = 1;
|
||||
}
|
||||
else if (log_level_string == "info"sv) {
|
||||
} else if (log_level_string == "info"sv) {
|
||||
sunshine.min_log_level = 2;
|
||||
}
|
||||
else if (log_level_string == "warning"sv) {
|
||||
} else if (log_level_string == "warning"sv) {
|
||||
sunshine.min_log_level = 3;
|
||||
}
|
||||
else if (log_level_string == "error"sv) {
|
||||
} else if (log_level_string == "error"sv) {
|
||||
sunshine.min_log_level = 4;
|
||||
}
|
||||
else if (log_level_string == "fatal"sv) {
|
||||
} else if (log_level_string == "fatal"sv) {
|
||||
sunshine.min_log_level = 5;
|
||||
}
|
||||
else if (log_level_string == "none"sv) {
|
||||
} else if (log_level_string == "none"sv) {
|
||||
sunshine.min_log_level = 6;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// accept digit directly
|
||||
auto val = log_level_string[0];
|
||||
if (val >= '0' && val < '7') {
|
||||
|
|
@ -1301,8 +1290,7 @@ namespace config {
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
parse(int argc, char *argv[]) {
|
||||
int parse(int argc, char *argv[]) {
|
||||
std::unordered_map<std::string, std::string> cmd_vars;
|
||||
#ifdef _WIN32
|
||||
bool shortcut_launch = false;
|
||||
|
|
@ -1319,8 +1307,7 @@ namespace config {
|
|||
#ifdef _WIN32
|
||||
else if (line == "--shortcut"sv) {
|
||||
shortcut_launch = true;
|
||||
}
|
||||
else if (line == "--shortcut-admin"sv) {
|
||||
} else if (line == "--shortcut-admin"sv) {
|
||||
service_admin_launch = true;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1336,15 +1323,13 @@ namespace config {
|
|||
logging::print_help(*argv);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto line_end = line + strlen(line);
|
||||
|
||||
auto pos = std::find(line, line_end, '=');
|
||||
if (pos == line_end) {
|
||||
sunshine.config_file = line;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
TUPLE_EL(var, 1, parse_option(line, line_end));
|
||||
if (!var) {
|
||||
logging::print_help(*argv);
|
||||
|
|
@ -1370,7 +1355,7 @@ namespace config {
|
|||
|
||||
// Create empty config file if it does not exist
|
||||
if (!fs::exists(sunshine.config_file)) {
|
||||
std::ofstream { sunshine.config_file };
|
||||
std::ofstream {sunshine.config_file};
|
||||
}
|
||||
|
||||
// Read config file
|
||||
|
|
@ -1385,11 +1370,9 @@ namespace config {
|
|||
// the path is incorrect or inaccessible.
|
||||
apply_config(std::move(vars));
|
||||
config_loaded = true;
|
||||
}
|
||||
catch (const std::filesystem::filesystem_error &err) {
|
||||
} catch (const std::filesystem::filesystem_error &err) {
|
||||
BOOST_LOG(fatal) << "Failed to apply config: "sv << err.what();
|
||||
}
|
||||
catch (const boost::filesystem::filesystem_error &err) {
|
||||
} catch (const boost::filesystem::filesystem_error &err) {
|
||||
BOOST_LOG(fatal) << "Failed to apply config: "sv << err.what();
|
||||
}
|
||||
|
||||
|
|
@ -1419,7 +1402,7 @@ namespace config {
|
|||
// Always return 1 to ensure Sunshine doesn't start normally
|
||||
return 1;
|
||||
}
|
||||
else if (shortcut_launch) {
|
||||
if (shortcut_launch) {
|
||||
if (!service_ctrl::is_service_running()) {
|
||||
// If the service isn't running, relaunch ourselves as admin to start it
|
||||
WCHAR executable[MAX_PATH];
|
||||
|
|
|
|||
23
src/config.h
23
src/config.h
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
|
@ -11,6 +12,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// local includes
|
||||
#include "nvenc/nvenc_config.h"
|
||||
|
||||
namespace config {
|
||||
|
|
@ -22,6 +24,7 @@ namespace config {
|
|||
int av1_mode;
|
||||
|
||||
int min_threads; // Minimum number of threads/slices for CPU encoding
|
||||
|
||||
struct {
|
||||
std::string sw_preset;
|
||||
std::string sw_tune;
|
||||
|
|
@ -204,17 +207,25 @@ namespace config {
|
|||
CONST_PIN, ///< Use "universal" pin
|
||||
FLAG_SIZE ///< Number of flags
|
||||
};
|
||||
}
|
||||
} // namespace flag
|
||||
|
||||
struct prep_cmd_t {
|
||||
prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd, bool &&elevated):
|
||||
do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)), elevated(std::move(elevated)) {}
|
||||
do_cmd(std::move(do_cmd)),
|
||||
undo_cmd(std::move(undo_cmd)),
|
||||
elevated(std::move(elevated)) {
|
||||
}
|
||||
|
||||
explicit prep_cmd_t(std::string &&do_cmd, bool &&elevated):
|
||||
do_cmd(std::move(do_cmd)), elevated(std::move(elevated)) {}
|
||||
do_cmd(std::move(do_cmd)),
|
||||
elevated(std::move(elevated)) {
|
||||
}
|
||||
|
||||
std::string do_cmd;
|
||||
std::string undo_cmd;
|
||||
bool elevated;
|
||||
};
|
||||
|
||||
struct sunshine_t {
|
||||
std::string locale;
|
||||
int min_log_level;
|
||||
|
|
@ -248,8 +259,6 @@ namespace config {
|
|||
extern input_t input;
|
||||
extern sunshine_t sunshine;
|
||||
|
||||
int
|
||||
parse(int argc, char *argv[]);
|
||||
std::unordered_map<std::string, std::string>
|
||||
parse_config(const std::string_view &file_content);
|
||||
int parse(int argc, char *argv[]);
|
||||
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content);
|
||||
} // namespace config
|
||||
|
|
|
|||
|
|
@ -6,25 +6,22 @@
|
|||
*/
|
||||
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
|
||||
#include "process.h"
|
||||
|
||||
// standard includes
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
|
||||
// lib includes
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/asio/ssl/context_base.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/xml_parser.hpp>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <Simple-Web-Server/crypto.hpp>
|
||||
#include <Simple-Web-Server/server_https.hpp>
|
||||
#include <boost/asio/ssl/context_base.hpp>
|
||||
|
||||
// local includes
|
||||
#include "config.h"
|
||||
#include "confighttp.h"
|
||||
#include "crypto.h"
|
||||
|
|
@ -36,6 +33,7 @@
|
|||
#include "network.h"
|
||||
#include "nvhttp.h"
|
||||
#include "platform/common.h"
|
||||
#include "process.h"
|
||||
#include "rtsp.h"
|
||||
#include "utility.h"
|
||||
#include "uuid.h"
|
||||
|
|
@ -62,8 +60,7 @@ namespace confighttp {
|
|||
* @brief Log the request details.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
print_req(const req_https_t &request) {
|
||||
void print_req(const req_https_t &request) {
|
||||
BOOST_LOG(debug) << "METHOD :: "sv << request->method;
|
||||
BOOST_LOG(debug) << "DESTINATION :: "sv << request->path;
|
||||
|
||||
|
|
@ -85,8 +82,7 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param output_tree The JSON tree to send.
|
||||
*/
|
||||
void
|
||||
send_response(resp_https_t response, const pt::ptree &output_tree) {
|
||||
void send_response(resp_https_t response, const pt::ptree &output_tree) {
|
||||
std::ostringstream data;
|
||||
pt::write_json(data, output_tree);
|
||||
response->write(data.str());
|
||||
|
|
@ -97,12 +93,11 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
send_unauthorized(resp_https_t response, req_https_t request) {
|
||||
void send_unauthorized(resp_https_t response, req_https_t request) {
|
||||
auto address = net::addr_to_normalized_string(request->remote_endpoint().address());
|
||||
BOOST_LOG(info) << "Web UI: ["sv << address << "] -- not authorized"sv;
|
||||
const SimpleWeb::CaseInsensitiveMultimap headers {
|
||||
{ "WWW-Authenticate", R"(Basic realm="Sunshine Gamestream Host", charset="UTF-8")" }
|
||||
{"WWW-Authenticate", R"(Basic realm="Sunshine Gamestream Host", charset="UTF-8")"}
|
||||
};
|
||||
response->write(SimpleWeb::StatusCode::client_error_unauthorized, headers);
|
||||
}
|
||||
|
|
@ -113,12 +108,11 @@ namespace confighttp {
|
|||
* @param request The HTTP request object.
|
||||
* @param path The path to redirect to.
|
||||
*/
|
||||
void
|
||||
send_redirect(resp_https_t response, req_https_t request, const char *path) {
|
||||
void send_redirect(resp_https_t response, req_https_t request, const char *path) {
|
||||
auto address = net::addr_to_normalized_string(request->remote_endpoint().address());
|
||||
BOOST_LOG(info) << "Web UI: ["sv << address << "] -- not authorized"sv;
|
||||
const SimpleWeb::CaseInsensitiveMultimap headers {
|
||||
{ "Location", path }
|
||||
{"Location", path}
|
||||
};
|
||||
response->write(SimpleWeb::StatusCode::redirection_temporary_redirect, headers);
|
||||
}
|
||||
|
|
@ -129,8 +123,7 @@ namespace confighttp {
|
|||
* @param request The HTTP request object.
|
||||
* @return True if the user is authenticated, false otherwise.
|
||||
*/
|
||||
bool
|
||||
authenticate(resp_https_t response, req_https_t request) {
|
||||
bool authenticate(resp_https_t response, req_https_t request) {
|
||||
auto address = net::addr_to_normalized_string(request->remote_endpoint().address());
|
||||
auto ip_type = net::from_address(address);
|
||||
|
||||
|
|
@ -180,8 +173,7 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
not_found(resp_https_t response, [[maybe_unused]] req_https_t request) {
|
||||
void not_found(resp_https_t response, [[maybe_unused]] req_https_t request) {
|
||||
constexpr SimpleWeb::StatusCode code = SimpleWeb::StatusCode::client_error_not_found;
|
||||
|
||||
pt::ptree tree;
|
||||
|
|
@ -203,8 +195,7 @@ namespace confighttp {
|
|||
* @param request The HTTP request object.
|
||||
* @param error_message The error message to include in the response.
|
||||
*/
|
||||
void
|
||||
bad_request(resp_https_t response, [[maybe_unused]] req_https_t request, const std::string &error_message = "Bad Request") {
|
||||
void bad_request(resp_https_t response, [[maybe_unused]] req_https_t request, const std::string &error_message = "Bad Request") {
|
||||
constexpr SimpleWeb::StatusCode code = SimpleWeb::StatusCode::client_error_bad_request;
|
||||
|
||||
pt::ptree tree;
|
||||
|
|
@ -227,9 +218,10 @@ namespace confighttp {
|
|||
* @param request The HTTP request object.
|
||||
* @todo combine these functions into a single function that accepts the page, i.e "index", "pin", "apps"
|
||||
*/
|
||||
void
|
||||
getIndexPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getIndexPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -244,9 +236,10 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getPinPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getPinPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -261,9 +254,10 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getAppsPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getAppsPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -279,9 +273,10 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getClientsPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getClientsPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -296,9 +291,10 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getConfigPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getConfigPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -313,9 +309,10 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getPasswordPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getPasswordPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -330,8 +327,7 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getWelcomePage(resp_https_t response, req_https_t request) {
|
||||
void getWelcomePage(resp_https_t response, req_https_t request) {
|
||||
print_req(request);
|
||||
if (!config::sunshine.username.empty()) {
|
||||
send_redirect(response, request, "/");
|
||||
|
|
@ -348,9 +344,10 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getTroubleshootingPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getTroubleshootingPage(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -367,8 +364,7 @@ namespace confighttp {
|
|||
* @todo combine function with getSunshineLogoImage and possibly getNodeModules
|
||||
* @todo use mime_types map
|
||||
*/
|
||||
void
|
||||
getFaviconImage(resp_https_t response, req_https_t request) {
|
||||
void getFaviconImage(resp_https_t response, req_https_t request) {
|
||||
print_req(request);
|
||||
|
||||
std::ifstream in(WEB_DIR "images/sunshine.ico", std::ios::binary);
|
||||
|
|
@ -384,8 +380,7 @@ namespace confighttp {
|
|||
* @todo combine function with getFaviconImage and possibly getNodeModules
|
||||
* @todo use mime_types map
|
||||
*/
|
||||
void
|
||||
getSunshineLogoImage(resp_https_t response, req_https_t request) {
|
||||
void getSunshineLogoImage(resp_https_t response, req_https_t request) {
|
||||
print_req(request);
|
||||
|
||||
std::ifstream in(WEB_DIR "images/logo-sunshine-45.png", std::ios::binary);
|
||||
|
|
@ -400,8 +395,7 @@ namespace confighttp {
|
|||
* @param query The path to check.
|
||||
* @return True if the path is a child of the base path, false otherwise.
|
||||
*/
|
||||
bool
|
||||
isChildPath(fs::path const &base, fs::path const &query) {
|
||||
bool isChildPath(fs::path const &base, fs::path const &query) {
|
||||
auto relPath = fs::relative(base, query);
|
||||
return *(relPath.begin()) != fs::path("..");
|
||||
}
|
||||
|
|
@ -411,8 +405,7 @@ namespace confighttp {
|
|||
* @param response The HTTP response object.
|
||||
* @param request The HTTP request object.
|
||||
*/
|
||||
void
|
||||
getNodeModules(resp_https_t response, req_https_t request) {
|
||||
void getNodeModules(resp_https_t response, req_https_t request) {
|
||||
print_req(request);
|
||||
fs::path webDirPath(WEB_DIR);
|
||||
fs::path nodeModulesPath(webDirPath / "assets");
|
||||
|
|
@ -455,9 +448,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/apps| GET| null}
|
||||
*/
|
||||
void
|
||||
getApps(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getApps(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -474,9 +468,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/logs| GET| null}
|
||||
*/
|
||||
void
|
||||
getLogs(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getLogs(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -518,9 +513,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/apps| POST| {"name":"Hello, World!","index":-1}}
|
||||
*/
|
||||
void
|
||||
saveApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void saveApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -551,16 +547,14 @@ namespace confighttp {
|
|||
|
||||
if (index == -1) {
|
||||
apps_node.push_back(std::make_pair("", inputTree));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick
|
||||
pt::ptree newApps;
|
||||
int i = 0;
|
||||
for (const auto &[k, v] : apps_node) {
|
||||
if (i == index) {
|
||||
newApps.push_back(std::make_pair("", inputTree));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
newApps.push_back(std::make_pair("", v));
|
||||
}
|
||||
i++;
|
||||
|
|
@ -590,8 +584,7 @@ namespace confighttp {
|
|||
|
||||
outputTree.put("status", true);
|
||||
send_response(response, outputTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SaveApp: "sv << e.what();
|
||||
bad_request(response, request, e.what());
|
||||
}
|
||||
|
|
@ -604,9 +597,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/apps/9999| DELETE| null}
|
||||
*/
|
||||
void
|
||||
deleteApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void deleteApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -622,8 +616,7 @@ namespace confighttp {
|
|||
std::string error;
|
||||
if (const int max_index = static_cast<int>(apps_node.size()) - 1; max_index < 0) {
|
||||
error = "No applications to delete";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
error = "'index' out of range, max index is "s + std::to_string(max_index);
|
||||
}
|
||||
bad_request(response, request, error);
|
||||
|
|
@ -646,8 +639,7 @@ namespace confighttp {
|
|||
outputTree.put("status", true);
|
||||
outputTree.put("result", "application "s + std::to_string(index) + " deleted");
|
||||
send_response(response, outputTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "DeleteApp: "sv << e.what();
|
||||
bad_request(response, request, e.what());
|
||||
}
|
||||
|
|
@ -667,9 +659,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/covers/upload| POST| {"key":"igdb_1234","url":"https://images.igdb.com/igdb/image/upload/t_cover_big_2x/abc123.png"}}
|
||||
*/
|
||||
void
|
||||
uploadCover(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void uploadCover(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
std::stringstream configStream;
|
||||
|
|
@ -678,8 +671,7 @@ namespace confighttp {
|
|||
pt::ptree inputTree;
|
||||
try {
|
||||
pt::read_json(ss, inputTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "UploadCover: "sv << e.what();
|
||||
bad_request(response, request, e.what());
|
||||
return;
|
||||
|
|
@ -705,8 +697,7 @@ namespace confighttp {
|
|||
bad_request(response, request, "Failed to download cover");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto data = SimpleWeb::Crypto::Base64::decode(inputTree.get<std::string>("data"));
|
||||
|
||||
std::ofstream imgfile(path);
|
||||
|
|
@ -724,9 +715,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/config| GET| null}
|
||||
*/
|
||||
void
|
||||
getConfig(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void getConfig(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -751,8 +743,7 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/configLocale| GET| null}
|
||||
*/
|
||||
void
|
||||
getLocale(resp_https_t response, req_https_t request) {
|
||||
void getLocale(resp_https_t response, req_https_t request) {
|
||||
// we need to return the locale whether authenticated or not
|
||||
|
||||
print_req(request);
|
||||
|
|
@ -778,9 +769,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/config| POST| {"key":"value"}}
|
||||
*/
|
||||
void
|
||||
saveConfig(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void saveConfig(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -794,15 +786,16 @@ namespace confighttp {
|
|||
pt::read_json(ss, inputTree);
|
||||
for (const auto &[k, v] : inputTree) {
|
||||
std::string value = inputTree.get<std::string>(k);
|
||||
if (value.length() == 0 || value.compare("null") == 0) continue;
|
||||
if (value.length() == 0 || value.compare("null") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
configStream << k << " = " << value << std::endl;
|
||||
}
|
||||
file_handler::write_file(config::sunshine.config_file.c_str(), configStream.str());
|
||||
outputTree.put("status", true);
|
||||
send_response(response, outputTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SaveConfig: "sv << e.what();
|
||||
bad_request(response, request, e.what());
|
||||
}
|
||||
|
|
@ -815,9 +808,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/restart| POST| null}
|
||||
*/
|
||||
void
|
||||
restart(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void restart(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -832,9 +826,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/reset-display-device-persistence| POST| null}
|
||||
*/
|
||||
void
|
||||
resetDisplayDevicePersistence(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void resetDisplayDevicePersistence(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -860,9 +855,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/password| POST| {"currentUsername":"admin","currentPassword":"admin","newUsername":"admin","newPassword":"admin","confirmNewPassword":"admin"}}
|
||||
*/
|
||||
void
|
||||
savePassword(resp_https_t response, req_https_t request) {
|
||||
if (!config::sunshine.username.empty() && !authenticate(response, request)) return;
|
||||
void savePassword(resp_https_t response, req_https_t request) {
|
||||
if (!config::sunshine.username.empty() && !authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -881,40 +877,37 @@ namespace confighttp {
|
|||
auto password = inputTree.count("currentPassword") > 0 ? inputTree.get<std::string>("currentPassword") : "";
|
||||
auto newPassword = inputTree.count("newPassword") > 0 ? inputTree.get<std::string>("newPassword") : "";
|
||||
auto confirmPassword = inputTree.count("confirmNewPassword") > 0 ? inputTree.get<std::string>("confirmNewPassword") : "";
|
||||
if (newUsername.length() == 0) newUsername = username;
|
||||
if (newUsername.length() == 0) {
|
||||
newUsername = username;
|
||||
}
|
||||
if (newUsername.length() == 0) {
|
||||
errors.emplace_back("Invalid Username");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
|
||||
if (config::sunshine.username.empty() || (boost::iequals(username, config::sunshine.username) && hash == config::sunshine.password)) {
|
||||
if (newPassword.empty() || newPassword != confirmPassword) {
|
||||
errors.emplace_back("Password Mismatch");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
http::save_user_creds(config::sunshine.credentials_file, newUsername, newPassword);
|
||||
http::reload_user_creds(config::sunshine.credentials_file);
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
errors.emplace_back("Invalid Current Credentials");
|
||||
}
|
||||
}
|
||||
|
||||
if (!errors.empty()) {
|
||||
// join the errors array
|
||||
std::string error = std::accumulate(errors.begin(), errors.end(), std::string(),
|
||||
[](const std::string &a, const std::string &b) {
|
||||
return a.empty() ? b : a + ", " + b;
|
||||
});
|
||||
std::string error = std::accumulate(errors.begin(), errors.end(), std::string(), [](const std::string &a, const std::string &b) {
|
||||
return a.empty() ? b : a + ", " + b;
|
||||
});
|
||||
bad_request(response, request, error);
|
||||
return;
|
||||
}
|
||||
|
||||
send_response(response, outputTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SavePassword: "sv << e.what();
|
||||
bad_request(response, request, e.what());
|
||||
}
|
||||
|
|
@ -934,9 +927,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/pin| POST| {"pin":"1234","name":"My PC"}}
|
||||
*/
|
||||
void
|
||||
savePin(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void savePin(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -952,8 +946,7 @@ namespace confighttp {
|
|||
std::string name = inputTree.get<std::string>("name");
|
||||
outputTree.put("status", nvhttp::pin(pin, name));
|
||||
send_response(response, outputTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SavePin: "sv << e.what();
|
||||
bad_request(response, request, e.what());
|
||||
}
|
||||
|
|
@ -966,9 +959,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/clients/unpair-all| POST| null}
|
||||
*/
|
||||
void
|
||||
unpairAll(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void unpairAll(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -993,9 +987,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/unpair| POST| {"uuid":"1234"}}
|
||||
*/
|
||||
void
|
||||
unpair(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void unpair(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -1010,8 +1005,7 @@ namespace confighttp {
|
|||
std::string uuid = inputTree.get<std::string>("uuid");
|
||||
outputTree.put("status", nvhttp::unpair_client(uuid));
|
||||
send_response(response, outputTree);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "Unpair: "sv << e.what();
|
||||
bad_request(response, request, e.what());
|
||||
}
|
||||
|
|
@ -1024,9 +1018,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/clients/list| GET| null}
|
||||
*/
|
||||
void
|
||||
listClients(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void listClients(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -1046,9 +1041,10 @@ namespace confighttp {
|
|||
*
|
||||
* @api_examples{/api/apps/close| POST| null}
|
||||
*/
|
||||
void
|
||||
closeApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) return;
|
||||
void closeApp(resp_https_t response, req_https_t request) {
|
||||
if (!authenticate(response, request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_req(request);
|
||||
|
||||
|
|
@ -1059,14 +1055,13 @@ namespace confighttp {
|
|||
send_response(response, outputTree);
|
||||
}
|
||||
|
||||
void
|
||||
start() {
|
||||
void start() {
|
||||
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
|
||||
|
||||
auto port_https = net::map_port(PORT_HTTPS);
|
||||
auto address_family = net::af_from_enum_string(config::sunshine.address_family);
|
||||
|
||||
https_server_t server { config::nvhttp.cert, config::nvhttp.pkey };
|
||||
https_server_t server {config::nvhttp.cert, config::nvhttp.pkey};
|
||||
server.default_resource["DELETE"] = [](resp_https_t response, req_https_t request) {
|
||||
bad_request(response, request);
|
||||
};
|
||||
|
|
@ -1116,8 +1111,7 @@ namespace confighttp {
|
|||
server->start([](unsigned short port) {
|
||||
BOOST_LOG(info) << "Configuration UI available at [https://localhost:"sv << port << "]";
|
||||
});
|
||||
}
|
||||
catch (boost::system::system_error &err) {
|
||||
} catch (boost::system::system_error &err) {
|
||||
// It's possible the exception gets thrown after calling server->stop() from a different thread
|
||||
if (shutdown_event->peek()) {
|
||||
return;
|
||||
|
|
@ -1128,7 +1122,7 @@ namespace confighttp {
|
|||
return;
|
||||
}
|
||||
};
|
||||
std::thread tcp { accept_and_run, &server };
|
||||
std::thread tcp {accept_and_run, &server};
|
||||
|
||||
// Wait for any event
|
||||
shutdown_event->view();
|
||||
|
|
|
|||
|
|
@ -4,34 +4,35 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
// local includes
|
||||
#include "thread_safe.h"
|
||||
|
||||
#define WEB_DIR SUNSHINE_ASSETS_DIR "/web/"
|
||||
|
||||
namespace confighttp {
|
||||
constexpr auto PORT_HTTPS = 1;
|
||||
void
|
||||
start();
|
||||
void start();
|
||||
} // namespace confighttp
|
||||
|
||||
// mime types map
|
||||
const std::map<std::string, std::string> mime_types = {
|
||||
{ "css", "text/css" },
|
||||
{ "gif", "image/gif" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "ico", "image/x-icon" },
|
||||
{ "jpeg", "image/jpeg" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "js", "application/javascript" },
|
||||
{ "json", "application/json" },
|
||||
{ "png", "image/png" },
|
||||
{ "svg", "image/svg+xml" },
|
||||
{ "ttf", "font/ttf" },
|
||||
{ "txt", "text/plain" },
|
||||
{ "woff2", "font/woff2" },
|
||||
{ "xml", "text/xml" },
|
||||
{"css", "text/css"},
|
||||
{"gif", "image/gif"},
|
||||
{"htm", "text/html"},
|
||||
{"html", "text/html"},
|
||||
{"ico", "image/x-icon"},
|
||||
{"jpeg", "image/jpeg"},
|
||||
{"jpg", "image/jpeg"},
|
||||
{"js", "application/javascript"},
|
||||
{"json", "application/json"},
|
||||
{"png", "image/png"},
|
||||
{"svg", "image/svg+xml"},
|
||||
{"ttf", "font/ttf"},
|
||||
{"txt", "text/plain"},
|
||||
{"woff2", "font/woff2"},
|
||||
{"xml", "text/xml"},
|
||||
};
|
||||
|
|
|
|||
140
src/crypto.cpp
140
src/crypto.cpp
|
|
@ -2,29 +2,33 @@
|
|||
* @file src/crypto.cpp
|
||||
* @brief Definitions for cryptography functions.
|
||||
*/
|
||||
#include "crypto.h"
|
||||
// lib includes
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
// local includes
|
||||
#include "crypto.h"
|
||||
|
||||
namespace crypto {
|
||||
using asn1_string_t = util::safe_ptr<ASN1_STRING, ASN1_STRING_free>;
|
||||
|
||||
cert_chain_t::cert_chain_t():
|
||||
_certs {}, _cert_ctx { X509_STORE_CTX_new() } {}
|
||||
void
|
||||
cert_chain_t::add(x509_t &&cert) {
|
||||
x509_store_t x509_store { X509_STORE_new() };
|
||||
_certs {},
|
||||
_cert_ctx {X509_STORE_CTX_new()} {
|
||||
}
|
||||
|
||||
void cert_chain_t::add(x509_t &&cert) {
|
||||
x509_store_t x509_store {X509_STORE_new()};
|
||||
|
||||
X509_STORE_add_cert(x509_store.get(), cert.get());
|
||||
_certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store)));
|
||||
}
|
||||
void
|
||||
cert_chain_t::clear() {
|
||||
|
||||
void cert_chain_t::clear() {
|
||||
_certs.clear();
|
||||
}
|
||||
|
||||
static int
|
||||
openssl_verify_cb(int ok, X509_STORE_CTX *ctx) {
|
||||
static int openssl_verify_cb(int ok, X509_STORE_CTX *ctx) {
|
||||
int err_code = X509_STORE_CTX_get_error(ctx);
|
||||
|
||||
switch (err_code) {
|
||||
|
|
@ -52,8 +56,7 @@ namespace crypto {
|
|||
* @param cert The certificate to verify.
|
||||
* @return nullptr if the certificate is valid, otherwise an error string.
|
||||
*/
|
||||
const char *
|
||||
cert_chain_t::verify(x509_t::element_type *cert) {
|
||||
const char *cert_chain_t::verify(x509_t::element_type *cert) {
|
||||
int err_code = 0;
|
||||
for (auto &[_, x509_store] : _certs) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
|
|
@ -86,8 +89,7 @@ namespace crypto {
|
|||
|
||||
namespace cipher {
|
||||
|
||||
static int
|
||||
init_decrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
static int init_decrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
if (!ctx) {
|
||||
|
|
@ -110,8 +112,7 @@ namespace crypto {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_encrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
static int init_encrypt_gcm(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
|
|
@ -131,8 +132,7 @@ namespace crypto {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_encrypt_cbc(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
static int init_encrypt_cbc(cipher_ctx_t &ctx, aes_t *key, aes_t *iv, bool padding) {
|
||||
ctx.reset(EVP_CIPHER_CTX_new());
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
|
|
@ -145,8 +145,7 @@ namespace crypto {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gcm_t::decrypt(const std::string_view &tagged_cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv) {
|
||||
int gcm_t::decrypt(const std::string_view &tagged_cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv) {
|
||||
if (!decrypt_ctx && init_decrypt_gcm(decrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -185,8 +184,7 @@ namespace crypto {
|
|||
* The function handles the creation and initialization of the encryption context, and manages the encryption process.
|
||||
* The resulting ciphertext and the GCM tag are written into the tagged_cipher buffer.
|
||||
*/
|
||||
int
|
||||
gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv) {
|
||||
int gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv) {
|
||||
if (!encrypt_ctx && init_encrypt_gcm(encrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -216,14 +214,12 @@ namespace crypto {
|
|||
return update_outlen + final_outlen;
|
||||
}
|
||||
|
||||
int
|
||||
gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv) {
|
||||
int gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv) {
|
||||
// This overload handles the common case of [GCM tag][cipher text] buffer layout
|
||||
return encrypt(plaintext, tagged_cipher, tagged_cipher + tag_size, iv);
|
||||
}
|
||||
|
||||
int
|
||||
ecb_t::decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext) {
|
||||
int ecb_t::decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
EVP_CIPHER_CTX_reset(decrypt_ctx.get());
|
||||
});
|
||||
|
|
@ -250,8 +246,7 @@ namespace crypto {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ecb_t::encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher) {
|
||||
int ecb_t::encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
EVP_CIPHER_CTX_reset(encrypt_ctx.get());
|
||||
});
|
||||
|
|
@ -284,8 +279,7 @@ namespace crypto {
|
|||
* The function handles the creation and initialization of the encryption context, and manages the encryption process.
|
||||
* The resulting ciphertext is written into the cipher buffer.
|
||||
*/
|
||||
int
|
||||
cbc_t::encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv) {
|
||||
int cbc_t::encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv) {
|
||||
if (!encrypt_ctx && init_encrypt_cbc(encrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -311,18 +305,20 @@ namespace crypto {
|
|||
}
|
||||
|
||||
ecb_t::ecb_t(const aes_t &key, bool padding):
|
||||
cipher_t { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_new(), key, padding } {}
|
||||
cipher_t {EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_new(), key, padding} {
|
||||
}
|
||||
|
||||
cbc_t::cbc_t(const aes_t &key, bool padding):
|
||||
cipher_t { nullptr, nullptr, key, padding } {}
|
||||
cipher_t {nullptr, nullptr, key, padding} {
|
||||
}
|
||||
|
||||
gcm_t::gcm_t(const crypto::aes_t &key, bool padding):
|
||||
cipher_t { nullptr, nullptr, key, padding } {}
|
||||
cipher_t {nullptr, nullptr, key, padding} {
|
||||
}
|
||||
|
||||
} // namespace cipher
|
||||
|
||||
aes_t
|
||||
gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin) {
|
||||
aes_t gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin) {
|
||||
aes_t key(16);
|
||||
|
||||
std::string salt_pin;
|
||||
|
|
@ -338,16 +334,14 @@ namespace crypto {
|
|||
return key;
|
||||
}
|
||||
|
||||
sha256_t
|
||||
hash(const std::string_view &plaintext) {
|
||||
sha256_t hash(const std::string_view &plaintext) {
|
||||
sha256_t hsh;
|
||||
EVP_Digest(plaintext.data(), plaintext.size(), hsh.data(), nullptr, EVP_sha256(), nullptr);
|
||||
return hsh;
|
||||
}
|
||||
|
||||
x509_t
|
||||
x509(const std::string_view &x) {
|
||||
bio_t io { BIO_new(BIO_s_mem()) };
|
||||
x509_t x509(const std::string_view &x) {
|
||||
bio_t io {BIO_new(BIO_s_mem())};
|
||||
|
||||
BIO_write(io.get(), x.data(), x.size());
|
||||
|
||||
|
|
@ -357,9 +351,8 @@ namespace crypto {
|
|||
return p;
|
||||
}
|
||||
|
||||
pkey_t
|
||||
pkey(const std::string_view &k) {
|
||||
bio_t io { BIO_new(BIO_s_mem()) };
|
||||
pkey_t pkey(const std::string_view &k) {
|
||||
bio_t io {BIO_new(BIO_s_mem())};
|
||||
|
||||
BIO_write(io.get(), k.data(), k.size());
|
||||
|
||||
|
|
@ -369,40 +362,36 @@ namespace crypto {
|
|||
return p;
|
||||
}
|
||||
|
||||
std::string
|
||||
pem(x509_t &x509) {
|
||||
bio_t bio { BIO_new(BIO_s_mem()) };
|
||||
std::string pem(x509_t &x509) {
|
||||
bio_t bio {BIO_new(BIO_s_mem())};
|
||||
|
||||
PEM_write_bio_X509(bio.get(), x509.get());
|
||||
BUF_MEM *mem_ptr;
|
||||
BIO_get_mem_ptr(bio.get(), &mem_ptr);
|
||||
|
||||
return { mem_ptr->data, mem_ptr->length };
|
||||
return {mem_ptr->data, mem_ptr->length};
|
||||
}
|
||||
|
||||
std::string
|
||||
pem(pkey_t &pkey) {
|
||||
bio_t bio { BIO_new(BIO_s_mem()) };
|
||||
std::string pem(pkey_t &pkey) {
|
||||
bio_t bio {BIO_new(BIO_s_mem())};
|
||||
|
||||
PEM_write_bio_PrivateKey(bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr);
|
||||
BUF_MEM *mem_ptr;
|
||||
BIO_get_mem_ptr(bio.get(), &mem_ptr);
|
||||
|
||||
return { mem_ptr->data, mem_ptr->length };
|
||||
return {mem_ptr->data, mem_ptr->length};
|
||||
}
|
||||
|
||||
std::string_view
|
||||
signature(const x509_t &x) {
|
||||
std::string_view signature(const x509_t &x) {
|
||||
// X509_ALGOR *_ = nullptr;
|
||||
|
||||
const ASN1_BIT_STRING *asn1 = nullptr;
|
||||
X509_get0_signature(&asn1, nullptr, x.get());
|
||||
|
||||
return { (const char *) asn1->data, (std::size_t) asn1->length };
|
||||
return {(const char *) asn1->data, (std::size_t) asn1->length};
|
||||
}
|
||||
|
||||
std::string
|
||||
rand(std::size_t bytes) {
|
||||
std::string rand(std::size_t bytes) {
|
||||
std::string r;
|
||||
r.resize(bytes);
|
||||
|
||||
|
|
@ -411,9 +400,8 @@ namespace crypto {
|
|||
return r;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>
|
||||
sign(const pkey_t &pkey, const std::string_view &data, const EVP_MD *md) {
|
||||
md_ctx_t ctx { EVP_MD_CTX_create() };
|
||||
std::vector<uint8_t> sign(const pkey_t &pkey, const std::string_view &data, const EVP_MD *md) {
|
||||
md_ctx_t ctx {EVP_MD_CTX_create()};
|
||||
|
||||
if (EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, (EVP_PKEY *) pkey.get()) != 1) {
|
||||
return {};
|
||||
|
|
@ -436,10 +424,9 @@ namespace crypto {
|
|||
return digest;
|
||||
}
|
||||
|
||||
creds_t
|
||||
gen_creds(const std::string_view &cn, std::uint32_t key_bits) {
|
||||
x509_t x509 { X509_new() };
|
||||
pkey_ctx_t ctx { EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) };
|
||||
creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) {
|
||||
x509_t x509 {X509_new()};
|
||||
pkey_ctx_t ctx {EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr)};
|
||||
pkey_t pkey;
|
||||
|
||||
EVP_PKEY_keygen_init(ctx.get());
|
||||
|
|
@ -449,7 +436,7 @@ namespace crypto {
|
|||
X509_set_version(x509.get(), 2);
|
||||
|
||||
// Generate a real serial number to avoid SEC_ERROR_REUSED_ISSUER_AND_SERIAL with Firefox
|
||||
bignum_t serial { BN_new() };
|
||||
bignum_t serial {BN_new()};
|
||||
BN_rand(serial.get(), 159, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); // 159 bits to fit in 20 bytes in DER format
|
||||
BN_set_negative(serial.get(), 0); // Serial numbers must be positive
|
||||
BN_to_ASN1_INTEGER(serial.get(), X509_get_serialNumber(x509.get()));
|
||||
|
|
@ -459,8 +446,8 @@ namespace crypto {
|
|||
X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
|
||||
X509_gmtime_adj(X509_get_notAfter(x509.get()), 20 * year);
|
||||
#else
|
||||
asn1_string_t not_before { ASN1_STRING_dup(X509_get0_notBefore(x509.get())) };
|
||||
asn1_string_t not_after { ASN1_STRING_dup(X509_get0_notAfter(x509.get())) };
|
||||
asn1_string_t not_before {ASN1_STRING_dup(X509_get0_notBefore(x509.get()))};
|
||||
asn1_string_t not_after {ASN1_STRING_dup(X509_get0_notAfter(x509.get()))};
|
||||
|
||||
X509_gmtime_adj(not_before.get(), 0);
|
||||
X509_gmtime_adj(not_after.get(), 20 * year);
|
||||
|
|
@ -472,26 +459,22 @@ namespace crypto {
|
|||
X509_set_pubkey(x509.get(), pkey.get());
|
||||
|
||||
auto name = X509_get_subject_name(x509.get());
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
|
||||
(const std::uint8_t *) cn.data(), cn.size(),
|
||||
-1, 0);
|
||||
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const std::uint8_t *) cn.data(), cn.size(), -1, 0);
|
||||
|
||||
X509_set_issuer_name(x509.get(), name);
|
||||
X509_sign(x509.get(), pkey.get(), EVP_sha256());
|
||||
|
||||
return { pem(x509), pem(pkey) };
|
||||
return {pem(x509), pem(pkey)};
|
||||
}
|
||||
|
||||
std::vector<uint8_t>
|
||||
sign256(const pkey_t &pkey, const std::string_view &data) {
|
||||
std::vector<uint8_t> sign256(const pkey_t &pkey, const std::string_view &data) {
|
||||
return sign(pkey, data, EVP_sha256());
|
||||
}
|
||||
|
||||
bool
|
||||
verify(const x509_t &x509, const std::string_view &data, const std::string_view &signature, const EVP_MD *md) {
|
||||
bool verify(const x509_t &x509, const std::string_view &data, const std::string_view &signature, const EVP_MD *md) {
|
||||
auto pkey = X509_get0_pubkey(x509.get());
|
||||
|
||||
md_ctx_t ctx { EVP_MD_CTX_create() };
|
||||
md_ctx_t ctx {EVP_MD_CTX_create()};
|
||||
|
||||
if (EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, pkey) != 1) {
|
||||
return false;
|
||||
|
|
@ -508,18 +491,15 @@ namespace crypto {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature) {
|
||||
bool verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature) {
|
||||
return verify(x509, data, signature, EVP_sha256());
|
||||
}
|
||||
|
||||
void
|
||||
md_ctx_destroy(EVP_MD_CTX *ctx) {
|
||||
void md_ctx_destroy(EVP_MD_CTX *ctx) {
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
}
|
||||
|
||||
std::string
|
||||
rand_alphabet(std::size_t bytes, const std::string_view &alphabet) {
|
||||
std::string rand_alphabet(std::size_t bytes, const std::string_view &alphabet) {
|
||||
auto value = rand(bytes);
|
||||
|
||||
for (std::size_t i = 0; i != value.size(); ++i) {
|
||||
|
|
|
|||
85
src/crypto.h
85
src/crypto.h
|
|
@ -4,12 +4,16 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <array>
|
||||
|
||||
// lib includes
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
// local includes
|
||||
#include "utility.h"
|
||||
|
||||
namespace crypto {
|
||||
|
|
@ -18,8 +22,7 @@ namespace crypto {
|
|||
std::string pkey;
|
||||
};
|
||||
|
||||
void
|
||||
md_ctx_destroy(EVP_MD_CTX *);
|
||||
void md_ctx_destroy(EVP_MD_CTX *);
|
||||
|
||||
using sha256_t = std::array<std::uint8_t, SHA256_DIGEST_LENGTH>;
|
||||
|
||||
|
|
@ -39,50 +42,33 @@ namespace crypto {
|
|||
* @param plaintext
|
||||
* @return The SHA-256 hash of the plaintext.
|
||||
*/
|
||||
sha256_t
|
||||
hash(const std::string_view &plaintext);
|
||||
sha256_t hash(const std::string_view &plaintext);
|
||||
|
||||
aes_t
|
||||
gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin);
|
||||
aes_t gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin);
|
||||
x509_t x509(const std::string_view &x);
|
||||
pkey_t pkey(const std::string_view &k);
|
||||
std::string pem(x509_t &x509);
|
||||
std::string pem(pkey_t &pkey);
|
||||
|
||||
x509_t
|
||||
x509(const std::string_view &x);
|
||||
pkey_t
|
||||
pkey(const std::string_view &k);
|
||||
std::string
|
||||
pem(x509_t &x509);
|
||||
std::string
|
||||
pem(pkey_t &pkey);
|
||||
std::vector<uint8_t> sign256(const pkey_t &pkey, const std::string_view &data);
|
||||
bool verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature);
|
||||
|
||||
std::vector<uint8_t>
|
||||
sign256(const pkey_t &pkey, const std::string_view &data);
|
||||
bool
|
||||
verify256(const x509_t &x509, const std::string_view &data, const std::string_view &signature);
|
||||
creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits);
|
||||
|
||||
creds_t
|
||||
gen_creds(const std::string_view &cn, std::uint32_t key_bits);
|
||||
std::string_view signature(const x509_t &x);
|
||||
|
||||
std::string_view
|
||||
signature(const x509_t &x);
|
||||
|
||||
std::string
|
||||
rand(std::size_t bytes);
|
||||
std::string
|
||||
rand_alphabet(std::size_t bytes,
|
||||
const std::string_view &alphabet = std::string_view { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-" });
|
||||
std::string rand(std::size_t bytes);
|
||||
std::string rand_alphabet(std::size_t bytes, const std::string_view &alphabet = std::string_view {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-"});
|
||||
|
||||
class cert_chain_t {
|
||||
public:
|
||||
KITTY_DECL_CONSTR(cert_chain_t)
|
||||
|
||||
void
|
||||
add(x509_t &&cert);
|
||||
void add(x509_t &&cert);
|
||||
|
||||
void
|
||||
clear();
|
||||
void clear();
|
||||
|
||||
const char *
|
||||
verify(x509_t::element_type *cert);
|
||||
const char *verify(x509_t::element_type *cert);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<x509_t, x509_store_t>> _certs;
|
||||
|
|
@ -91,8 +77,8 @@ namespace crypto {
|
|||
|
||||
namespace cipher {
|
||||
constexpr std::size_t tag_size = 16;
|
||||
constexpr std::size_t
|
||||
round_to_pkcs7_padded(std::size_t size) {
|
||||
|
||||
constexpr std::size_t round_to_pkcs7_padded(std::size_t size) {
|
||||
return ((size + 15) / 16) * 16;
|
||||
}
|
||||
|
||||
|
|
@ -110,23 +96,19 @@ namespace crypto {
|
|||
public:
|
||||
ecb_t() = default;
|
||||
ecb_t(ecb_t &&) noexcept = default;
|
||||
ecb_t &
|
||||
operator=(ecb_t &&) noexcept = default;
|
||||
ecb_t &operator=(ecb_t &&) noexcept = default;
|
||||
|
||||
ecb_t(const aes_t &key, bool padding = true);
|
||||
|
||||
int
|
||||
encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher);
|
||||
int
|
||||
decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext);
|
||||
int encrypt(const std::string_view &plaintext, std::vector<std::uint8_t> &cipher);
|
||||
int decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext);
|
||||
};
|
||||
|
||||
class gcm_t: public cipher_t {
|
||||
public:
|
||||
gcm_t() = default;
|
||||
gcm_t(gcm_t &&) noexcept = default;
|
||||
gcm_t &
|
||||
operator=(gcm_t &&) noexcept = default;
|
||||
gcm_t &operator=(gcm_t &&) noexcept = default;
|
||||
|
||||
gcm_t(const crypto::aes_t &key, bool padding = true);
|
||||
|
||||
|
|
@ -138,8 +120,7 @@ namespace crypto {
|
|||
* @param iv The initialization vector to be used for the encryption.
|
||||
* @return The total length of the ciphertext and GCM tag. Returns -1 in case of an error.
|
||||
*/
|
||||
int
|
||||
encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv);
|
||||
int encrypt(const std::string_view &plaintext, std::uint8_t *tag, std::uint8_t *ciphertext, aes_t *iv);
|
||||
|
||||
/**
|
||||
* @brief Encrypts the plaintext using AES GCM mode.
|
||||
|
|
@ -149,19 +130,16 @@ namespace crypto {
|
|||
* @param iv The initialization vector to be used for the encryption.
|
||||
* @return The total length of the ciphertext and GCM tag written into tagged_cipher. Returns -1 in case of an error.
|
||||
*/
|
||||
int
|
||||
encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv);
|
||||
int encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv);
|
||||
|
||||
int
|
||||
decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv);
|
||||
int decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &plaintext, aes_t *iv);
|
||||
};
|
||||
|
||||
class cbc_t: public cipher_t {
|
||||
public:
|
||||
cbc_t() = default;
|
||||
cbc_t(cbc_t &&) noexcept = default;
|
||||
cbc_t &
|
||||
operator=(cbc_t &&) noexcept = default;
|
||||
cbc_t &operator=(cbc_t &&) noexcept = default;
|
||||
|
||||
cbc_t(const crypto::aes_t &key, bool padding = true);
|
||||
|
||||
|
|
@ -173,8 +151,7 @@ namespace crypto {
|
|||
* @param iv The initialization vector to be used for the encryption.
|
||||
* @return The total length of the ciphertext written into cipher. Returns -1 in case of an error.
|
||||
*/
|
||||
int
|
||||
encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv);
|
||||
int encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv);
|
||||
};
|
||||
} // namespace cipher
|
||||
} // namespace crypto
|
||||
|
|
|
|||
|
|
@ -29,15 +29,15 @@
|
|||
|
||||
namespace display_device {
|
||||
namespace {
|
||||
constexpr std::chrono::milliseconds DEFAULT_RETRY_INTERVAL { 5000 };
|
||||
constexpr std::chrono::milliseconds DEFAULT_RETRY_INTERVAL {5000};
|
||||
|
||||
/**
|
||||
* @brief A global for the settings manager interface and other settings whose lifetime is managed by `display_device::init(...)`.
|
||||
*/
|
||||
struct {
|
||||
std::mutex mutex {};
|
||||
std::chrono::milliseconds config_revert_delay { 0 };
|
||||
std::unique_ptr<RetryScheduler<SettingsManagerInterface>> sm_instance { nullptr };
|
||||
std::chrono::milliseconds config_revert_delay {0};
|
||||
std::unique_ptr<RetryScheduler<SettingsManagerInterface>> sm_instance {nullptr};
|
||||
} DD_DATA;
|
||||
|
||||
/**
|
||||
|
|
@ -49,8 +49,7 @@ namespace display_device {
|
|||
*/
|
||||
class sunshine_audio_context_t: public AudioContextInterface {
|
||||
public:
|
||||
[[nodiscard]] bool
|
||||
capture() override {
|
||||
[[nodiscard]] bool capture() override {
|
||||
return context_scheduler.execute([](auto &audio_context) {
|
||||
// Explicitly releasing the context first in case it was not release yet so that it can be potentially cleaned up.
|
||||
audio_context = boost::none;
|
||||
|
|
@ -61,8 +60,7 @@ namespace display_device {
|
|||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isCaptured() const override {
|
||||
[[nodiscard]] bool isCaptured() const override {
|
||||
return context_scheduler.execute([](const auto &audio_context) {
|
||||
if (audio_context) {
|
||||
// In case we still have context we need to check whether it was released or not.
|
||||
|
|
@ -74,8 +72,7 @@ namespace display_device {
|
|||
});
|
||||
}
|
||||
|
||||
void
|
||||
release() override {
|
||||
void release() override {
|
||||
context_scheduler.schedule([](auto &audio_context, auto &stop_token) {
|
||||
if (audio_context) {
|
||||
audio_context->released = true;
|
||||
|
|
@ -93,7 +90,7 @@ namespace display_device {
|
|||
audio_context = boost::none;
|
||||
stop_token.requestStop();
|
||||
},
|
||||
SchedulerOptions { .m_sleep_durations = { 2s } });
|
||||
SchedulerOptions {.m_sleep_durations = {2s}});
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -102,20 +99,20 @@ namespace display_device {
|
|||
* @brief A reference to the audio context that will automatically extend the audio session.
|
||||
* @note It is auto-initialized here for convenience.
|
||||
*/
|
||||
decltype(audio::get_audio_ctx_ref()) audio_ctx_ref { audio::get_audio_ctx_ref() };
|
||||
decltype(audio::get_audio_ctx_ref()) audio_ctx_ref {audio::get_audio_ctx_ref()};
|
||||
|
||||
/**
|
||||
* @brief Will be set to true if the capture was released, but we still have to keep the context around, because the device is not available.
|
||||
*/
|
||||
bool released { false };
|
||||
bool released {false};
|
||||
|
||||
/**
|
||||
* @brief How many times to check if the audio sink is available before giving up.
|
||||
*/
|
||||
int retry_counter { 15 };
|
||||
int retry_counter {15};
|
||||
};
|
||||
|
||||
RetryScheduler<boost::optional<audio_context_t>> context_scheduler { std::make_unique<boost::optional<audio_context_t>>(boost::none) };
|
||||
RetryScheduler<boost::optional<audio_context_t>> context_scheduler {std::make_unique<boost::optional<audio_context_t>>(boost::none)};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -124,9 +121,8 @@ namespace display_device {
|
|||
* @param value String to be converted
|
||||
* @return Parsed unsigned integer.
|
||||
*/
|
||||
unsigned int
|
||||
stou(const std::string &value) {
|
||||
unsigned long result { std::stoul(value) };
|
||||
unsigned int stou(const std::string &value) {
|
||||
unsigned long result {std::stoul(value)};
|
||||
if (result > std::numeric_limits<unsigned int>::max()) {
|
||||
throw std::out_of_range("stou");
|
||||
}
|
||||
|
|
@ -151,10 +147,9 @@ namespace display_device {
|
|||
* }
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
parse_resolution_string(const std::string &input, std::optional<Resolution> &output) {
|
||||
const std::string trimmed_input { boost::algorithm::trim_copy(input) };
|
||||
const std::regex resolution_regex { R"(^(\d+)x(\d+)$)" };
|
||||
bool parse_resolution_string(const std::string &input, std::optional<Resolution> &output) {
|
||||
const std::string trimmed_input {boost::algorithm::trim_copy(input)};
|
||||
const std::regex resolution_regex {R"(^(\d+)x(\d+)$)"};
|
||||
|
||||
if (std::smatch match; std::regex_match(trimmed_input, match, resolution_regex)) {
|
||||
try {
|
||||
|
|
@ -163,16 +158,13 @@ namespace display_device {
|
|||
stou(match[2].str())
|
||||
};
|
||||
return true;
|
||||
}
|
||||
catch (const std::out_of_range &) {
|
||||
} catch (const std::out_of_range &) {
|
||||
BOOST_LOG(error) << "Failed to parse resolution string " << trimmed_input << " (number out of range).";
|
||||
}
|
||||
catch (const std::exception &err) {
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG(error) << "Failed to parse resolution string " << trimmed_input << ":\n"
|
||||
<< err.what();
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (trimmed_input.empty()) {
|
||||
output = std::nullopt;
|
||||
return true;
|
||||
|
|
@ -203,16 +195,17 @@ namespace display_device {
|
|||
* }
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
parse_refresh_rate_string(const std::string &input, std::optional<FloatingPoint> &output, const bool allow_decimal_point = true) {
|
||||
static const auto is_zero { [](const auto &character) { return character == '0'; } };
|
||||
const std::string trimmed_input { boost::algorithm::trim_copy(input) };
|
||||
const std::regex refresh_rate_regex { allow_decimal_point ? R"(^(\d+)(?:\.(\d+))?$)" : R"(^(\d+)$)" };
|
||||
bool parse_refresh_rate_string(const std::string &input, std::optional<FloatingPoint> &output, const bool allow_decimal_point = true) {
|
||||
static const auto is_zero {[](const auto &character) {
|
||||
return character == '0';
|
||||
}};
|
||||
const std::string trimmed_input {boost::algorithm::trim_copy(input)};
|
||||
const std::regex refresh_rate_regex {allow_decimal_point ? R"(^(\d+)(?:\.(\d+))?$)" : R"(^(\d+)$)"};
|
||||
|
||||
if (std::smatch match; std::regex_match(trimmed_input, match, refresh_rate_regex)) {
|
||||
try {
|
||||
// Here we are trimming zeros from the string to possibly reduce out of bounds case
|
||||
std::string trimmed_match_1 { boost::algorithm::trim_left_copy_if(match[1].str(), is_zero) };
|
||||
std::string trimmed_match_1 {boost::algorithm::trim_left_copy_if(match[1].str(), is_zero)};
|
||||
if (trimmed_match_1.empty()) {
|
||||
trimmed_match_1 = "0"s; // Just in case ALL the string is full of zeros, we want to leave one
|
||||
}
|
||||
|
|
@ -230,33 +223,29 @@ namespace display_device {
|
|||
// denominator = 1000
|
||||
|
||||
// We are essentially removing the decimal point here: 59.995 -> 59995
|
||||
const std::string numerator_str { trimmed_match_1 + trimmed_match_2 };
|
||||
const auto numerator { stou(numerator_str) };
|
||||
const std::string numerator_str {trimmed_match_1 + trimmed_match_2};
|
||||
const auto numerator {stou(numerator_str)};
|
||||
|
||||
// Here we are counting decimal places and calculating denominator: 10^decimal_places
|
||||
const auto denominator { static_cast<unsigned int>(std::pow(10, trimmed_match_2.size())) };
|
||||
const auto denominator {static_cast<unsigned int>(std::pow(10, trimmed_match_2.size()))};
|
||||
|
||||
output = Rational { numerator, denominator };
|
||||
}
|
||||
else {
|
||||
output = Rational {numerator, denominator};
|
||||
} else {
|
||||
// We do not have a decimal point, just a valid number.
|
||||
// For example:
|
||||
// 60:
|
||||
// numerator = 60
|
||||
// denominator = 1
|
||||
output = Rational { stou(trimmed_match_1), 1 };
|
||||
output = Rational {stou(trimmed_match_1), 1};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (const std::out_of_range &) {
|
||||
} catch (const std::out_of_range &) {
|
||||
BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << " (number out of range).";
|
||||
}
|
||||
catch (const std::exception &err) {
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << ":\n"
|
||||
<< err.what();
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (trimmed_input.empty()) {
|
||||
output = std::nullopt;
|
||||
return true;
|
||||
|
|
@ -279,8 +268,7 @@ namespace display_device {
|
|||
* const auto device_prep_option = parse_device_prep_option(video_config);
|
||||
* @examples_end
|
||||
*/
|
||||
std::optional<SingleDisplayConfiguration::DevicePreparation>
|
||||
parse_device_prep_option(const config::video_t &video_config) {
|
||||
std::optional<SingleDisplayConfiguration::DevicePreparation> parse_device_prep_option(const config::video_t &video_config) {
|
||||
using enum config::video_t::dd_t::config_option_e;
|
||||
using enum SingleDisplayConfiguration::DevicePreparation;
|
||||
|
||||
|
|
@ -315,44 +303,42 @@ namespace display_device {
|
|||
* const bool success = parse_resolution_option(video_config, *launch_session, config);
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
parse_resolution_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) {
|
||||
bool parse_resolution_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) {
|
||||
using resolution_option_e = config::video_t::dd_t::resolution_option_e;
|
||||
|
||||
switch (video_config.dd.resolution_option) {
|
||||
case resolution_option_e::automatic: {
|
||||
if (!session.enable_sops) {
|
||||
BOOST_LOG(warning) << R"(Sunshine is configured to change resolution automatically, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)";
|
||||
}
|
||||
else if (session.width >= 0 && session.height >= 0) {
|
||||
config.m_resolution = Resolution {
|
||||
static_cast<unsigned int>(session.width),
|
||||
static_cast<unsigned int>(session.height)
|
||||
};
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Resolution provided by client session config is invalid: " << session.width << "x" << session.height;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case resolution_option_e::manual: {
|
||||
if (!session.enable_sops) {
|
||||
BOOST_LOG(warning) << R"(Sunshine is configured to change resolution manually, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)";
|
||||
}
|
||||
else {
|
||||
if (!parse_resolution_string(video_config.dd.manual_resolution, config.m_resolution)) {
|
||||
BOOST_LOG(error) << "Failed to parse manual resolution string!";
|
||||
case resolution_option_e::automatic:
|
||||
{
|
||||
if (!session.enable_sops) {
|
||||
BOOST_LOG(warning) << R"(Sunshine is configured to change resolution automatically, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)";
|
||||
} else if (session.width >= 0 && session.height >= 0) {
|
||||
config.m_resolution = Resolution {
|
||||
static_cast<unsigned int>(session.width),
|
||||
static_cast<unsigned int>(session.height)
|
||||
};
|
||||
} else {
|
||||
BOOST_LOG(error) << "Resolution provided by client session config is invalid: " << session.width << "x" << session.height;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case resolution_option_e::manual:
|
||||
{
|
||||
if (!session.enable_sops) {
|
||||
BOOST_LOG(warning) << R"(Sunshine is configured to change resolution manually, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)";
|
||||
} else {
|
||||
if (!parse_resolution_string(video_config.dd.manual_resolution, config.m_resolution)) {
|
||||
BOOST_LOG(error) << "Failed to parse manual resolution string!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.m_resolution) {
|
||||
BOOST_LOG(error) << "Manual resolution must be specified!";
|
||||
return false;
|
||||
if (!config.m_resolution) {
|
||||
BOOST_LOG(error) << "Manual resolution must be specified!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case resolution_option_e::disabled:
|
||||
break;
|
||||
}
|
||||
|
|
@ -375,33 +361,33 @@ namespace display_device {
|
|||
* const bool success = parse_refresh_rate_option(video_config, *launch_session, config);
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
parse_refresh_rate_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) {
|
||||
bool parse_refresh_rate_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) {
|
||||
using refresh_rate_option_e = config::video_t::dd_t::refresh_rate_option_e;
|
||||
|
||||
switch (video_config.dd.refresh_rate_option) {
|
||||
case refresh_rate_option_e::automatic: {
|
||||
if (session.fps >= 0) {
|
||||
config.m_refresh_rate = Rational { static_cast<unsigned int>(session.fps), 1 };
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "FPS value provided by client session config is invalid: " << session.fps;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case refresh_rate_option_e::manual: {
|
||||
if (!parse_refresh_rate_string(video_config.dd.manual_refresh_rate, config.m_refresh_rate)) {
|
||||
BOOST_LOG(error) << "Failed to parse manual refresh rate string!";
|
||||
return false;
|
||||
case refresh_rate_option_e::automatic:
|
||||
{
|
||||
if (session.fps >= 0) {
|
||||
config.m_refresh_rate = Rational {static_cast<unsigned int>(session.fps), 1};
|
||||
} else {
|
||||
BOOST_LOG(error) << "FPS value provided by client session config is invalid: " << session.fps;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case refresh_rate_option_e::manual:
|
||||
{
|
||||
if (!parse_refresh_rate_string(video_config.dd.manual_refresh_rate, config.m_refresh_rate)) {
|
||||
BOOST_LOG(error) << "Failed to parse manual refresh rate string!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!config.m_refresh_rate) {
|
||||
BOOST_LOG(error) << "Manual refresh rate must be specified!";
|
||||
return false;
|
||||
if (!config.m_refresh_rate) {
|
||||
BOOST_LOG(error) << "Manual refresh rate must be specified!";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case refresh_rate_option_e::disabled:
|
||||
break;
|
||||
}
|
||||
|
|
@ -422,8 +408,7 @@ namespace display_device {
|
|||
* const auto hdr_option = parse_hdr_option(video_config, *launch_session);
|
||||
* @examples_end
|
||||
*/
|
||||
std::optional<HdrState>
|
||||
parse_hdr_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) {
|
||||
std::optional<HdrState> parse_hdr_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) {
|
||||
using hdr_option_e = config::video_t::dd_t::hdr_option_e;
|
||||
|
||||
switch (video_config.dd.hdr_option) {
|
||||
|
|
@ -450,11 +435,10 @@ namespace display_device {
|
|||
* @param video_config User's video related configuration.
|
||||
* @returns Enum value if remapping can be performed, null optional if remapping shall be skipped.
|
||||
*/
|
||||
std::optional<remapping_type_e>
|
||||
determine_remapping_type(const config::video_t &video_config) {
|
||||
std::optional<remapping_type_e> determine_remapping_type(const config::video_t &video_config) {
|
||||
using dd_t = config::video_t::dd_t;
|
||||
const bool auto_resolution { video_config.dd.resolution_option == dd_t::resolution_option_e::automatic };
|
||||
const bool auto_refresh_rate { video_config.dd.refresh_rate_option == dd_t::refresh_rate_option_e::automatic };
|
||||
const bool auto_resolution {video_config.dd.resolution_option == dd_t::resolution_option_e::automatic};
|
||||
const bool auto_refresh_rate {video_config.dd.refresh_rate_option == dd_t::refresh_rate_option_e::automatic};
|
||||
|
||||
if (auto_resolution && auto_refresh_rate) {
|
||||
return remapping_type_e::mixed;
|
||||
|
|
@ -486,8 +470,7 @@ namespace display_device {
|
|||
* @param type Remapping type to check.
|
||||
* @returns True if resolution is to be mapped, false otherwise.
|
||||
*/
|
||||
bool
|
||||
is_resolution_mapped(const remapping_type_e type) {
|
||||
bool is_resolution_mapped(const remapping_type_e type) {
|
||||
return type == remapping_type_e::resolution_only || type == remapping_type_e::mixed;
|
||||
}
|
||||
|
||||
|
|
@ -496,8 +479,7 @@ namespace display_device {
|
|||
* @param type Remapping type to check.
|
||||
* @returns True if FPS is to be mapped, false otherwise.
|
||||
*/
|
||||
bool
|
||||
is_fps_mapped(const remapping_type_e type) {
|
||||
bool is_fps_mapped(const remapping_type_e type) {
|
||||
return type == remapping_type_e::refresh_rate_only || type == remapping_type_e::mixed;
|
||||
}
|
||||
|
||||
|
|
@ -507,17 +489,16 @@ namespace display_device {
|
|||
* @param type Specify which entry fields should be parsed.
|
||||
* @returns Parsed structure or null optional if a necessary field could not be parsed.
|
||||
*/
|
||||
std::optional<parsed_remapping_entry_t>
|
||||
parse_remapping_entry(const config::video_t::dd_t::mode_remapping_entry_t &entry, const remapping_type_e type) {
|
||||
std::optional<parsed_remapping_entry_t> parse_remapping_entry(const config::video_t::dd_t::mode_remapping_entry_t &entry, const remapping_type_e type) {
|
||||
parsed_remapping_entry_t result {};
|
||||
|
||||
if (is_resolution_mapped(type) && (!parse_resolution_string(entry.requested_resolution, result.requested_resolution) ||
|
||||
!parse_resolution_string(entry.final_resolution, result.final_resolution))) {
|
||||
!parse_resolution_string(entry.final_resolution, result.final_resolution))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (is_fps_mapped(type) && (!parse_refresh_rate_string(entry.requested_fps, result.requested_fps, false) ||
|
||||
!parse_refresh_rate_string(entry.final_refresh_rate, result.final_refresh_rate))) {
|
||||
!parse_refresh_rate_string(entry.final_refresh_rate, result.final_refresh_rate))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -539,14 +520,13 @@ namespace display_device {
|
|||
* const bool success = remap_display_mode_if_needed(video_config, *launch_session, config);
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
remap_display_mode_if_needed(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) {
|
||||
const auto remapping_type { determine_remapping_type(video_config) };
|
||||
bool remap_display_mode_if_needed(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) {
|
||||
const auto remapping_type {determine_remapping_type(video_config)};
|
||||
if (!remapping_type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto &remapping_list { [&]() {
|
||||
const auto &remapping_list {[&]() {
|
||||
using enum remapping_type_e;
|
||||
|
||||
switch (*remapping_type) {
|
||||
|
|
@ -558,7 +538,7 @@ namespace display_device {
|
|||
default:
|
||||
return video_config.dd.mode_remapping.mixed;
|
||||
}
|
||||
}() };
|
||||
}()};
|
||||
|
||||
if (remapping_list.empty()) {
|
||||
BOOST_LOG(debug) << "No values are available for display mode remapping.";
|
||||
|
|
@ -566,9 +546,9 @@ namespace display_device {
|
|||
}
|
||||
BOOST_LOG(debug) << "Trying to remap display modes...";
|
||||
|
||||
const auto entry_to_string { [type = *remapping_type](const config::video_t::dd_t::mode_remapping_entry_t &entry) {
|
||||
const bool mapping_resolution { is_resolution_mapped(type) };
|
||||
const bool mapping_fps { is_fps_mapped(type) };
|
||||
const auto entry_to_string {[type = *remapping_type](const config::video_t::dd_t::mode_remapping_entry_t &entry) {
|
||||
const bool mapping_resolution {is_resolution_mapped(type)};
|
||||
const bool mapping_fps {is_fps_mapped(type)};
|
||||
|
||||
// clang-format off
|
||||
return (mapping_resolution ? " - requested resolution: "s + entry.requested_resolution + "\n" : "") +
|
||||
|
|
@ -576,10 +556,10 @@ namespace display_device {
|
|||
(mapping_resolution ? " - final resolution: "s + entry.final_resolution + "\n" : "") +
|
||||
(mapping_fps ? " - final refresh rate: "s + entry.final_refresh_rate : "");
|
||||
// clang-format on
|
||||
} };
|
||||
}};
|
||||
|
||||
for (const auto &entry : remapping_list) {
|
||||
const auto parsed_entry { parse_remapping_entry(entry, *remapping_type) };
|
||||
const auto parsed_entry {parse_remapping_entry(entry, *remapping_type)};
|
||||
if (!parsed_entry) {
|
||||
BOOST_LOG(error) << "Failed to parse remapping entry from:\n"
|
||||
<< entry_to_string(entry);
|
||||
|
|
@ -632,16 +612,18 @@ namespace display_device {
|
|||
* @param video_config User's video related configuration.
|
||||
* @return An interface or nullptr if the OS does not support the interface.
|
||||
*/
|
||||
std::unique_ptr<SettingsManagerInterface>
|
||||
make_settings_manager([[maybe_unused]] const std::filesystem::path &persistence_filepath, [[maybe_unused]] const config::video_t &video_config) {
|
||||
std::unique_ptr<SettingsManagerInterface> make_settings_manager([[maybe_unused]] const std::filesystem::path &persistence_filepath, [[maybe_unused]] const config::video_t &video_config) {
|
||||
#ifdef _WIN32
|
||||
return std::make_unique<SettingsManager>(
|
||||
std::make_shared<WinDisplayDevice>(std::make_shared<WinApiLayer>()),
|
||||
std::make_shared<sunshine_audio_context_t>(),
|
||||
std::make_unique<PersistentState>(
|
||||
std::make_shared<FileSettingsPersistence>(persistence_filepath)),
|
||||
std::make_shared<FileSettingsPersistence>(persistence_filepath)
|
||||
),
|
||||
WinWorkarounds {
|
||||
.m_hdr_blank_delay = video_config.dd.wa.hdr_toggle ? std::make_optional(500ms) : std::nullopt });
|
||||
.m_hdr_blank_delay = video_config.dd.wa.hdr_toggle ? std::make_optional(500ms) : std::nullopt
|
||||
}
|
||||
);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
|
|
@ -660,17 +642,16 @@ namespace display_device {
|
|||
* @brief Reverts the configuration based on the provided option.
|
||||
* @note This is function does not lock mutex.
|
||||
*/
|
||||
void
|
||||
revert_configuration_unlocked(const revert_option_e option) {
|
||||
void revert_configuration_unlocked(const revert_option_e option) {
|
||||
if (!DD_DATA.sm_instance) {
|
||||
// Platform is not supported, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: by default the executor function is immediately executed in the calling thread. With delay, we want to avoid that.
|
||||
SchedulerOptions scheduler_option { .m_sleep_durations = { DEFAULT_RETRY_INTERVAL } };
|
||||
SchedulerOptions scheduler_option {.m_sleep_durations = {DEFAULT_RETRY_INTERVAL}};
|
||||
if (option == revert_option_e::try_indefinitely_with_delay && DD_DATA.config_revert_delay > std::chrono::milliseconds::zero()) {
|
||||
scheduler_option.m_sleep_durations = { DD_DATA.config_revert_delay, DEFAULT_RETRY_INTERVAL };
|
||||
scheduler_option.m_sleep_durations = {DD_DATA.config_revert_delay, DEFAULT_RETRY_INTERVAL};
|
||||
scheduler_option.m_execution = SchedulerOptions::Execution::ScheduledOnly;
|
||||
}
|
||||
|
||||
|
|
@ -681,17 +662,21 @@ namespace display_device {
|
|||
return;
|
||||
}
|
||||
|
||||
auto available_devices { [&settings_iface]() {
|
||||
const auto devices { settings_iface.enumAvailableDevices() };
|
||||
auto available_devices {[&settings_iface]() {
|
||||
const auto devices {settings_iface.enumAvailableDevices()};
|
||||
std::set<std::string> parsed_devices;
|
||||
|
||||
std::transform(
|
||||
std::begin(devices), std::end(devices),
|
||||
std::begin(devices),
|
||||
std::end(devices),
|
||||
std::inserter(parsed_devices, std::end(parsed_devices)),
|
||||
[](const auto &device) { return device.m_device_id + " - " + device.m_friendly_name; });
|
||||
[](const auto &device) {
|
||||
return device.m_device_id + " - " + device.m_friendly_name;
|
||||
}
|
||||
);
|
||||
|
||||
return parsed_devices;
|
||||
}() };
|
||||
}()};
|
||||
if (available_devices == tried_out_devices) {
|
||||
BOOST_LOG(debug) << "Skipping reverting configuration, because no newly added/removed devices were detected since last check. Currently available devices:\n"
|
||||
<< toJson(available_devices);
|
||||
|
|
@ -699,11 +684,10 @@ namespace display_device {
|
|||
}
|
||||
|
||||
using enum SettingsManagerInterface::RevertResult;
|
||||
if (const auto result { settings_iface.revertSettings() }; result == Ok) {
|
||||
if (const auto result {settings_iface.revertSettings()}; result == Ok) {
|
||||
stop_token.requestStop();
|
||||
return;
|
||||
}
|
||||
else if (result == ApiTemporarilyUnavailable) {
|
||||
} else if (result == ApiTemporarilyUnavailable) {
|
||||
// Do nothing and retry next time
|
||||
return;
|
||||
}
|
||||
|
|
@ -713,13 +697,12 @@ namespace display_device {
|
|||
<< toJson(available_devices);
|
||||
tried_out_devices.swap(available_devices);
|
||||
},
|
||||
scheduler_option);
|
||||
scheduler_option);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<platf::deinit_t>
|
||||
init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config) {
|
||||
std::lock_guard lock { DD_DATA.mutex };
|
||||
std::unique_ptr<platf::deinit_t> init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config) {
|
||||
std::lock_guard lock {DD_DATA.mutex};
|
||||
// We can support re-init without any issues, however we should make sure to clean up first!
|
||||
revert_configuration_unlocked(revert_option_e::try_once);
|
||||
DD_DATA.config_revert_delay = video_config.dd.config_revert_delay;
|
||||
|
|
@ -727,10 +710,12 @@ namespace display_device {
|
|||
|
||||
// If we fail to create settings manager, this means platform is not supported, and
|
||||
// we will need to provided error-free pass-trough in other methods
|
||||
if (auto settings_manager { make_settings_manager(persistence_filepath, video_config) }) {
|
||||
if (auto settings_manager {make_settings_manager(persistence_filepath, video_config)}) {
|
||||
DD_DATA.sm_instance = std::make_unique<RetryScheduler<SettingsManagerInterface>>(std::move(settings_manager));
|
||||
|
||||
const auto available_devices { DD_DATA.sm_instance->execute([](auto &settings_iface) { return settings_iface.enumAvailableDevices(); }) };
|
||||
const auto available_devices {DD_DATA.sm_instance->execute([](auto &settings_iface) {
|
||||
return settings_iface.enumAvailableDevices();
|
||||
})};
|
||||
BOOST_LOG(info) << "Currently available display devices:\n"
|
||||
<< toJson(available_devices);
|
||||
|
||||
|
|
@ -742,44 +727,44 @@ namespace display_device {
|
|||
class deinit_t: public platf::deinit_t {
|
||||
public:
|
||||
~deinit_t() override {
|
||||
std::lock_guard lock { DD_DATA.mutex };
|
||||
std::lock_guard lock {DD_DATA.mutex};
|
||||
try {
|
||||
// This may throw if used incorrectly. At the moment this will not happen, however
|
||||
// in case some unforeseen changes are made that could raise an exception,
|
||||
// we definitely don't want this to happen in destructor. Especially in the
|
||||
// deinit_t where the outcome does not really matter.
|
||||
revert_configuration_unlocked(revert_option_e::try_once);
|
||||
}
|
||||
catch (std::exception &err) {
|
||||
} catch (std::exception &err) {
|
||||
BOOST_LOG(fatal) << err.what();
|
||||
}
|
||||
|
||||
DD_DATA.sm_instance = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
return std::make_unique<deinit_t>();
|
||||
}
|
||||
|
||||
std::string
|
||||
map_output_name(const std::string &output_name) {
|
||||
std::lock_guard lock { DD_DATA.mutex };
|
||||
std::string map_output_name(const std::string &output_name) {
|
||||
std::lock_guard lock {DD_DATA.mutex};
|
||||
if (!DD_DATA.sm_instance) {
|
||||
// Fallback to giving back the output name if the platform is not supported.
|
||||
return output_name;
|
||||
}
|
||||
|
||||
return DD_DATA.sm_instance->execute([&output_name](auto &settings_iface) { return settings_iface.getDisplayName(output_name); });
|
||||
return DD_DATA.sm_instance->execute([&output_name](auto &settings_iface) {
|
||||
return settings_iface.getDisplayName(output_name);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) {
|
||||
const auto result { parse_configuration(video_config, session) };
|
||||
if (const auto *parsed_config { std::get_if<SingleDisplayConfiguration>(&result) }; parsed_config) {
|
||||
void configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) {
|
||||
const auto result {parse_configuration(video_config, session)};
|
||||
if (const auto *parsed_config {std::get_if<SingleDisplayConfiguration>(&result)}; parsed_config) {
|
||||
configure_display(*parsed_config);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *disabled { std::get_if<configuration_disabled_tag_t>(&result) }; disabled) {
|
||||
if (const auto *disabled {std::get_if<configuration_disabled_tag_t>(&result)}; disabled) {
|
||||
revert_configuration();
|
||||
return;
|
||||
}
|
||||
|
|
@ -788,9 +773,8 @@ namespace display_device {
|
|||
// want to revert active configuration in case we have any
|
||||
}
|
||||
|
||||
void
|
||||
configure_display(const SingleDisplayConfiguration &config) {
|
||||
std::lock_guard lock { DD_DATA.mutex };
|
||||
void configure_display(const SingleDisplayConfiguration &config) {
|
||||
std::lock_guard lock {DD_DATA.mutex};
|
||||
if (!DD_DATA.sm_instance) {
|
||||
// Platform is not supported, nothing to do.
|
||||
return;
|
||||
|
|
@ -803,18 +787,16 @@ namespace display_device {
|
|||
stop_token.requestStop();
|
||||
}
|
||||
},
|
||||
{ .m_sleep_durations = { DEFAULT_RETRY_INTERVAL } });
|
||||
{.m_sleep_durations = {DEFAULT_RETRY_INTERVAL}});
|
||||
}
|
||||
|
||||
void
|
||||
revert_configuration() {
|
||||
std::lock_guard lock { DD_DATA.mutex };
|
||||
void revert_configuration() {
|
||||
std::lock_guard lock {DD_DATA.mutex};
|
||||
revert_configuration_unlocked(revert_option_e::try_indefinitely_with_delay);
|
||||
}
|
||||
|
||||
bool
|
||||
reset_persistence() {
|
||||
std::lock_guard lock { DD_DATA.mutex };
|
||||
bool reset_persistence() {
|
||||
std::lock_guard lock {DD_DATA.mutex};
|
||||
if (!DD_DATA.sm_instance) {
|
||||
// Platform is not supported, assume success.
|
||||
return true;
|
||||
|
|
@ -828,9 +810,8 @@ namespace display_device {
|
|||
});
|
||||
}
|
||||
|
||||
std::variant<failed_to_parse_tag_t, configuration_disabled_tag_t, SingleDisplayConfiguration>
|
||||
parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) {
|
||||
const auto device_prep { parse_device_prep_option(video_config) };
|
||||
std::variant<failed_to_parse_tag_t, configuration_disabled_tag_t, SingleDisplayConfiguration> parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) {
|
||||
const auto device_prep {parse_device_prep_option(video_config)};
|
||||
if (!device_prep) {
|
||||
return configuration_disabled_tag_t {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,18 +4,22 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <display_device/types.h>
|
||||
// standard includes
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
// lib includes
|
||||
#include <display_device/types.h>
|
||||
|
||||
// forward declarations
|
||||
namespace platf {
|
||||
class deinit_t;
|
||||
}
|
||||
|
||||
namespace config {
|
||||
struct video_t;
|
||||
}
|
||||
|
||||
namespace rtsp_stream {
|
||||
struct launch_session_t;
|
||||
}
|
||||
|
|
@ -32,8 +36,7 @@ namespace display_device {
|
|||
* const auto init_guard { init("/my/persitence/file.state", video_config) };
|
||||
* @examples_end
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t>
|
||||
init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config);
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t> init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config);
|
||||
|
||||
/**
|
||||
* @brief Map the output name to a specific display.
|
||||
|
|
@ -45,8 +48,7 @@ namespace display_device {
|
|||
* const auto mapped_name_custom { map_output_name("{some-device-id}") };
|
||||
* @examples_end
|
||||
*/
|
||||
[[nodiscard]] std::string
|
||||
map_output_name(const std::string &output_name);
|
||||
[[nodiscard]] std::string map_output_name(const std::string &output_name);
|
||||
|
||||
/**
|
||||
* @brief Configure the display device based on the user configuration and the session information.
|
||||
|
|
@ -62,8 +64,7 @@ namespace display_device {
|
|||
* configure_display(video_config, *launch_session);
|
||||
* @examples_end
|
||||
*/
|
||||
void
|
||||
configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session);
|
||||
void configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session);
|
||||
|
||||
/**
|
||||
* @brief Configure the display device using the provided configuration.
|
||||
|
|
@ -83,8 +84,7 @@ namespace display_device {
|
|||
* configure_display(valid_config);
|
||||
* @examples_end
|
||||
*/
|
||||
void
|
||||
configure_display(const SingleDisplayConfiguration &config);
|
||||
void configure_display(const SingleDisplayConfiguration &config);
|
||||
|
||||
/**
|
||||
* @brief Revert the display configuration and restore the previous state.
|
||||
|
|
@ -96,8 +96,7 @@ namespace display_device {
|
|||
* revert_configuration();
|
||||
* @examples_end
|
||||
*/
|
||||
void
|
||||
revert_configuration();
|
||||
void revert_configuration();
|
||||
|
||||
/**
|
||||
* @brief Reset the persistence and currently held initial display state.
|
||||
|
|
@ -119,8 +118,7 @@ namespace display_device {
|
|||
* const auto result = reset_persistence();
|
||||
* @examples_end
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
reset_persistence();
|
||||
[[nodiscard]] bool reset_persistence();
|
||||
|
||||
/**
|
||||
* @brief A tag structure indicating that configuration parsing has failed.
|
||||
|
|
@ -150,6 +148,5 @@ namespace display_device {
|
|||
* }
|
||||
* @examples_end
|
||||
*/
|
||||
[[nodiscard]] std::variant<failed_to_parse_tag_t, configuration_disabled_tag_t, SingleDisplayConfiguration>
|
||||
parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session);
|
||||
[[nodiscard]] std::variant<failed_to_parse_tag_t, configuration_disabled_tag_t, SingleDisplayConfiguration> parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session);
|
||||
} // namespace display_device
|
||||
|
|
|
|||
|
|
@ -26,21 +26,18 @@ extern "C" {
|
|||
|
||||
using namespace std::literals;
|
||||
|
||||
void
|
||||
launch_ui() {
|
||||
void launch_ui() {
|
||||
std::string url = "https://localhost:" + std::to_string(net::map_port(confighttp::PORT_HTTPS));
|
||||
platf::open_url(url);
|
||||
}
|
||||
|
||||
void
|
||||
launch_ui_with_path(std::string path) {
|
||||
void launch_ui_with_path(std::string path) {
|
||||
std::string url = "https://localhost:" + std::to_string(net::map_port(confighttp::PORT_HTTPS)) + path;
|
||||
platf::open_url(url);
|
||||
}
|
||||
|
||||
namespace args {
|
||||
int
|
||||
creds(const char *name, int argc, char *argv[]) {
|
||||
int creds(const char *name, int argc, char *argv[]) {
|
||||
if (argc < 2 || argv[0] == "help"sv || argv[1] == "help"sv) {
|
||||
help(name);
|
||||
}
|
||||
|
|
@ -50,21 +47,18 @@ namespace args {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
help(const char *name) {
|
||||
int help(const char *name) {
|
||||
logging::print_help(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
version() {
|
||||
int version() {
|
||||
// version was already logged at startup
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int
|
||||
restore_nvprefs_undo() {
|
||||
int restore_nvprefs_undo() {
|
||||
if (nvprefs_instance.load()) {
|
||||
nvprefs_instance.restore_from_and_delete_undo_file_if_exists();
|
||||
nvprefs_instance.unload();
|
||||
|
|
@ -78,8 +72,7 @@ namespace lifetime {
|
|||
char **argv;
|
||||
std::atomic_int desired_exit_code;
|
||||
|
||||
void
|
||||
exit_sunshine(int exit_code, bool async) {
|
||||
void exit_sunshine(int exit_code, bool async) {
|
||||
// Store the exit code of the first exit_sunshine() call
|
||||
int zero = 0;
|
||||
desired_exit_code.compare_exchange_strong(zero, exit_code);
|
||||
|
|
@ -94,8 +87,7 @@ namespace lifetime {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
debug_trap() {
|
||||
void debug_trap() {
|
||||
#ifdef _WIN32
|
||||
DebugBreak();
|
||||
#else
|
||||
|
|
@ -103,22 +95,19 @@ namespace lifetime {
|
|||
#endif
|
||||
}
|
||||
|
||||
char **
|
||||
get_argv() {
|
||||
char **get_argv() {
|
||||
return argv;
|
||||
}
|
||||
} // namespace lifetime
|
||||
|
||||
void
|
||||
log_publisher_data() {
|
||||
void log_publisher_data() {
|
||||
BOOST_LOG(info) << "Package Publisher: "sv << SUNSHINE_PUBLISHER_NAME;
|
||||
BOOST_LOG(info) << "Publisher Website: "sv << SUNSHINE_PUBLISHER_WEBSITE;
|
||||
BOOST_LOG(info) << "Get support: "sv << SUNSHINE_PUBLISHER_ISSUE_URL;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool
|
||||
is_gamestream_enabled() {
|
||||
bool is_gamestream_enabled() {
|
||||
DWORD enabled;
|
||||
DWORD size = sizeof(enabled);
|
||||
return RegGetValueW(
|
||||
|
|
@ -128,7 +117,8 @@ is_gamestream_enabled() {
|
|||
RRF_RT_REG_DWORD,
|
||||
nullptr,
|
||||
&enabled,
|
||||
&size) == ERROR_SUCCESS &&
|
||||
&size
|
||||
) == ERROR_SUCCESS &&
|
||||
enabled != 0;
|
||||
}
|
||||
|
||||
|
|
@ -168,8 +158,7 @@ namespace service_ctrl {
|
|||
/**
|
||||
* @brief Asynchronously starts the Sunshine service.
|
||||
*/
|
||||
bool
|
||||
start_service() {
|
||||
bool start_service() {
|
||||
if (!service_handle) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -189,8 +178,7 @@ namespace service_ctrl {
|
|||
* @brief Query the service status.
|
||||
* @param status The SERVICE_STATUS struct to populate.
|
||||
*/
|
||||
bool
|
||||
query_service_status(SERVICE_STATUS &status) {
|
||||
bool query_service_status(SERVICE_STATUS &status) {
|
||||
if (!service_handle) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -209,9 +197,8 @@ namespace service_ctrl {
|
|||
SC_HANDLE service_handle = NULL;
|
||||
};
|
||||
|
||||
bool
|
||||
is_service_running() {
|
||||
service_controller sc { SERVICE_QUERY_STATUS };
|
||||
bool is_service_running() {
|
||||
service_controller sc {SERVICE_QUERY_STATUS};
|
||||
|
||||
SERVICE_STATUS status;
|
||||
if (!sc.query_service_status(status)) {
|
||||
|
|
@ -221,9 +208,8 @@ namespace service_ctrl {
|
|||
return status.dwCurrentState == SERVICE_RUNNING;
|
||||
}
|
||||
|
||||
bool
|
||||
start_service() {
|
||||
service_controller sc { SERVICE_QUERY_STATUS | SERVICE_START };
|
||||
bool start_service() {
|
||||
service_controller sc {SERVICE_QUERY_STATUS | SERVICE_START};
|
||||
|
||||
std::cout << "Starting Sunshine..."sv;
|
||||
|
||||
|
|
@ -247,8 +233,7 @@ namespace service_ctrl {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
wait_for_ui_ready() {
|
||||
bool wait_for_ui_ready() {
|
||||
std::cout << "Waiting for Web UI to be ready...";
|
||||
|
||||
// Wait up to 30 seconds for the web UI to start
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@
|
|||
* launch_ui();
|
||||
* @examples_end
|
||||
*/
|
||||
void
|
||||
launch_ui();
|
||||
void launch_ui();
|
||||
|
||||
/**
|
||||
* @brief Launch the Web UI at a specific endpoint.
|
||||
|
|
@ -27,8 +26,7 @@ launch_ui();
|
|||
* launch_ui_with_path("/pin");
|
||||
* @examples_end
|
||||
*/
|
||||
void
|
||||
launch_ui_with_path(std::string path);
|
||||
void launch_ui_with_path(std::string path);
|
||||
|
||||
/**
|
||||
* @brief Functions for handling command line arguments.
|
||||
|
|
@ -43,8 +41,7 @@ namespace args {
|
|||
* creds("sunshine", 2, {"new_username", "new_password"});
|
||||
* @examples_end
|
||||
*/
|
||||
int
|
||||
creds(const char *name, int argc, char *argv[]);
|
||||
int creds(const char *name, int argc, char *argv[]);
|
||||
|
||||
/**
|
||||
* @brief Print help to stdout, then exit.
|
||||
|
|
@ -53,8 +50,7 @@ namespace args {
|
|||
* help("sunshine");
|
||||
* @examples_end
|
||||
*/
|
||||
int
|
||||
help(const char *name);
|
||||
int help(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Print the version to stdout, then exit.
|
||||
|
|
@ -62,8 +58,7 @@ namespace args {
|
|||
* version();
|
||||
* @examples_end
|
||||
*/
|
||||
int
|
||||
version();
|
||||
int version();
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
|
|
@ -75,8 +70,7 @@ namespace args {
|
|||
* restore_nvprefs_undo();
|
||||
* @examples_end
|
||||
*/
|
||||
int
|
||||
restore_nvprefs_undo();
|
||||
int restore_nvprefs_undo();
|
||||
#endif
|
||||
} // namespace args
|
||||
|
||||
|
|
@ -92,35 +86,30 @@ namespace lifetime {
|
|||
* @param exit_code The exit code to return from main().
|
||||
* @param async Specifies whether our termination will be non-blocking.
|
||||
*/
|
||||
void
|
||||
exit_sunshine(int exit_code, bool async);
|
||||
void exit_sunshine(int exit_code, bool async);
|
||||
|
||||
/**
|
||||
* @brief Breaks into the debugger or terminates Sunshine if no debugger is attached.
|
||||
*/
|
||||
void
|
||||
debug_trap();
|
||||
void debug_trap();
|
||||
|
||||
/**
|
||||
* @brief Get the argv array passed to main().
|
||||
*/
|
||||
char **
|
||||
get_argv();
|
||||
char **get_argv();
|
||||
} // namespace lifetime
|
||||
|
||||
/**
|
||||
* @brief Log the publisher metadata provided from CMake.
|
||||
*/
|
||||
void
|
||||
log_publisher_data();
|
||||
void log_publisher_data();
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
* @brief Check if NVIDIA's GameStream software is running.
|
||||
* @return `true` if GameStream is enabled, `false` otherwise.
|
||||
*/
|
||||
bool
|
||||
is_gamestream_enabled();
|
||||
bool is_gamestream_enabled();
|
||||
|
||||
/**
|
||||
* @brief Namespace for controlling the Sunshine service model on Windows.
|
||||
|
|
@ -132,8 +121,7 @@ namespace service_ctrl {
|
|||
* is_service_running();
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
is_service_running();
|
||||
bool is_service_running();
|
||||
|
||||
/**
|
||||
* @brief Start the service and wait for startup to complete.
|
||||
|
|
@ -141,8 +129,7 @@ namespace service_ctrl {
|
|||
* start_service();
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
start_service();
|
||||
bool start_service();
|
||||
|
||||
/**
|
||||
* @brief Wait for the UI to be ready after Sunshine startup.
|
||||
|
|
@ -150,7 +137,6 @@ namespace service_ctrl {
|
|||
* wait_for_ui_ready();
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
wait_for_ui_ready();
|
||||
bool wait_for_ui_ready();
|
||||
} // namespace service_ctrl
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@
|
|||
#include "logging.h"
|
||||
|
||||
namespace file_handler {
|
||||
std::string
|
||||
get_parent_directory(const std::string &path) {
|
||||
std::string get_parent_directory(const std::string &path) {
|
||||
// remove any trailing path separators
|
||||
std::string trimmed_path = path;
|
||||
while (!trimmed_path.empty() && trimmed_path.back() == '/') {
|
||||
|
|
@ -24,8 +23,7 @@ namespace file_handler {
|
|||
return p.parent_path().string();
|
||||
}
|
||||
|
||||
bool
|
||||
make_directory(const std::string &path) {
|
||||
bool make_directory(const std::string &path) {
|
||||
// first, check if the directory already exists
|
||||
if (std::filesystem::exists(path)) {
|
||||
return true;
|
||||
|
|
@ -34,19 +32,17 @@ namespace file_handler {
|
|||
return std::filesystem::create_directories(path);
|
||||
}
|
||||
|
||||
std::string
|
||||
read_file(const char *path) {
|
||||
std::string read_file(const char *path) {
|
||||
if (!std::filesystem::exists(path)) {
|
||||
BOOST_LOG(debug) << "Missing file: " << path;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::ifstream in(path);
|
||||
return std::string { (std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>() };
|
||||
return std::string {(std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>()};
|
||||
}
|
||||
|
||||
int
|
||||
write_file(const char *path, const std::string_view &contents) {
|
||||
int write_file(const char *path, const std::string_view &contents) {
|
||||
std::ofstream out(path);
|
||||
|
||||
if (!out.is_open()) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
|
|
@ -18,8 +19,7 @@ namespace file_handler {
|
|||
* std::string parent_dir = get_parent_directory("path/to/file");
|
||||
* @examples_end
|
||||
*/
|
||||
std::string
|
||||
get_parent_directory(const std::string &path);
|
||||
std::string get_parent_directory(const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Make a directory.
|
||||
|
|
@ -29,8 +29,7 @@ namespace file_handler {
|
|||
* bool dir_created = make_directory("path/to/directory");
|
||||
* @examples_end
|
||||
*/
|
||||
bool
|
||||
make_directory(const std::string &path);
|
||||
bool make_directory(const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Read a file to string.
|
||||
|
|
@ -40,8 +39,7 @@ namespace file_handler {
|
|||
* std::string contents = read_file("path/to/file");
|
||||
* @examples_end
|
||||
*/
|
||||
std::string
|
||||
read_file(const char *path);
|
||||
std::string read_file(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Writes a file.
|
||||
|
|
@ -52,6 +50,5 @@ namespace file_handler {
|
|||
* int write_status = write_file("path/to/file", "file contents");
|
||||
* @examples_end
|
||||
*/
|
||||
int
|
||||
write_file(const char *path, const std::string_view &contents);
|
||||
int write_file(const char *path, const std::string_view &contents);
|
||||
} // namespace file_handler
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
* @file globals.cpp
|
||||
* @brief Definitions for globally accessible variables and functions.
|
||||
*/
|
||||
// local includes
|
||||
#include "globals.h"
|
||||
|
||||
safe::mail_t mail::man;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// local includes
|
||||
#include "entry_handler.h"
|
||||
#include "thread_pool.h"
|
||||
|
||||
|
|
@ -31,9 +32,9 @@ extern nvprefs::nvprefs_interface nvprefs_instance;
|
|||
* @brief Handles process-wide communication.
|
||||
*/
|
||||
namespace mail {
|
||||
#define MAIL(x) \
|
||||
#define MAIL(x) \
|
||||
constexpr auto x = std::string_view { \
|
||||
#x \
|
||||
#x \
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue