clang: adjust formatting rules (#1015)
This commit is contained in:
parent
79cf382cd9
commit
21eb4eb6dd
103 changed files with 26883 additions and 25173 deletions
|
|
@ -7,7 +7,7 @@
|
|||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignOperands: Align
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
|
|
@ -18,8 +18,9 @@ AllowShortFunctionsOnASingleLine: All
|
|||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AlignTrailingComments: false
|
||||
AlwaysBreakAfterReturnType: All
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
|
|
@ -37,32 +38,32 @@ BraceWrapping:
|
|||
SplitEmptyRecord: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 2
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: BeforeHash
|
||||
IndentWidth: 2
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: Never
|
||||
SpaceBeforeCtorInitializerColon: false
|
||||
SpaceBeforeInheritanceColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
|
|
|
|||
40
scripts/update_clang_format.py
Normal file
40
scripts/update_clang_format.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# standard imports
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
# variables
|
||||
directories = [
|
||||
'src',
|
||||
'tools',
|
||||
os.path.join('third-party', 'glad'),
|
||||
os.path.join('third-party', 'nvfbc'),
|
||||
os.path.join('third-party', 'wayland-protocols')
|
||||
]
|
||||
file_types = [
|
||||
'cpp',
|
||||
'h',
|
||||
'm',
|
||||
'mm'
|
||||
]
|
||||
|
||||
|
||||
def clang_format(file: str):
|
||||
print(f'Formatting {file} ...')
|
||||
subprocess.run(['clang-format', '-i', file])
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point.
|
||||
"""
|
||||
# walk the directories
|
||||
for directory in directories:
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
if os.path.isfile(file_path) and file.rsplit('.')[-1] in file_types:
|
||||
clang_format(file=file_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -11,11 +11,11 @@
|
|||
#include "utility.h"
|
||||
|
||||
namespace audio {
|
||||
using namespace std::literals;
|
||||
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
|
||||
using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<std::int16_t>>>;
|
||||
using namespace std::literals;
|
||||
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
|
||||
using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<std::int16_t>>>;
|
||||
|
||||
struct audio_ctx_t {
|
||||
struct audio_ctx_t {
|
||||
// We want to change the sink for the first stream only
|
||||
std::unique_ptr<std::atomic_bool> sink_flag;
|
||||
|
||||
|
|
@ -23,16 +23,19 @@ struct audio_ctx_t {
|
|||
|
||||
bool restore_sink;
|
||||
platf::sink_t sink;
|
||||
};
|
||||
};
|
||||
|
||||
static int start_audio_control(audio_ctx_t &ctx);
|
||||
static void stop_audio_control(audio_ctx_t &);
|
||||
static int
|
||||
start_audio_control(audio_ctx_t &ctx);
|
||||
static void
|
||||
stop_audio_control(audio_ctx_t &);
|
||||
|
||||
int map_stream(int channels, bool quality);
|
||||
int
|
||||
map_stream(int channels, bool quality);
|
||||
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
|
||||
opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] {
|
||||
opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] {
|
||||
{
|
||||
SAMPLE_RATE,
|
||||
2,
|
||||
|
|
@ -81,11 +84,12 @@ opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] {
|
|||
platf::speaker::map_surround71,
|
||||
2048000,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
auto control_shared = safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control);
|
||||
auto control_shared = safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control);
|
||||
|
||||
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])];
|
||||
|
||||
|
|
@ -105,11 +109,11 @@ void encodeThread(sample_queue_t samples, config_t config, void *channel_data) {
|
|||
opus_multistream_encoder_ctl(opus.get(), OPUS_SET_VBR(0));
|
||||
|
||||
auto frame_size = config.packetDuration * stream->sampleRate / 1000;
|
||||
while(auto sample = samples->pop()) {
|
||||
while (auto sample = samples->pop()) {
|
||||
buffer_t packet { 1400 };
|
||||
|
||||
int bytes = opus_multistream_encode(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size());
|
||||
if(bytes < 0) {
|
||||
if (bytes < 0) {
|
||||
BOOST_LOG(error) << "Couldn't encode audio: "sv << opus_strerror(bytes);
|
||||
packets->stop();
|
||||
|
||||
|
|
@ -119,19 +123,20 @@ void encodeThread(sample_queue_t samples, config_t config, void *channel_data) {
|
|||
packet.fake_resize(bytes);
|
||||
packets->raise(channel_data, std::move(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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])];
|
||||
|
||||
auto ref = control_shared.ref();
|
||||
if(!ref) {
|
||||
if (!ref) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &control = ref->control;
|
||||
if(!control) {
|
||||
if (!control) {
|
||||
shutdown_event->view();
|
||||
|
||||
return;
|
||||
|
|
@ -142,12 +147,12 @@ void capture(safe::mail_t mail, config_t config, void *channel_data) {
|
|||
// 2. Virtual if available
|
||||
// 3. Host
|
||||
std::string *sink = &ref->sink.host;
|
||||
if(!config::audio.sink.empty()) {
|
||||
if (!config::audio.sink.empty()) {
|
||||
sink = &config::audio.sink;
|
||||
}
|
||||
else if(ref->sink.null) {
|
||||
else if (ref->sink.null) {
|
||||
auto &null = *ref->sink.null;
|
||||
switch(stream->channelCount) {
|
||||
switch (stream->channelCount) {
|
||||
case 2:
|
||||
sink = &null.stereo;
|
||||
break;
|
||||
|
|
@ -161,17 +166,17 @@ void capture(safe::mail_t mail, config_t config, void *channel_data) {
|
|||
}
|
||||
|
||||
// Only the first to start a session may change the default sink
|
||||
if(!ref->sink_flag->exchange(true, std::memory_order_acquire)) {
|
||||
if (!ref->sink_flag->exchange(true, std::memory_order_acquire)) {
|
||||
ref->restore_sink = !config.flags[config_t::HOST_AUDIO];
|
||||
|
||||
// If the sink is empty (Host has no sink!), definately switch to the virtual.
|
||||
if(ref->sink.host.empty()) {
|
||||
if(control->set_sink(*sink)) {
|
||||
if (ref->sink.host.empty()) {
|
||||
if (control->set_sink(*sink)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If the client requests audio on the host, don't change the default sink
|
||||
else if(!config.flags[config_t::HOST_AUDIO] && control->set_sink(*sink)) {
|
||||
else if (!config.flags[config_t::HOST_AUDIO] && control->set_sink(*sink)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -193,18 +198,18 @@ void capture(safe::mail_t mail, config_t config, void *channel_data) {
|
|||
int samples_per_frame = frame_size * stream->channelCount;
|
||||
|
||||
auto mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size);
|
||||
if(!mic) {
|
||||
if (!mic) {
|
||||
BOOST_LOG(error) << "Couldn't create audio input"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while(!shutdown_event->peek()) {
|
||||
while (!shutdown_event->peek()) {
|
||||
std::vector<std::int16_t> sample_buffer;
|
||||
sample_buffer.resize(samples_per_frame);
|
||||
|
||||
auto status = mic->sample(sample_buffer);
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case platf::capture_e::ok:
|
||||
break;
|
||||
case platf::capture_e::timeout:
|
||||
|
|
@ -212,7 +217,7 @@ void capture(safe::mail_t mail, config_t config, void *channel_data) {
|
|||
case platf::capture_e::reinit:
|
||||
mic.reset();
|
||||
mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size);
|
||||
if(!mic) {
|
||||
if (!mic) {
|
||||
BOOST_LOG(error) << "Couldn't re-initialize audio input"sv;
|
||||
|
||||
return;
|
||||
|
|
@ -224,11 +229,12 @@ void capture(safe::mail_t mail, config_t config, void *channel_data) {
|
|||
|
||||
samples->raise(std::move(sample_buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int map_stream(int channels, bool quality) {
|
||||
int
|
||||
map_stream(int channels, bool quality) {
|
||||
int shift = quality ? 1 : 0;
|
||||
switch(channels) {
|
||||
switch (channels) {
|
||||
case 2:
|
||||
return STEREO + shift;
|
||||
case 6:
|
||||
|
|
@ -237,9 +243,10 @@ int map_stream(int channels, bool quality) {
|
|||
return SURROUND71 + shift;
|
||||
}
|
||||
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;
|
||||
});
|
||||
|
|
@ -249,12 +256,12 @@ int start_audio_control(audio_ctx_t &ctx) {
|
|||
// The default sink has not been replaced yet.
|
||||
ctx.restore_sink = false;
|
||||
|
||||
if(!(ctx.control = platf::audio_control())) {
|
||||
if (!(ctx.control = platf::audio_control())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto sink = ctx.control->sink_info();
|
||||
if(!sink) {
|
||||
if (!sink) {
|
||||
// Let the calling code know it failed
|
||||
ctx.control.reset();
|
||||
return 0;
|
||||
|
|
@ -264,18 +271,19 @@ int start_audio_control(audio_ctx_t &ctx) {
|
|||
|
||||
fg.disable();
|
||||
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) {
|
||||
if (!ctx.restore_sink) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink;
|
||||
if(!sink.empty()) {
|
||||
if (!sink.empty()) {
|
||||
// Best effort, it's allowed to fail
|
||||
ctx.control->set_sink(sink);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace audio
|
||||
|
|
|
|||
21
src/audio.h
21
src/audio.h
|
|
@ -4,7 +4,7 @@
|
|||
#include "thread_safe.h"
|
||||
#include "utility.h"
|
||||
namespace audio {
|
||||
enum stream_config_e : int {
|
||||
enum stream_config_e : int {
|
||||
STEREO,
|
||||
HIGH_STEREO,
|
||||
SURROUND51,
|
||||
|
|
@ -12,20 +12,20 @@ enum stream_config_e : int {
|
|||
SURROUND71,
|
||||
HIGH_SURROUND71,
|
||||
MAX_STREAM_CONFIG
|
||||
};
|
||||
};
|
||||
|
||||
struct opus_stream_config_t {
|
||||
struct opus_stream_config_t {
|
||||
std::int32_t sampleRate;
|
||||
int channelCount;
|
||||
int streams;
|
||||
int coupledStreams;
|
||||
const std::uint8_t *mapping;
|
||||
int bitrate;
|
||||
};
|
||||
};
|
||||
|
||||
extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG];
|
||||
extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG];
|
||||
|
||||
struct config_t {
|
||||
struct config_t {
|
||||
enum flags_e : int {
|
||||
HIGH_QUALITY,
|
||||
HOST_AUDIO,
|
||||
|
|
@ -37,11 +37,12 @@ struct config_t {
|
|||
int mask;
|
||||
|
||||
std::bitset<MAX_FLAGS> flags;
|
||||
};
|
||||
};
|
||||
|
||||
using buffer_t = util::buffer_t<std::uint8_t>;
|
||||
using packet_t = std::pair<void *, buffer_t>;
|
||||
void capture(safe::mail_t mail, config_t config, void *channel_data);
|
||||
using buffer_t = util::buffer_t<std::uint8_t>;
|
||||
using packet_t = std::pair<void *, buffer_t>;
|
||||
void
|
||||
capture(safe::mail_t mail, config_t config, void *channel_data);
|
||||
} // namespace audio
|
||||
|
||||
#endif
|
||||
|
|
|
|||
112
src/cbs.cpp
112
src/cbs.cpp
|
|
@ -12,27 +12,29 @@ extern "C" {
|
|||
|
||||
using namespace std::literals;
|
||||
namespace cbs {
|
||||
void close(CodedBitstreamContext *c) {
|
||||
void
|
||||
close(CodedBitstreamContext *c) {
|
||||
ff_cbs_close(&c);
|
||||
}
|
||||
}
|
||||
|
||||
using ctx_t = util::safe_ptr<CodedBitstreamContext, close>;
|
||||
using ctx_t = util::safe_ptr<CodedBitstreamContext, close>;
|
||||
|
||||
class frag_t : public CodedBitstreamFragment {
|
||||
public:
|
||||
class frag_t: public CodedBitstreamFragment {
|
||||
public:
|
||||
frag_t(frag_t &&o) {
|
||||
std::copy((std::uint8_t *)&o, (std::uint8_t *)(&o + 1), (std::uint8_t *)this);
|
||||
std::copy((std::uint8_t *) &o, (std::uint8_t *) (&o + 1), (std::uint8_t *) this);
|
||||
|
||||
o.data = nullptr;
|
||||
o.units = nullptr;
|
||||
};
|
||||
|
||||
frag_t() {
|
||||
std::fill_n((std::uint8_t *)this, sizeof(*this), 0);
|
||||
std::fill_n((std::uint8_t *) this, sizeof(*this), 0);
|
||||
}
|
||||
|
||||
frag_t &operator=(frag_t &&o) {
|
||||
std::copy((std::uint8_t *)&o, (std::uint8_t *)(&o + 1), (std::uint8_t *)this);
|
||||
frag_t &
|
||||
operator=(frag_t &&o) {
|
||||
std::copy((std::uint8_t *) &o, (std::uint8_t *) (&o + 1), (std::uint8_t *) this);
|
||||
|
||||
o.data = nullptr;
|
||||
o.units = nullptr;
|
||||
|
|
@ -40,18 +42,18 @@ public:
|
|||
return *this;
|
||||
};
|
||||
|
||||
|
||||
~frag_t() {
|
||||
if(data || units) {
|
||||
if (data || units) {
|
||||
ff_cbs_fragment_free(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
util::buffer_t<std::uint8_t> write(const cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) {
|
||||
util::buffer_t<std::uint8_t>
|
||||
write(const 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) {
|
||||
if (err < 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);
|
||||
|
||||
|
|
@ -59,7 +61,7 @@ util::buffer_t<std::uint8_t> write(const cbs::ctx_t &cbs_ctx, std::uint8_t nal,
|
|||
}
|
||||
|
||||
err = ff_cbs_write_fragment_data(cbs_ctx.get(), &frag);
|
||||
if(err < 0) {
|
||||
if (err < 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);
|
||||
|
||||
|
|
@ -71,16 +73,18 @@ util::buffer_t<std::uint8_t> write(const cbs::ctx_t &cbs_ctx, std::uint8_t nal,
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
util::buffer_t<std::uint8_t> make_sps_h264(const AVCodecContext *ctx) {
|
||||
util::buffer_t<std::uint8_t>
|
||||
make_sps_h264(const AVCodecContext *ctx) {
|
||||
H264RawSPS sps {};
|
||||
|
||||
/* b_per_p == ctx->max_b_frames for h264 */
|
||||
|
|
@ -91,7 +95,6 @@ util::buffer_t<std::uint8_t> make_sps_h264(const AVCodecContext *ctx) {
|
|||
auto mb_width = (FFALIGN(ctx->width, 16) / 16) * 16;
|
||||
auto mb_height = (FFALIGN(ctx->height, 16) / 16) * 16;
|
||||
|
||||
|
||||
sps.nal_unit_header.nal_ref_idc = 3;
|
||||
sps.nal_unit_header.nal_unit_type = H264_NAL_SPS;
|
||||
|
||||
|
|
@ -99,7 +102,7 @@ util::buffer_t<std::uint8_t> make_sps_h264(const AVCodecContext *ctx) {
|
|||
|
||||
sps.constraint_set1_flag = 1;
|
||||
|
||||
if(ctx->level != FF_LEVEL_UNKNOWN) {
|
||||
if (ctx->level != FF_LEVEL_UNKNOWN) {
|
||||
sps.level_idc = ctx->level;
|
||||
}
|
||||
else {
|
||||
|
|
@ -113,7 +116,7 @@ util::buffer_t<std::uint8_t> make_sps_h264(const AVCodecContext *ctx) {
|
|||
mb_height,
|
||||
dpb_frame);
|
||||
|
||||
if(!level) {
|
||||
if (!level) {
|
||||
BOOST_LOG(error) << "Could not guess h264 level"sv;
|
||||
|
||||
return {};
|
||||
|
|
@ -136,7 +139,7 @@ util::buffer_t<std::uint8_t> make_sps_h264(const AVCodecContext *ctx) {
|
|||
sps.frame_mbs_only_flag = 1;
|
||||
sps.direct_8x8_inference_flag = 1;
|
||||
|
||||
if(ctx->width != mb_width || ctx->height != mb_height) {
|
||||
if (ctx->width != mb_width || ctx->height != mb_height) {
|
||||
sps.frame_cropping_flag = 1;
|
||||
sps.frame_crop_left_offset = 0;
|
||||
sps.frame_crop_top_offset = 0;
|
||||
|
|
@ -165,28 +168,28 @@ util::buffer_t<std::uint8_t> make_sps_h264(const AVCodecContext *ctx) {
|
|||
vui.max_num_reorder_frames = max_b_depth;
|
||||
vui.max_dec_frame_buffering = max_b_depth + 1;
|
||||
|
||||
return write(sps.nal_unit_header.nal_unit_type, (void *)&sps.nal_unit_header, AV_CODEC_ID_H264);
|
||||
}
|
||||
return write(sps.nal_unit_header.nal_unit_type, (void *) &sps.nal_unit_header, AV_CODEC_ID_H264);
|
||||
}
|
||||
|
||||
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)) {
|
||||
if (ff_cbs_init(&ctx, AV_CODEC_ID_H265, nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
cbs::frag_t frag;
|
||||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if(err < 0) {
|
||||
if (err < 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 {};
|
||||
}
|
||||
|
||||
|
||||
auto vps_p = ((CodedBitstreamH265Context *)ctx->priv_data)->active_vps;
|
||||
auto sps_p = ((CodedBitstreamH265Context *)ctx->priv_data)->active_sps;
|
||||
auto vps_p = ((CodedBitstreamH265Context *) ctx->priv_data)->active_vps;
|
||||
auto sps_p = ((CodedBitstreamH265Context *) ctx->priv_data)->active_sps;
|
||||
|
||||
H265RawSPS sps { *sps_p };
|
||||
H265RawVPS vps { *vps_p };
|
||||
|
|
@ -209,7 +212,6 @@ hevc_t make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) {
|
|||
vui.transfer_characteristics = avctx->color_trc;
|
||||
vui.matrix_coefficients = avctx->colorspace;
|
||||
|
||||
|
||||
vui.vui_timing_info_present_flag = vps.vps_timing_info_present_flag;
|
||||
vui.vui_num_units_in_tick = vps.vps_num_units_in_tick;
|
||||
vui.vui_time_scale = vps.vps_time_scale;
|
||||
|
|
@ -228,73 +230,75 @@ hevc_t make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) {
|
|||
cbs::ctx_t write_ctx;
|
||||
ff_cbs_init(&write_ctx, AV_CODEC_ID_H265, nullptr);
|
||||
|
||||
|
||||
return hevc_t {
|
||||
nal_t {
|
||||
write(write_ctx, vps.nal_unit_header.nal_unit_type, (void *)&vps.nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(ctx, vps_p->nal_unit_header.nal_unit_type, (void *)&vps_p->nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(write_ctx, vps.nal_unit_header.nal_unit_type, (void *) &vps.nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(ctx, vps_p->nal_unit_header.nal_unit_type, (void *) &vps_p->nal_unit_header, AV_CODEC_ID_H265),
|
||||
},
|
||||
|
||||
nal_t {
|
||||
write(write_ctx, sps.nal_unit_header.nal_unit_type, (void *)&sps.nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(ctx, sps_p->nal_unit_header.nal_unit_type, (void *)&sps_p->nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(write_ctx, sps.nal_unit_header.nal_unit_type, (void *) &sps.nal_unit_header, AV_CODEC_ID_H265),
|
||||
write(ctx, sps_p->nal_unit_header.nal_unit_type, (void *) &sps_p->nal_unit_header, AV_CODEC_ID_H265),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
util::buffer_t<std::uint8_t> read_sps_h264(const AVPacket *packet) {
|
||||
util::buffer_t<std::uint8_t>
|
||||
read_sps_h264(const AVPacket *packet) {
|
||||
cbs::ctx_t ctx;
|
||||
if(ff_cbs_init(&ctx, AV_CODEC_ID_H264, nullptr)) {
|
||||
if (ff_cbs_init(&ctx, AV_CODEC_ID_H264, nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
cbs::frag_t frag;
|
||||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, &*packet);
|
||||
if(err < 0) {
|
||||
if (err < 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 {};
|
||||
}
|
||||
|
||||
auto h264 = (H264RawNALUnitHeader *)((CodedBitstreamH264Context *)ctx->priv_data)->active_sps;
|
||||
return write(h264->nal_unit_type, (void *)h264, AV_CODEC_ID_H264);
|
||||
}
|
||||
auto h264 = (H264RawNALUnitHeader *) ((CodedBitstreamH264Context *) ctx->priv_data)->active_sps;
|
||||
return write(h264->nal_unit_type, (void *) h264, AV_CODEC_ID_H264);
|
||||
}
|
||||
|
||||
h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet) {
|
||||
h264_t
|
||||
make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet) {
|
||||
return h264_t {
|
||||
make_sps_h264(ctx),
|
||||
read_sps_h264(packet),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
if (ff_cbs_init(&ctx, (AVCodecID) codec_id, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cbs::frag_t frag;
|
||||
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if(err < 0) {
|
||||
if (err < 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;
|
||||
}
|
||||
|
||||
if(codec_id == AV_CODEC_ID_H264) {
|
||||
auto h264 = (CodedBitstreamH264Context *)ctx->priv_data;
|
||||
if (codec_id == AV_CODEC_ID_H264) {
|
||||
auto h264 = (CodedBitstreamH264Context *) ctx->priv_data;
|
||||
|
||||
if(!h264->active_sps->vui_parameters_present_flag) {
|
||||
if (!h264->active_sps->vui_parameters_present_flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return ((CodedBitstreamH265Context *)ctx->priv_data)->active_sps->vui_parameters_present_flag;
|
||||
}
|
||||
return ((CodedBitstreamH265Context *) ctx->priv_data)->active_sps->vui_parameters_present_flag;
|
||||
}
|
||||
} // namespace cbs
|
||||
|
|
|
|||
23
src/cbs.h
23
src/cbs.h
|
|
@ -8,27 +8,30 @@ struct AVCodecContext;
|
|||
|
||||
namespace cbs {
|
||||
|
||||
struct nal_t {
|
||||
struct nal_t {
|
||||
util::buffer_t<std::uint8_t> _new;
|
||||
util::buffer_t<std::uint8_t> old;
|
||||
};
|
||||
};
|
||||
|
||||
struct hevc_t {
|
||||
struct hevc_t {
|
||||
nal_t vps;
|
||||
nal_t sps;
|
||||
};
|
||||
};
|
||||
|
||||
struct h264_t {
|
||||
struct h264_t {
|
||||
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);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Check if SPS->VUI is present
|
||||
*/
|
||||
bool validate_sps(const AVPacket *packet, int codec_id);
|
||||
bool
|
||||
validate_sps(const AVPacket *packet, int codec_id);
|
||||
} // namespace cbs
|
||||
|
||||
#endif
|
||||
|
|
|
|||
610
src/config.cpp
610
src/config.cpp
File diff suppressed because it is too large
Load diff
60
src/config.h
60
src/config.h
|
|
@ -9,7 +9,7 @@
|
|||
#include <vector>
|
||||
|
||||
namespace config {
|
||||
struct video_t {
|
||||
struct video_t {
|
||||
// ffmpeg params
|
||||
int qp; // higher == more compression and less quality
|
||||
|
||||
|
|
@ -57,14 +57,14 @@ struct video_t {
|
|||
std::string adapter_name;
|
||||
std::string output_name;
|
||||
bool dwmflush;
|
||||
};
|
||||
};
|
||||
|
||||
struct audio_t {
|
||||
struct audio_t {
|
||||
std::string sink;
|
||||
std::string virtual_sink;
|
||||
};
|
||||
};
|
||||
|
||||
struct stream_t {
|
||||
struct stream_t {
|
||||
std::chrono::milliseconds ping_timeout;
|
||||
|
||||
std::string file_apps;
|
||||
|
|
@ -73,9 +73,9 @@ struct stream_t {
|
|||
|
||||
// max unique instances of video and audio streams
|
||||
int channels;
|
||||
};
|
||||
};
|
||||
|
||||
struct nvhttp_t {
|
||||
struct nvhttp_t {
|
||||
// Could be any of the following values:
|
||||
// pc|lan|wan
|
||||
std::string origin_pin_allowed;
|
||||
|
|
@ -91,9 +91,9 @@ struct nvhttp_t {
|
|||
std::string external_ip;
|
||||
std::vector<std::string> resolutions;
|
||||
std::vector<int> fps;
|
||||
};
|
||||
};
|
||||
|
||||
struct input_t {
|
||||
struct input_t {
|
||||
std::unordered_map<int, int> keybindings;
|
||||
|
||||
std::chrono::milliseconds back_button_timeout;
|
||||
|
|
@ -105,27 +105,29 @@ struct input_t {
|
|||
bool keyboard;
|
||||
bool mouse;
|
||||
bool controller;
|
||||
};
|
||||
};
|
||||
|
||||
namespace flag {
|
||||
enum flag_e : std::size_t {
|
||||
namespace flag {
|
||||
enum flag_e : std::size_t {
|
||||
PIN_STDIN = 0, // Read PIN from stdin instead of http
|
||||
FRESH_STATE, // Do not load or save state
|
||||
FORCE_VIDEO_HEADER_REPLACE, // force replacing headers inside video data
|
||||
UPNP, // Try Universal Plug 'n Play
|
||||
CONST_PIN, // Use "universal" pin
|
||||
FLAG_SIZE
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct prep_cmd_t {
|
||||
prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd) : do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)) {}
|
||||
explicit prep_cmd_t(std::string &&do_cmd) : do_cmd(std::move(do_cmd)) {}
|
||||
struct prep_cmd_t {
|
||||
prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd):
|
||||
do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)) {}
|
||||
explicit prep_cmd_t(std::string &&do_cmd):
|
||||
do_cmd(std::move(do_cmd)) {}
|
||||
std::string do_cmd;
|
||||
std::string undo_cmd;
|
||||
};
|
||||
};
|
||||
|
||||
struct sunshine_t {
|
||||
struct sunshine_t {
|
||||
int min_log_level;
|
||||
std::bitset<flag::FLAG_SIZE> flags;
|
||||
std::string credentials_file;
|
||||
|
|
@ -146,16 +148,18 @@ struct sunshine_t {
|
|||
std::string log_file;
|
||||
|
||||
std::vector<prep_cmd_t> prep_cmds;
|
||||
};
|
||||
};
|
||||
|
||||
extern video_t video;
|
||||
extern audio_t audio;
|
||||
extern stream_t stream;
|
||||
extern nvhttp_t nvhttp;
|
||||
extern input_t input;
|
||||
extern sunshine_t sunshine;
|
||||
extern video_t video;
|
||||
extern audio_t audio;
|
||||
extern stream_t stream;
|
||||
extern nvhttp_t nvhttp;
|
||||
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
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -38,67 +38,71 @@
|
|||
using namespace std::literals;
|
||||
|
||||
namespace confighttp {
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
using https_server_t = SimpleWeb::Server<SimpleWeb::HTTPS>;
|
||||
using https_server_t = SimpleWeb::Server<SimpleWeb::HTTPS>;
|
||||
|
||||
using args_t = SimpleWeb::CaseInsensitiveMultimap;
|
||||
using resp_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>;
|
||||
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
|
||||
using args_t = SimpleWeb::CaseInsensitiveMultimap;
|
||||
using resp_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>;
|
||||
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
|
||||
|
||||
enum class op_e {
|
||||
enum class op_e {
|
||||
ADD,
|
||||
REMOVE
|
||||
};
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
for(auto &[name, val] : request->header) {
|
||||
for (auto &[name, val] : request->header) {
|
||||
BOOST_LOG(debug) << name << " -- " << (name == "Authorization" ? "CREDENTIALS REDACTED" : val);
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << " [--] "sv;
|
||||
|
||||
for(auto &[name, val] : request->parse_query_string()) {
|
||||
for (auto &[name, val] : request->parse_query_string()) {
|
||||
BOOST_LOG(debug) << name << " -- " << val;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << " [--] "sv;
|
||||
}
|
||||
}
|
||||
|
||||
void send_unauthorized(resp_https_t response, req_https_t request) {
|
||||
void
|
||||
send_unauthorized(resp_https_t response, req_https_t request) {
|
||||
auto address = request->remote_endpoint().address().to_string();
|
||||
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")" }
|
||||
};
|
||||
response->write(SimpleWeb::StatusCode::client_error_unauthorized, headers);
|
||||
}
|
||||
}
|
||||
|
||||
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 = request->remote_endpoint().address().to_string();
|
||||
BOOST_LOG(info) << "Web UI: ["sv << address << "] -- not authorized"sv;
|
||||
const SimpleWeb::CaseInsensitiveMultimap headers {
|
||||
{ "Location", path }
|
||||
};
|
||||
response->write(SimpleWeb::StatusCode::redirection_temporary_redirect, headers);
|
||||
}
|
||||
}
|
||||
|
||||
bool authenticate(resp_https_t response, req_https_t request) {
|
||||
bool
|
||||
authenticate(resp_https_t response, req_https_t request) {
|
||||
auto address = request->remote_endpoint().address().to_string();
|
||||
auto ip_type = net::from_address(address);
|
||||
|
||||
if(ip_type > http::origin_web_ui_allowed) {
|
||||
if (ip_type > http::origin_web_ui_allowed) {
|
||||
BOOST_LOG(info) << "Web UI: ["sv << address << "] -- denied"sv;
|
||||
response->write(SimpleWeb::StatusCode::client_error_forbidden);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If credentials are shown, redirect the user to a /welcome page
|
||||
if(config::sunshine.username.empty()) {
|
||||
if (config::sunshine.username.empty()) {
|
||||
send_redirect(response, request, "/welcome");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -108,7 +112,7 @@ bool authenticate(resp_https_t response, req_https_t request) {
|
|||
});
|
||||
|
||||
auto auth = request->header.find("authorization");
|
||||
if(auth == request->header.end()) {
|
||||
if (auth == request->header.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +120,7 @@ bool authenticate(resp_https_t response, req_https_t request) {
|
|||
auto authData = SimpleWeb::Crypto::Base64::decode(rawAuth.substr("Basic "sv.length()));
|
||||
|
||||
int index = authData.find(':');
|
||||
if(index >= authData.size() - 1) {
|
||||
if (index >= authData.size() - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -124,15 +128,16 @@ bool authenticate(resp_https_t response, req_https_t request) {
|
|||
auto password = authData.substr(index + 1);
|
||||
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
|
||||
|
||||
if(username != config::sunshine.username || hash != config::sunshine.password) {
|
||||
if (username != config::sunshine.username || hash != config::sunshine.password) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fg.disable();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void not_found(resp_https_t response, req_https_t request) {
|
||||
void
|
||||
not_found(resp_https_t response, req_https_t request) {
|
||||
pt::ptree tree;
|
||||
tree.put("root.<xmlattr>.status_code", 404);
|
||||
|
||||
|
|
@ -143,31 +148,34 @@ void not_found(resp_https_t response, req_https_t request) {
|
|||
|
||||
*response << "HTTP/1.1 404 NOT FOUND\r\n"
|
||||
<< data.str();
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
|
||||
print_req(request);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "index.html");
|
||||
response->write(header + content);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "pin.html");
|
||||
response->write(header + content);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -177,60 +185,66 @@ void getAppsPage(resp_https_t response, req_https_t request) {
|
|||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "apps.html");
|
||||
response->write(header + content, headers);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "clients.html");
|
||||
response->write(header + content);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "config.html");
|
||||
response->write(header + content);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "password.html");
|
||||
response->write(header + content);
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
if (!config::sunshine.username.empty()) {
|
||||
send_redirect(response, request, "/");
|
||||
return;
|
||||
}
|
||||
std::string header = read_file(WEB_DIR "header-no-nav.html");
|
||||
std::string content = read_file(WEB_DIR "welcome.html");
|
||||
response->write(header + content);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::string header = read_file(WEB_DIR "header.html");
|
||||
std::string content = read_file(WEB_DIR "troubleshooting.html");
|
||||
response->write(header + content);
|
||||
}
|
||||
}
|
||||
|
||||
void getFaviconImage(resp_https_t response, req_https_t request) {
|
||||
void
|
||||
getFaviconImage(resp_https_t response, req_https_t request) {
|
||||
// todo - combine function with getSunshineLogoImage and possibly getNodeModules
|
||||
// todo - use mime_types map
|
||||
print_req(request);
|
||||
|
|
@ -239,9 +253,10 @@ void getFaviconImage(resp_https_t response, req_https_t request) {
|
|||
SimpleWeb::CaseInsensitiveMultimap headers;
|
||||
headers.emplace("Content-Type", "image/x-icon");
|
||||
response->write(SimpleWeb::StatusCode::success_ok, in, headers);
|
||||
}
|
||||
}
|
||||
|
||||
void getSunshineLogoImage(resp_https_t response, req_https_t request) {
|
||||
void
|
||||
getSunshineLogoImage(resp_https_t response, req_https_t request) {
|
||||
// todo - combine function with getFaviconImage and possibly getNodeModules
|
||||
// todo - use mime_types map
|
||||
print_req(request);
|
||||
|
|
@ -250,14 +265,16 @@ void getSunshineLogoImage(resp_https_t response, req_https_t request) {
|
|||
SimpleWeb::CaseInsensitiveMultimap headers;
|
||||
headers.emplace("Content-Type", "image/png");
|
||||
response->write(SimpleWeb::StatusCode::success_ok, in, headers);
|
||||
}
|
||||
}
|
||||
|
||||
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("..");
|
||||
}
|
||||
}
|
||||
|
||||
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 / "node_modules");
|
||||
|
|
@ -266,11 +283,11 @@ void getNodeModules(resp_https_t response, req_https_t request) {
|
|||
auto filePath = fs::weakly_canonical(webDirPath / fs::path(request->path).relative_path());
|
||||
|
||||
// Don't do anything if file does not exist or is outside the node_modules directory
|
||||
if(!isChildPath(filePath, nodeModulesPath)) {
|
||||
if (!isChildPath(filePath, nodeModulesPath)) {
|
||||
BOOST_LOG(warning) << "Someone requested a path " << filePath << " that is outside the node_modules folder";
|
||||
response->write(SimpleWeb::StatusCode::client_error_bad_request, "Bad Request");
|
||||
}
|
||||
else if(!fs::exists(filePath)) {
|
||||
else if (!fs::exists(filePath)) {
|
||||
response->write(SimpleWeb::StatusCode::client_error_not_found);
|
||||
}
|
||||
else {
|
||||
|
|
@ -279,7 +296,7 @@ void getNodeModules(resp_https_t response, req_https_t request) {
|
|||
// remove the leading period from the extension
|
||||
auto mimeType = mime_types.find(relPath.extension().string().substr(1));
|
||||
// check if the extension is in the map at the x position
|
||||
if(mimeType != mime_types.end()) {
|
||||
if (mimeType != mime_types.end()) {
|
||||
// if it is, set the content type to the mime type
|
||||
SimpleWeb::CaseInsensitiveMultimap headers;
|
||||
headers.emplace("Content-Type", mimeType->second);
|
||||
|
|
@ -288,19 +305,21 @@ void getNodeModules(resp_https_t response, req_https_t request) {
|
|||
}
|
||||
// do not return any file if the type is not in the map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
std::string content = read_file(config::stream.file_apps.c_str());
|
||||
response->write(content);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -308,10 +327,11 @@ void getLogs(resp_https_t response, req_https_t request) {
|
|||
SimpleWeb::CaseInsensitiveMultimap headers;
|
||||
headers.emplace("Content-Type", "text/plain");
|
||||
response->write(SimpleWeb::StatusCode::success_ok, content, headers);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -334,11 +354,11 @@ void saveApp(resp_https_t response, req_https_t request) {
|
|||
pt::read_json(ss, inputTree);
|
||||
pt::read_json(config::stream.file_apps, fileTree);
|
||||
|
||||
if(inputTree.get_child("prep-cmd").empty()) {
|
||||
if (inputTree.get_child("prep-cmd").empty()) {
|
||||
inputTree.erase("prep-cmd");
|
||||
}
|
||||
|
||||
if(inputTree.get_child("detached").empty()) {
|
||||
if (inputTree.get_child("detached").empty()) {
|
||||
inputTree.erase("detached");
|
||||
}
|
||||
|
||||
|
|
@ -347,15 +367,15 @@ void saveApp(resp_https_t response, req_https_t request) {
|
|||
|
||||
inputTree.erase("index");
|
||||
|
||||
if(index == -1) {
|
||||
if (index == -1) {
|
||||
apps_node.push_back(std::make_pair("", inputTree));
|
||||
}
|
||||
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 &kv : apps_node) {
|
||||
if(i == index) {
|
||||
for (const auto &kv : apps_node) {
|
||||
if (i == index) {
|
||||
newApps.push_back(std::make_pair("", inputTree));
|
||||
}
|
||||
else {
|
||||
|
|
@ -368,7 +388,7 @@ void saveApp(resp_https_t response, req_https_t request) {
|
|||
}
|
||||
pt::write_json(config::stream.file_apps, fileTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SaveApp: "sv << e.what();
|
||||
|
||||
outputTree.put("status", "false");
|
||||
|
|
@ -378,10 +398,11 @@ void saveApp(resp_https_t response, req_https_t request) {
|
|||
|
||||
outputTree.put("status", "true");
|
||||
proc::refresh(config::stream.file_apps);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -398,7 +419,7 @@ void deleteApp(resp_https_t response, req_https_t request) {
|
|||
auto &apps_node = fileTree.get_child("apps"s);
|
||||
int index = stoi(request->path_match[1]);
|
||||
|
||||
if(index < 0) {
|
||||
if (index < 0) {
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", "Invalid Index");
|
||||
return;
|
||||
|
|
@ -407,8 +428,8 @@ void deleteApp(resp_https_t response, req_https_t request) {
|
|||
// 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 &kv : apps_node) {
|
||||
if(i++ != index) {
|
||||
for (const auto &kv : apps_node) {
|
||||
if (i++ != index) {
|
||||
newApps.push_back(std::make_pair("", kv.second));
|
||||
}
|
||||
}
|
||||
|
|
@ -417,7 +438,7 @@ void deleteApp(resp_https_t response, req_https_t request) {
|
|||
}
|
||||
pt::write_json(config::stream.file_apps, fileTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "DeleteApp: "sv << e.what();
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", "Invalid File JSON");
|
||||
|
|
@ -426,10 +447,11 @@ void deleteApp(resp_https_t response, req_https_t request) {
|
|||
|
||||
outputTree.put("status", "true");
|
||||
proc::refresh(config::stream.file_apps);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -439,7 +461,7 @@ void uploadCover(resp_https_t response, req_https_t request) {
|
|||
std::ostringstream data;
|
||||
|
||||
SimpleWeb::StatusCode code = SimpleWeb::StatusCode::success_ok;
|
||||
if(outputTree.get_child_optional("error").has_value()) {
|
||||
if (outputTree.get_child_optional("error").has_value()) {
|
||||
code = SimpleWeb::StatusCode::client_error_bad_request;
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +472,7 @@ void uploadCover(resp_https_t response, req_https_t request) {
|
|||
try {
|
||||
pt::read_json(ss, inputTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "UploadCover: "sv << e.what();
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", e.what());
|
||||
|
|
@ -458,24 +480,24 @@ void uploadCover(resp_https_t response, req_https_t request) {
|
|||
}
|
||||
|
||||
auto key = inputTree.get("key", "");
|
||||
if(key.empty()) {
|
||||
if (key.empty()) {
|
||||
outputTree.put("error", "Cover key is required");
|
||||
return;
|
||||
}
|
||||
auto url = inputTree.get("url", "");
|
||||
|
||||
const std::string coverdir = platf::appdata().string() + "/covers/";
|
||||
if(!boost::filesystem::exists(coverdir)) {
|
||||
if (!boost::filesystem::exists(coverdir)) {
|
||||
boost::filesystem::create_directory(coverdir);
|
||||
}
|
||||
|
||||
std::basic_string path = coverdir + http::url_escape(key) + ".png";
|
||||
if(!url.empty()) {
|
||||
if(http::url_get_host(url) != "images.igdb.com") {
|
||||
if (!url.empty()) {
|
||||
if (http::url_get_host(url) != "images.igdb.com") {
|
||||
outputTree.put("error", "Only images.igdb.com is allowed");
|
||||
return;
|
||||
}
|
||||
if(!http::download_file(url, path)) {
|
||||
if (!http::download_file(url, path)) {
|
||||
outputTree.put("error", "Failed to download cover");
|
||||
return;
|
||||
}
|
||||
|
|
@ -484,13 +506,14 @@ void uploadCover(resp_https_t response, req_https_t request) {
|
|||
auto data = SimpleWeb::Crypto::Base64::decode(inputTree.get<std::string>("data"));
|
||||
|
||||
std::ofstream imgfile(path);
|
||||
imgfile.write(data.data(), (int)data.size());
|
||||
imgfile.write(data.data(), (int) data.size());
|
||||
}
|
||||
outputTree.put("path", path);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -509,13 +532,14 @@ void getConfig(resp_https_t response, req_https_t request) {
|
|||
|
||||
auto vars = config::parse_config(read_file(config::sunshine.config_file.c_str()));
|
||||
|
||||
for(auto &[name, value] : vars) {
|
||||
for (auto &[name, value] : vars) {
|
||||
outputTree.put(std::move(name), std::move(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);
|
||||
|
||||
|
|
@ -533,24 +557,25 @@ void saveConfig(resp_https_t response, req_https_t request) {
|
|||
try {
|
||||
// TODO: Input Validation
|
||||
pt::read_json(ss, inputTree);
|
||||
for(const auto &kv : inputTree) {
|
||||
for (const auto &kv : inputTree) {
|
||||
std::string value = inputTree.get<std::string>(kv.first);
|
||||
if(value.length() == 0 || value.compare("null") == 0) continue;
|
||||
if (value.length() == 0 || value.compare("null") == 0) continue;
|
||||
|
||||
configStream << kv.first << " = " << value << std::endl;
|
||||
}
|
||||
write_file(config::sunshine.config_file.c_str(), configStream.str());
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SaveConfig: "sv << e.what();
|
||||
outputTree.put("status", "false");
|
||||
outputTree.put("error", e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -565,23 +590,24 @@ void restart(resp_https_t response, req_https_t request) {
|
|||
response->write(data.str());
|
||||
});
|
||||
|
||||
if(!platf::restart_supported()) {
|
||||
if (!platf::restart_supported()) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Restart is not currently supported on this platform");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!platf::restart()) {
|
||||
if (!platf::restart()) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Restart failed");
|
||||
return;
|
||||
}
|
||||
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -605,15 +631,15 @@ void savePassword(resp_https_t response, req_https_t request) {
|
|||
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) {
|
||||
if (newUsername.length() == 0) newUsername = username;
|
||||
if (newUsername.length() == 0) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Invalid Username");
|
||||
}
|
||||
else {
|
||||
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
|
||||
if(config::sunshine.username.empty() || (username == config::sunshine.username && hash == config::sunshine.password)) {
|
||||
if(newPassword.empty() || newPassword != confirmPassword) {
|
||||
if (config::sunshine.username.empty() || (username == config::sunshine.username && hash == config::sunshine.password)) {
|
||||
if (newPassword.empty() || newPassword != confirmPassword) {
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", "Password Mismatch");
|
||||
}
|
||||
|
|
@ -629,16 +655,17 @@ void savePassword(resp_https_t response, req_https_t request) {
|
|||
}
|
||||
}
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SavePassword: "sv << e.what();
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -659,16 +686,17 @@ void savePin(resp_https_t response, req_https_t request) {
|
|||
std::string pin = inputTree.get<std::string>("pin");
|
||||
outputTree.put("status", nvhttp::pin(pin));
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(warning) << "SavePin: "sv << e.what();
|
||||
outputTree.put("status", false);
|
||||
outputTree.put("error", e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -681,10 +709,11 @@ void unpairAll(resp_https_t response, req_https_t request) {
|
|||
});
|
||||
nvhttp::erase_all_clients();
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -698,9 +727,10 @@ void closeApp(resp_https_t response, req_https_t request) {
|
|||
|
||||
proc::proc.terminate();
|
||||
outputTree.put("status", true);
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
void
|
||||
start() {
|
||||
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
|
||||
|
||||
auto port_https = map_port(PORT_HTTPS);
|
||||
|
|
@ -740,9 +770,9 @@ void start() {
|
|||
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()) {
|
||||
if (shutdown_event->peek()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -759,5 +789,5 @@ void start() {
|
|||
server.stop();
|
||||
|
||||
tcp.join();
|
||||
}
|
||||
}
|
||||
} // namespace confighttp
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@
|
|||
|
||||
#define WEB_DIR SUNSHINE_ASSETS_DIR "/web/"
|
||||
|
||||
|
||||
namespace confighttp {
|
||||
constexpr auto PORT_HTTPS = 1;
|
||||
void start();
|
||||
constexpr auto PORT_HTTPS = 1;
|
||||
void
|
||||
start();
|
||||
} // namespace confighttp
|
||||
|
||||
// mime types map
|
||||
|
|
|
|||
241
src/crypto.cpp
241
src/crypto.cpp
|
|
@ -4,20 +4,23 @@
|
|||
#include <openssl/pem.h>
|
||||
|
||||
namespace crypto {
|
||||
using asn1_string_t = util::safe_ptr<ASN1_STRING, ASN1_STRING_free>;
|
||||
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) {
|
||||
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() };
|
||||
|
||||
X509_STORE_add_cert(x509_store.get(), cert.get());
|
||||
_certs.emplace_back(std::make_pair(std::move(cert), std::move(x509_store)));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
switch (err_code) {
|
||||
// Expired or not-yet-valid certificates are fine. Sometimes Moonlight is running on embedded devices
|
||||
// that don't have accurate clocks (or haven't yet synchronized by the time Moonlight first runs).
|
||||
// This behavior also matches what GeForce Experience does.
|
||||
|
|
@ -30,18 +33,19 @@ static int openssl_verify_cb(int ok, X509_STORE_CTX *ctx) {
|
|||
default:
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* When certificates from two or more instances of Moonlight have been added to x509_store_t,
|
||||
* only one of them will be verified by X509_verify_cert, resulting in only a single instance of
|
||||
* Moonlight to be able to use Sunshine
|
||||
*
|
||||
* To circumvent this, x509_store_t instance will be created for each instance of the certificates.
|
||||
*/
|
||||
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) {
|
||||
for (auto &[_, x509_store] : _certs) {
|
||||
auto fg = util::fail_guard([this]() {
|
||||
X509_STORE_CTX_cleanup(_cert_ctx.get());
|
||||
});
|
||||
|
|
@ -56,86 +60,90 @@ const char *cert_chain_t::verify(x509_t::element_type *cert) {
|
|||
|
||||
auto err = X509_verify_cert(_cert_ctx.get());
|
||||
|
||||
if(err == 1) {
|
||||
if (err == 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
err_code = X509_STORE_CTX_get_error(_cert_ctx.get());
|
||||
|
||||
if(err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) {
|
||||
if (err_code != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && err_code != X509_V_ERR_INVALID_CA) {
|
||||
return X509_verify_cert_error_string(err_code);
|
||||
}
|
||||
}
|
||||
|
||||
return X509_verify_cert_error_string(err_code);
|
||||
}
|
||||
}
|
||||
|
||||
namespace cipher {
|
||||
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) {
|
||||
if (!ctx) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv->size(), nullptr) != 1) {
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv->size(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key->data(), iv->data()) != 1) {
|
||||
if (EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
|
||||
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
|
||||
if(EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_gcm(), nullptr, nullptr, nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv->size(), nullptr) != 1) {
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, iv->size(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_EncryptInit_ex(ctx.get(), nullptr, nullptr, key->data(), iv->data()) != 1) {
|
||||
if (EVP_EncryptInit_ex(ctx.get(), nullptr, nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
|
||||
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
|
||||
if(EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, key->data(), iv->data()) != 1) {
|
||||
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, key->data(), iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(ctx.get(), padding);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if(EVP_DecryptInit_ex(decrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
if (EVP_DecryptInit_ex(decrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -145,31 +153,32 @@ int gcm_t::decrypt(const std::string_view &tagged_cipher, std::vector<std::uint8
|
|||
plaintext.resize((cipher.size() + 15) / 16 * 16);
|
||||
|
||||
int size;
|
||||
if(EVP_DecryptUpdate(decrypt_ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) {
|
||||
if (EVP_DecryptUpdate(decrypt_ctx.get(), plaintext.data(), &size, (const std::uint8_t *) cipher.data(), cipher.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(decrypt_ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char *>(tag.data())) != 1) {
|
||||
if (EVP_CIPHER_CTX_ctrl(decrypt_ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), const_cast<char *>(tag.data())) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int len = size;
|
||||
if(EVP_DecryptFinal_ex(decrypt_ctx.get(), plaintext.data() + size, &len) != 1) {
|
||||
if (EVP_DecryptFinal_ex(decrypt_ctx.get(), plaintext.data() + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
plaintext.resize(size + len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv) {
|
||||
if(!encrypt_ctx && init_encrypt_gcm(encrypt_ctx, &key, iv, padding)) {
|
||||
int
|
||||
gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_cipher, aes_t *iv) {
|
||||
if (!encrypt_ctx && init_encrypt_gcm(encrypt_ctx, &key, iv, padding)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if(EVP_EncryptInit_ex(encrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
if (EVP_EncryptInit_ex(encrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -180,23 +189,24 @@ int gcm_t::encrypt(const std::string_view &plaintext, std::uint8_t *tagged_ciphe
|
|||
int size = round_to_pkcs7_padded(plaintext.size());
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if(EVP_EncryptUpdate(encrypt_ctx.get(), cipher, &size, (const std::uint8_t *)plaintext.data(), plaintext.size()) != 1) {
|
||||
if (EVP_EncryptUpdate(encrypt_ctx.get(), cipher, &size, (const std::uint8_t *) plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// GCM encryption won't ever fill ciphertext here but we have to call it anyway
|
||||
if(EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher + size, &len) != 1) {
|
||||
if (EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_CIPHER_CTX_ctrl(encrypt_ctx.get(), EVP_CTRL_GCM_GET_TAG, tag_size, tag) != 1) {
|
||||
if (EVP_CIPHER_CTX_ctrl(encrypt_ctx.get(), EVP_CTRL_GCM_GET_TAG, tag_size, tag) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len + size;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
int len;
|
||||
|
||||
auto fg = util::fail_guard([this]() {
|
||||
|
|
@ -204,34 +214,35 @@ int ecb_t::decrypt(const std::string_view &cipher, std::vector<std::uint8_t> &pl
|
|||
});
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if(EVP_DecryptInit_ex(decrypt_ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
if (EVP_DecryptInit_ex(decrypt_ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(decrypt_ctx.get(), padding);
|
||||
|
||||
plaintext.resize((cipher.size() + 15) / 16 * 16);
|
||||
auto size = (int)plaintext.size();
|
||||
auto size = (int) plaintext.size();
|
||||
// Decrypt into the caller's buffer, leaving room for the auth tag to be prepended
|
||||
if(EVP_DecryptUpdate(decrypt_ctx.get(), plaintext.data(), &size, (const std::uint8_t *)cipher.data(), cipher.size()) != 1) {
|
||||
if (EVP_DecryptUpdate(decrypt_ctx.get(), plaintext.data(), &size, (const std::uint8_t *) cipher.data(), cipher.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_DecryptFinal_ex(decrypt_ctx.get(), plaintext.data(), &len) != 1) {
|
||||
if (EVP_DecryptFinal_ex(decrypt_ctx.get(), plaintext.data(), &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
plaintext.resize(len + size);
|
||||
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());
|
||||
});
|
||||
|
||||
// Gen 7 servers use 128-bit AES ECB
|
||||
if(EVP_EncryptInit_ex(encrypt_ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
if (EVP_EncryptInit_ex(encrypt_ctx.get(), EVP_aes_128_ecb(), nullptr, key.data(), nullptr) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -240,29 +251,30 @@ int ecb_t::encrypt(const std::string_view &plaintext, std::vector<std::uint8_t>
|
|||
int len;
|
||||
|
||||
cipher.resize((plaintext.size() + 15) / 16 * 16);
|
||||
auto size = (int)cipher.size();
|
||||
auto size = (int) cipher.size();
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if(EVP_EncryptUpdate(encrypt_ctx.get(), cipher.data(), &size, (const std::uint8_t *)plaintext.data(), plaintext.size()) != 1) {
|
||||
if (EVP_EncryptUpdate(encrypt_ctx.get(), cipher.data(), &size, (const std::uint8_t *) plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher.data() + size, &len) != 1) {
|
||||
if (EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher.data() + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cipher.resize(len + size);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Calling with cipher == nullptr results in a parameter change
|
||||
// without requiring a reallocation of the internal cipher ctx.
|
||||
if(EVP_EncryptInit_ex(encrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
if (EVP_EncryptInit_ex(encrypt_ctx.get(), nullptr, nullptr, nullptr, iv->data()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -271,29 +283,30 @@ int cbc_t::encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_
|
|||
int size = plaintext.size(); // round_to_pkcs7_padded(plaintext.size());
|
||||
|
||||
// Encrypt into the caller's buffer
|
||||
if(EVP_EncryptUpdate(encrypt_ctx.get(), cipher, &size, (const std::uint8_t *)plaintext.data(), plaintext.size()) != 1) {
|
||||
if (EVP_EncryptUpdate(encrypt_ctx.get(), cipher, &size, (const std::uint8_t *) plaintext.data(), plaintext.size()) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher + size, &len) != 1) {
|
||||
if (EVP_EncryptFinal_ex(encrypt_ctx.get(), cipher + size, &len) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size + len;
|
||||
}
|
||||
}
|
||||
|
||||
ecb_t::ecb_t(const aes_t &key, bool padding)
|
||||
: cipher_t { EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_new(), key, padding } {}
|
||||
ecb_t::ecb_t(const aes_t &key, bool 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 } {}
|
||||
cbc_t::cbc_t(const aes_t &key, bool padding):
|
||||
cipher_t { nullptr, nullptr, key, padding } {}
|
||||
|
||||
gcm_t::gcm_t(const crypto::aes_t &key, bool padding)
|
||||
: cipher_t { nullptr, nullptr, key, padding } {}
|
||||
gcm_t::gcm_t(const crypto::aes_t &key, bool padding):
|
||||
cipher_t { nullptr, nullptr, key, padding } {}
|
||||
|
||||
} // namespace cipher
|
||||
} // 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;
|
||||
|
||||
std::string salt_pin;
|
||||
|
|
@ -307,15 +320,17 @@ aes_t gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &p
|
|||
std::copy(std::begin(hsh), std::begin(hsh) + key.size(), std::begin(key));
|
||||
|
||||
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) {
|
||||
x509_t
|
||||
x509(const std::string_view &x) {
|
||||
bio_t io { BIO_new(BIO_s_mem()) };
|
||||
|
||||
BIO_write(io.get(), x.data(), x.size());
|
||||
|
|
@ -324,9 +339,10 @@ x509_t x509(const std::string_view &x) {
|
|||
PEM_read_bio_X509(io.get(), &p, nullptr, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
pkey_t pkey(const std::string_view &k) {
|
||||
pkey_t
|
||||
pkey(const std::string_view &k) {
|
||||
bio_t io { BIO_new(BIO_s_mem()) };
|
||||
|
||||
BIO_write(io.get(), k.data(), k.size());
|
||||
|
|
@ -335,9 +351,10 @@ pkey_t pkey(const std::string_view &k) {
|
|||
PEM_read_bio_PrivateKey(io.get(), &p, nullptr, nullptr);
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
std::string pem(x509_t &x509) {
|
||||
std::string
|
||||
pem(x509_t &x509) {
|
||||
bio_t bio { BIO_new(BIO_s_mem()) };
|
||||
|
||||
PEM_write_bio_X509(bio.get(), x509.get());
|
||||
|
|
@ -345,9 +362,10 @@ std::string pem(x509_t &x509) {
|
|||
BIO_get_mem_ptr(bio.get(), &mem_ptr);
|
||||
|
||||
return { mem_ptr->data, mem_ptr->length };
|
||||
}
|
||||
}
|
||||
|
||||
std::string pem(pkey_t &pkey) {
|
||||
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);
|
||||
|
|
@ -355,34 +373,37 @@ std::string pem(pkey_t &pkey) {
|
|||
BIO_get_mem_ptr(bio.get(), &mem_ptr);
|
||||
|
||||
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);
|
||||
|
||||
RAND_bytes((uint8_t *)r.data(), r.size());
|
||||
RAND_bytes((uint8_t *) r.data(), r.size());
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> sign(const pkey_t &pkey, const std::string_view &data, const EVP_MD *md) {
|
||||
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, pkey.get()) != 1) {
|
||||
if (EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, pkey.get()) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if(EVP_DigestSignUpdate(ctx.get(), data.data(), data.size()) != 1) {
|
||||
if (EVP_DigestSignUpdate(ctx.get(), data.data(), data.size()) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -391,14 +412,15 @@ std::vector<uint8_t> sign(const pkey_t &pkey, const std::string_view &data, cons
|
|||
std::vector<uint8_t> digest;
|
||||
digest.resize(slen);
|
||||
|
||||
if(EVP_DigestSignFinal(ctx.get(), digest.data(), &slen) != 1) {
|
||||
if (EVP_DigestSignFinal(ctx.get(), digest.data(), &slen) != 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return digest;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
x509_t x509 { X509_new() };
|
||||
pkey_ctx_t ctx { EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) };
|
||||
pkey_t pkey;
|
||||
|
|
@ -434,54 +456,59 @@ creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) {
|
|||
|
||||
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(),
|
||||
(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) };
|
||||
}
|
||||
}
|
||||
|
||||
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_get_pubkey(x509.get());
|
||||
|
||||
md_ctx_t ctx { EVP_MD_CTX_create() };
|
||||
|
||||
if(EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, pkey) != 1) {
|
||||
if (EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, pkey) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(EVP_DigestVerifyUpdate(ctx.get(), data.data(), data.size()) != 1) {
|
||||
if (EVP_DigestVerifyUpdate(ctx.get(), data.data(), data.size()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(EVP_DigestVerifyFinal(ctx.get(), (const uint8_t *)signature.data(), signature.size()) != 1) {
|
||||
if (EVP_DigestVerifyFinal(ctx.get(), (const uint8_t *) signature.data(), signature.size()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
for (std::size_t i = 0; i != value.size(); ++i) {
|
||||
value[i] = alphabet[value[i] % alphabet.length()];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
|
|
|
|||
140
src/crypto.h
140
src/crypto.h
|
|
@ -12,93 +12,113 @@
|
|||
#include "utility.h"
|
||||
|
||||
namespace crypto {
|
||||
struct creds_t {
|
||||
struct creds_t {
|
||||
std::string x509;
|
||||
std::string pkey;
|
||||
};
|
||||
constexpr std::size_t digest_size = 256;
|
||||
};
|
||||
constexpr std::size_t digest_size = 256;
|
||||
|
||||
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>;
|
||||
using sha256_t = std::array<std::uint8_t, SHA256_DIGEST_LENGTH>;
|
||||
|
||||
using aes_t = std::array<std::uint8_t, 16>;
|
||||
using x509_t = util::safe_ptr<X509, X509_free>;
|
||||
using x509_store_t = util::safe_ptr<X509_STORE, X509_STORE_free>;
|
||||
using x509_store_ctx_t = util::safe_ptr<X509_STORE_CTX, X509_STORE_CTX_free>;
|
||||
using cipher_ctx_t = util::safe_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
|
||||
using md_ctx_t = util::safe_ptr<EVP_MD_CTX, md_ctx_destroy>;
|
||||
using bio_t = util::safe_ptr<BIO, BIO_free_all>;
|
||||
using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
|
||||
using pkey_ctx_t = util::safe_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
|
||||
using bignum_t = util::safe_ptr<BIGNUM, BN_free>;
|
||||
using aes_t = std::array<std::uint8_t, 16>;
|
||||
using x509_t = util::safe_ptr<X509, X509_free>;
|
||||
using x509_store_t = util::safe_ptr<X509_STORE, X509_STORE_free>;
|
||||
using x509_store_ctx_t = util::safe_ptr<X509_STORE_CTX, X509_STORE_CTX_free>;
|
||||
using cipher_ctx_t = util::safe_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
|
||||
using md_ctx_t = util::safe_ptr<EVP_MD_CTX, md_ctx_destroy>;
|
||||
using bio_t = util::safe_ptr<BIO, BIO_free_all>;
|
||||
using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
|
||||
using pkey_ctx_t = util::safe_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
|
||||
using bignum_t = util::safe_ptr<BIGNUM, BN_free>;
|
||||
|
||||
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,
|
||||
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:
|
||||
class cert_chain_t {
|
||||
public:
|
||||
KITTY_DECL_CONSTR(cert_chain_t)
|
||||
|
||||
void add(x509_t &&cert);
|
||||
void
|
||||
add(x509_t &&cert);
|
||||
|
||||
const char *verify(x509_t::element_type *cert);
|
||||
const char *
|
||||
verify(x509_t::element_type *cert);
|
||||
|
||||
private:
|
||||
private:
|
||||
std::vector<std::pair<x509_t, x509_store_t>> _certs;
|
||||
x509_store_ctx_t _cert_ctx;
|
||||
};
|
||||
};
|
||||
|
||||
namespace cipher {
|
||||
constexpr std::size_t tag_size = 16;
|
||||
constexpr std::size_t round_to_pkcs7_padded(std::size_t size) {
|
||||
namespace cipher {
|
||||
constexpr std::size_t tag_size = 16;
|
||||
constexpr std::size_t
|
||||
round_to_pkcs7_padded(std::size_t size) {
|
||||
return ((size + 15) / 16) * 16;
|
||||
}
|
||||
}
|
||||
|
||||
class cipher_t {
|
||||
public:
|
||||
class cipher_t {
|
||||
public:
|
||||
cipher_ctx_t decrypt_ctx;
|
||||
cipher_ctx_t encrypt_ctx;
|
||||
|
||||
aes_t key;
|
||||
|
||||
bool padding;
|
||||
};
|
||||
};
|
||||
|
||||
class ecb_t : public cipher_t {
|
||||
public:
|
||||
class ecb_t: public cipher_t {
|
||||
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:
|
||||
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);
|
||||
|
||||
|
|
@ -108,16 +128,19 @@ public:
|
|||
* return -1 on error
|
||||
* return bytes written on success
|
||||
*/
|
||||
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:
|
||||
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);
|
||||
|
||||
|
|
@ -127,9 +150,10 @@ public:
|
|||
* return -1 on error
|
||||
* return bytes written on success
|
||||
*/
|
||||
int encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv);
|
||||
};
|
||||
} // namespace cipher
|
||||
int
|
||||
encrypt(const std::string_view &plaintext, std::uint8_t *cipher, aes_t *iv);
|
||||
};
|
||||
} // namespace cipher
|
||||
} // namespace crypto
|
||||
|
||||
#endif //SUNSHINE_CRYPTO_H
|
||||
|
|
|
|||
|
|
@ -27,51 +27,55 @@
|
|||
#include "uuid.h"
|
||||
|
||||
namespace http {
|
||||
using namespace std::literals;
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
using namespace std::literals;
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
int reload_user_creds(const std::string &file);
|
||||
bool user_creds_exist(const std::string &file);
|
||||
int
|
||||
reload_user_creds(const std::string &file);
|
||||
bool
|
||||
user_creds_exist(const std::string &file);
|
||||
|
||||
std::string unique_id;
|
||||
net::net_e origin_pin_allowed;
|
||||
net::net_e origin_web_ui_allowed;
|
||||
std::string unique_id;
|
||||
net::net_e origin_pin_allowed;
|
||||
net::net_e origin_web_ui_allowed;
|
||||
|
||||
int init() {
|
||||
int
|
||||
init() {
|
||||
bool clean_slate = config::sunshine.flags[config::flag::FRESH_STATE];
|
||||
origin_pin_allowed = net::from_enum_string(config::nvhttp.origin_pin_allowed);
|
||||
origin_web_ui_allowed = net::from_enum_string(config::nvhttp.origin_web_ui_allowed);
|
||||
|
||||
if(clean_slate) {
|
||||
if (clean_slate) {
|
||||
unique_id = uuid_util::uuid_t::generate().string();
|
||||
auto dir = std::filesystem::temp_directory_path() / "Sunshine"sv;
|
||||
config::nvhttp.cert = (dir / ("cert-"s + unique_id)).string();
|
||||
config::nvhttp.pkey = (dir / ("pkey-"s + unique_id)).string();
|
||||
}
|
||||
|
||||
if(!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) {
|
||||
if(create_creds(config::nvhttp.pkey, config::nvhttp.cert)) {
|
||||
if (!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) {
|
||||
if (create_creds(config::nvhttp.pkey, config::nvhttp.cert)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(user_creds_exist(config::sunshine.credentials_file)) {
|
||||
if(reload_user_creds(config::sunshine.credentials_file)) return -1;
|
||||
if (user_creds_exist(config::sunshine.credentials_file)) {
|
||||
if (reload_user_creds(config::sunshine.credentials_file)) return -1;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Open the Web UI to set your new username and password and getting started";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int save_user_creds(const std::string &file, const std::string &username, const std::string &password, bool run_our_mouth) {
|
||||
int
|
||||
save_user_creds(const std::string &file, const std::string &username, const std::string &password, bool run_our_mouth) {
|
||||
pt::ptree outputTree;
|
||||
|
||||
if(fs::exists(file)) {
|
||||
if (fs::exists(file)) {
|
||||
try {
|
||||
pt::read_json(file, outputTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "Couldn't read user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -84,17 +88,18 @@ int save_user_creds(const std::string &file, const std::string &username, const
|
|||
try {
|
||||
pt::write_json(file, outputTree);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "generating user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "New credentials have been created"sv;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool user_creds_exist(const std::string &file) {
|
||||
if(!fs::exists(file)) {
|
||||
bool
|
||||
user_creds_exist(const std::string &file) {
|
||||
if (!fs::exists(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -105,14 +110,15 @@ bool user_creds_exist(const std::string &file) {
|
|||
inputTree.find("password") != inputTree.not_found() &&
|
||||
inputTree.find("salt") != inputTree.not_found();
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "validating user credentials: "sv << e.what();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int reload_user_creds(const std::string &file) {
|
||||
int
|
||||
reload_user_creds(const std::string &file) {
|
||||
pt::ptree inputTree;
|
||||
try {
|
||||
pt::read_json(file, inputTree);
|
||||
|
|
@ -120,14 +126,15 @@ int reload_user_creds(const std::string &file) {
|
|||
config::sunshine.password = inputTree.get<std::string>("password");
|
||||
config::sunshine.salt = inputTree.get<std::string>("salt");
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "loading user credentials: "sv << e.what();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int create_creds(const std::string &pkey, const std::string &cert) {
|
||||
int
|
||||
create_creds(const std::string &pkey, const std::string &cert) {
|
||||
fs::path pkey_path = pkey;
|
||||
fs::path cert_path = cert;
|
||||
|
||||
|
|
@ -140,23 +147,23 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
|||
|
||||
std::error_code err_code {};
|
||||
fs::create_directories(pkey_dir, err_code);
|
||||
if(err_code) {
|
||||
if (err_code) {
|
||||
BOOST_LOG(error) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs::create_directories(cert_dir, err_code);
|
||||
if(err_code) {
|
||||
if (err_code) {
|
||||
BOOST_LOG(error) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(write_file(pkey.c_str(), creds.pkey)) {
|
||||
if (write_file(pkey.c_str(), creds.pkey)) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(write_file(cert.c_str(), creds.x509)) {
|
||||
if (write_file(cert.c_str(), creds.x509)) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.cert << ']';
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -165,7 +172,7 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
|||
fs::perms::owner_read | fs::perms::owner_write,
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if(err_code) {
|
||||
if (err_code) {
|
||||
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -174,22 +181,23 @@ int create_creds(const std::string &pkey, const std::string &cert) {
|
|||
fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write,
|
||||
fs::perm_options::replace, err_code);
|
||||
|
||||
if(err_code) {
|
||||
if (err_code) {
|
||||
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool download_file(const std::string &url, const std::string &file) {
|
||||
bool
|
||||
download_file(const std::string &url, const std::string &file) {
|
||||
CURL *curl = curl_easy_init();
|
||||
if(!curl) {
|
||||
if (!curl) {
|
||||
BOOST_LOG(error) << "Couldn't create CURL instance";
|
||||
return false;
|
||||
}
|
||||
FILE *fp = fopen(file.c_str(), "wb");
|
||||
if(!fp) {
|
||||
if (!fp) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << file << ']';
|
||||
curl_easy_cleanup(curl);
|
||||
return false;
|
||||
|
|
@ -201,28 +209,30 @@ bool download_file(const std::string &url, const std::string &file) {
|
|||
curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
|
||||
#endif
|
||||
CURLcode result = curl_easy_perform(curl);
|
||||
if(result != CURLE_OK) {
|
||||
if (result != CURLE_OK) {
|
||||
BOOST_LOG(error) << "Couldn't download ["sv << url << ", code:" << result << ']';
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
fclose(fp);
|
||||
return result == CURLE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
std::string url_escape(const std::string &url) {
|
||||
std::string
|
||||
url_escape(const std::string &url) {
|
||||
CURL *curl = curl_easy_init();
|
||||
char *string = curl_easy_escape(curl, url.c_str(), url.length());
|
||||
std::string result(string);
|
||||
curl_free(string);
|
||||
curl_easy_cleanup(curl);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
std::string url_get_host(const std::string &url) {
|
||||
std::string
|
||||
url_get_host(const std::string &url) {
|
||||
CURLU *curlu = curl_url();
|
||||
curl_url_set(curlu, CURLUPART_URL, url.c_str(), url.length());
|
||||
char *host;
|
||||
if(curl_url_get(curlu, CURLUPART_HOST, &host, 0) != CURLUE_OK) {
|
||||
if (curl_url_get(curlu, CURLUPART_HOST, &host, 0) != CURLUE_OK) {
|
||||
curl_url_cleanup(curlu);
|
||||
return "";
|
||||
}
|
||||
|
|
@ -230,6 +240,6 @@ std::string url_get_host(const std::string &url) {
|
|||
curl_free(host);
|
||||
curl_url_cleanup(curlu);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
|
|
|
|||
|
|
@ -3,21 +3,28 @@
|
|||
|
||||
namespace http {
|
||||
|
||||
int init();
|
||||
int create_creds(const std::string &pkey, const std::string &cert);
|
||||
int save_user_creds(
|
||||
int
|
||||
init();
|
||||
int
|
||||
create_creds(const std::string &pkey, const std::string &cert);
|
||||
int
|
||||
save_user_creds(
|
||||
const std::string &file,
|
||||
const std::string &username,
|
||||
const std::string &password,
|
||||
bool run_our_mouth = false);
|
||||
|
||||
int reload_user_creds(const std::string &file);
|
||||
bool download_file(const std::string &url, const std::string &file);
|
||||
std::string url_escape(const std::string &url);
|
||||
std::string url_get_host(const std::string &url);
|
||||
int
|
||||
reload_user_creds(const std::string &file);
|
||||
bool
|
||||
download_file(const std::string &url, const std::string &file);
|
||||
std::string
|
||||
url_escape(const std::string &url);
|
||||
std::string
|
||||
url_get_host(const std::string &url);
|
||||
|
||||
extern std::string unique_id;
|
||||
extern net::net_e origin_pin_allowed;
|
||||
extern net::net_e origin_web_ui_allowed;
|
||||
extern std::string unique_id;
|
||||
extern net::net_e origin_pin_allowed;
|
||||
extern net::net_e origin_web_ui_allowed;
|
||||
|
||||
} // namespace http
|
||||
|
|
|
|||
369
src/input.cpp
369
src/input.cpp
|
|
@ -20,60 +20,64 @@ extern "C" {
|
|||
using namespace std::literals;
|
||||
namespace input {
|
||||
|
||||
constexpr auto MAX_GAMEPADS = std::min((std::size_t)platf::MAX_GAMEPADS, sizeof(std::int16_t) * 8);
|
||||
#define DISABLE_LEFT_BUTTON_DELAY ((thread_pool_util::ThreadPool::task_id_t)0x01)
|
||||
constexpr auto MAX_GAMEPADS = std::min((std::size_t) platf::MAX_GAMEPADS, sizeof(std::int16_t) * 8);
|
||||
#define DISABLE_LEFT_BUTTON_DELAY ((thread_pool_util::ThreadPool::task_id_t) 0x01)
|
||||
#define ENABLE_LEFT_BUTTON_DELAY nullptr
|
||||
|
||||
constexpr auto VKEY_SHIFT = 0x10;
|
||||
constexpr auto VKEY_LSHIFT = 0xA0;
|
||||
constexpr auto VKEY_RSHIFT = 0xA1;
|
||||
constexpr auto VKEY_CONTROL = 0x11;
|
||||
constexpr auto VKEY_LCONTROL = 0xA2;
|
||||
constexpr auto VKEY_RCONTROL = 0xA3;
|
||||
constexpr auto VKEY_MENU = 0x12;
|
||||
constexpr auto VKEY_LMENU = 0xA4;
|
||||
constexpr auto VKEY_RMENU = 0xA5;
|
||||
constexpr auto VKEY_SHIFT = 0x10;
|
||||
constexpr auto VKEY_LSHIFT = 0xA0;
|
||||
constexpr auto VKEY_RSHIFT = 0xA1;
|
||||
constexpr auto VKEY_CONTROL = 0x11;
|
||||
constexpr auto VKEY_LCONTROL = 0xA2;
|
||||
constexpr auto VKEY_RCONTROL = 0xA3;
|
||||
constexpr auto VKEY_MENU = 0x12;
|
||||
constexpr auto VKEY_LMENU = 0xA4;
|
||||
constexpr auto VKEY_RMENU = 0xA5;
|
||||
|
||||
enum class button_state_e {
|
||||
enum class button_state_e {
|
||||
NONE,
|
||||
DOWN,
|
||||
UP
|
||||
};
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
int alloc_id(std::bitset<N> &gamepad_mask) {
|
||||
for(int x = 0; x < gamepad_mask.size(); ++x) {
|
||||
if(!gamepad_mask[x]) {
|
||||
template <std::size_t N>
|
||||
int
|
||||
alloc_id(std::bitset<N> &gamepad_mask) {
|
||||
for (int x = 0; x < gamepad_mask.size(); ++x) {
|
||||
if (!gamepad_mask[x]) {
|
||||
gamepad_mask[x] = true;
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
void free_id(std::bitset<N> &gamepad_mask, int id) {
|
||||
template <std::size_t N>
|
||||
void
|
||||
free_id(std::bitset<N> &gamepad_mask, int id) {
|
||||
gamepad_mask[id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
static task_pool_util::TaskPool::task_id_t key_press_repeat_id {};
|
||||
static std::unordered_map<short, bool> key_press {};
|
||||
static std::array<std::uint8_t, 5> mouse_press {};
|
||||
static task_pool_util::TaskPool::task_id_t key_press_repeat_id {};
|
||||
static std::unordered_map<short, bool> key_press {};
|
||||
static std::array<std::uint8_t, 5> mouse_press {};
|
||||
|
||||
static platf::input_t platf_input;
|
||||
static std::bitset<platf::MAX_GAMEPADS> gamepadMask {};
|
||||
static platf::input_t platf_input;
|
||||
static std::bitset<platf::MAX_GAMEPADS> gamepadMask {};
|
||||
|
||||
void free_gamepad(platf::input_t &platf_input, int id) {
|
||||
void
|
||||
free_gamepad(platf::input_t &platf_input, int id) {
|
||||
platf::gamepad(platf_input, id, platf::gamepad_state_t {});
|
||||
platf::free_gamepad(platf_input, id);
|
||||
|
||||
free_id(gamepadMask, id);
|
||||
}
|
||||
struct gamepad_t {
|
||||
gamepad_t() : gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {}
|
||||
}
|
||||
struct gamepad_t {
|
||||
gamepad_t():
|
||||
gamepad_state {}, back_timeout_id {}, id { -1 }, back_button_state { button_state_e::NONE } {}
|
||||
~gamepad_t() {
|
||||
if(id >= 0) {
|
||||
if (id >= 0) {
|
||||
task_pool.push([id = this->id]() {
|
||||
free_gamepad(platf_input, id);
|
||||
});
|
||||
|
|
@ -92,9 +96,9 @@ struct gamepad_t {
|
|||
// Sunshine forces the button to be in a specific state until the gamepad state matches that of
|
||||
// Moonlight once more.
|
||||
button_state_e back_button_state;
|
||||
};
|
||||
};
|
||||
|
||||
struct input_t {
|
||||
struct input_t {
|
||||
enum shortkey_e {
|
||||
CTRL = 0x1,
|
||||
ALT = 0x2,
|
||||
|
|
@ -105,8 +109,8 @@ struct input_t {
|
|||
|
||||
input_t(
|
||||
safe::mail_raw_t::event_t<input::touch_port_t> touch_port_event,
|
||||
platf::rumble_queue_t rumble_queue)
|
||||
: shortcutFlags {},
|
||||
platf::rumble_queue_t rumble_queue):
|
||||
shortcutFlags {},
|
||||
active_gamepad_state {},
|
||||
gamepads(MAX_GAMEPADS),
|
||||
touch_port_event { std::move(touch_port_event) },
|
||||
|
|
@ -126,44 +130,47 @@ struct input_t {
|
|||
thread_pool_util::ThreadPool::task_id_t mouse_left_button_timeout;
|
||||
|
||||
input::touch_port_t touch_port;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Apply shortcut based on VKEY
|
||||
* On success
|
||||
* return > 0
|
||||
* On nothing
|
||||
* return 0
|
||||
*/
|
||||
inline int apply_shortcut(short keyCode) {
|
||||
inline int
|
||||
apply_shortcut(short keyCode) {
|
||||
constexpr auto VK_F1 = 0x70;
|
||||
constexpr auto VK_F13 = 0x7C;
|
||||
|
||||
BOOST_LOG(debug) << "Apply Shortcut: 0x"sv << util::hex((std::uint8_t)keyCode).to_string_view();
|
||||
BOOST_LOG(debug) << "Apply Shortcut: 0x"sv << util::hex((std::uint8_t) keyCode).to_string_view();
|
||||
|
||||
if(keyCode >= VK_F1 && keyCode <= VK_F13) {
|
||||
if (keyCode >= VK_F1 && keyCode <= VK_F13) {
|
||||
mail::man->event<int>(mail::switch_display)->raise(keyCode - VK_F1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch(keyCode) {
|
||||
switch (keyCode) {
|
||||
case 0x4E /* VKEY_N */:
|
||||
display_cursor = !display_cursor;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void print(PNV_REL_MOUSE_MOVE_PACKET packet) {
|
||||
void
|
||||
print(PNV_REL_MOUSE_MOVE_PACKET packet) {
|
||||
BOOST_LOG(debug)
|
||||
<< "--begin relative mouse move packet--"sv << std::endl
|
||||
<< "deltaX ["sv << util::endian::big(packet->deltaX) << ']' << std::endl
|
||||
<< "deltaY ["sv << util::endian::big(packet->deltaY) << ']' << std::endl
|
||||
<< "--end relative mouse move packet--"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void print(PNV_ABS_MOUSE_MOVE_PACKET packet) {
|
||||
void
|
||||
print(PNV_ABS_MOUSE_MOVE_PACKET packet) {
|
||||
BOOST_LOG(debug)
|
||||
<< "--begin absolute mouse move packet--"sv << std::endl
|
||||
<< "x ["sv << util::endian::big(packet->x) << ']' << std::endl
|
||||
|
|
@ -171,31 +178,35 @@ void print(PNV_ABS_MOUSE_MOVE_PACKET packet) {
|
|||
<< "width ["sv << util::endian::big(packet->width) << ']' << std::endl
|
||||
<< "height ["sv << util::endian::big(packet->height) << ']' << std::endl
|
||||
<< "--end absolute mouse move packet--"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void print(PNV_MOUSE_BUTTON_PACKET packet) {
|
||||
void
|
||||
print(PNV_MOUSE_BUTTON_PACKET packet) {
|
||||
BOOST_LOG(debug)
|
||||
<< "--begin mouse button packet--"sv << std::endl
|
||||
<< "action ["sv << util::hex(packet->header.magic).to_string_view() << ']' << std::endl
|
||||
<< "button ["sv << util::hex(packet->button).to_string_view() << ']' << std::endl
|
||||
<< "--end mouse button packet--"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void print(PNV_SCROLL_PACKET packet) {
|
||||
void
|
||||
print(PNV_SCROLL_PACKET packet) {
|
||||
BOOST_LOG(debug)
|
||||
<< "--begin mouse scroll packet--"sv << std::endl
|
||||
<< "scrollAmt1 ["sv << util::endian::big(packet->scrollAmt1) << ']' << std::endl
|
||||
<< "--end mouse scroll packet--"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void print(PSS_HSCROLL_PACKET packet) {
|
||||
void
|
||||
print(PSS_HSCROLL_PACKET packet) {
|
||||
BOOST_LOG(debug)
|
||||
<< "--begin mouse hscroll packet--"sv << std::endl
|
||||
<< "scrollAmount ["sv << util::endian::big(packet->scrollAmount) << ']' << std::endl
|
||||
<< "--end mouse hscroll packet--"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void print(PNV_KEYBOARD_PACKET packet) {
|
||||
void
|
||||
print(PNV_KEYBOARD_PACKET packet) {
|
||||
BOOST_LOG(debug)
|
||||
<< "--begin keyboard packet--"sv << std::endl
|
||||
<< "keyAction ["sv << util::hex(packet->header.magic).to_string_view() << ']' << std::endl
|
||||
|
|
@ -203,17 +214,19 @@ void print(PNV_KEYBOARD_PACKET packet) {
|
|||
<< "modifiers ["sv << util::hex(packet->modifiers).to_string_view() << ']' << std::endl
|
||||
<< "flags ["sv << util::hex(packet->flags).to_string_view() << ']' << std::endl
|
||||
<< "--end keyboard packet--"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void print(PNV_UNICODE_PACKET packet) {
|
||||
void
|
||||
print(PNV_UNICODE_PACKET packet) {
|
||||
std::string text(packet->text, util::endian::big(packet->header.size) - sizeof(packet->header.magic));
|
||||
BOOST_LOG(debug)
|
||||
<< "--begin unicode packet--"sv << std::endl
|
||||
<< "text ["sv << text << ']' << std::endl
|
||||
<< "--end unicode packet--"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void print(PNV_MULTI_CONTROLLER_PACKET packet) {
|
||||
void
|
||||
print(PNV_MULTI_CONTROLLER_PACKET packet) {
|
||||
// Moonlight spams controller packet even when not necessary
|
||||
BOOST_LOG(verbose)
|
||||
<< "--begin controller packet--"sv << std::endl
|
||||
|
|
@ -227,62 +240,65 @@ void print(PNV_MULTI_CONTROLLER_PACKET packet) {
|
|||
<< "rightStickX ["sv << packet->rightStickX << ']' << std::endl
|
||||
<< "rightStickY ["sv << packet->rightStickY << ']' << std::endl
|
||||
<< "--end controller packet--"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void print(void *payload) {
|
||||
auto header = (PNV_INPUT_HEADER)payload;
|
||||
void
|
||||
print(void *payload) {
|
||||
auto header = (PNV_INPUT_HEADER) payload;
|
||||
|
||||
switch(util::endian::little(header->magic)) {
|
||||
switch (util::endian::little(header->magic)) {
|
||||
case MOUSE_MOVE_REL_MAGIC_GEN5:
|
||||
print((PNV_REL_MOUSE_MOVE_PACKET)payload);
|
||||
print((PNV_REL_MOUSE_MOVE_PACKET) payload);
|
||||
break;
|
||||
case MOUSE_MOVE_ABS_MAGIC:
|
||||
print((PNV_ABS_MOUSE_MOVE_PACKET)payload);
|
||||
print((PNV_ABS_MOUSE_MOVE_PACKET) payload);
|
||||
break;
|
||||
case MOUSE_BUTTON_DOWN_EVENT_MAGIC_GEN5:
|
||||
case MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5:
|
||||
print((PNV_MOUSE_BUTTON_PACKET)payload);
|
||||
print((PNV_MOUSE_BUTTON_PACKET) payload);
|
||||
break;
|
||||
case SCROLL_MAGIC_GEN5:
|
||||
print((PNV_SCROLL_PACKET)payload);
|
||||
print((PNV_SCROLL_PACKET) payload);
|
||||
break;
|
||||
case SS_HSCROLL_MAGIC:
|
||||
print((PSS_HSCROLL_PACKET)payload);
|
||||
print((PSS_HSCROLL_PACKET) payload);
|
||||
break;
|
||||
case KEY_DOWN_EVENT_MAGIC:
|
||||
case KEY_UP_EVENT_MAGIC:
|
||||
print((PNV_KEYBOARD_PACKET)payload);
|
||||
print((PNV_KEYBOARD_PACKET) payload);
|
||||
break;
|
||||
case UTF8_TEXT_EVENT_MAGIC:
|
||||
print((PNV_UNICODE_PACKET)payload);
|
||||
print((PNV_UNICODE_PACKET) payload);
|
||||
break;
|
||||
case MULTI_CONTROLLER_MAGIC_GEN5:
|
||||
print((PNV_MULTI_CONTROLLER_PACKET)payload);
|
||||
print((PNV_MULTI_CONTROLLER_PACKET) payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, PNV_REL_MOUSE_MOVE_PACKET packet) {
|
||||
if(!config::input.mouse) {
|
||||
void
|
||||
passthrough(std::shared_ptr<input_t> &input, PNV_REL_MOUSE_MOVE_PACKET packet) {
|
||||
if (!config::input.mouse) {
|
||||
return;
|
||||
}
|
||||
|
||||
input->mouse_left_button_timeout = DISABLE_LEFT_BUTTON_DELAY;
|
||||
platf::move_mouse(platf_input, util::endian::big(packet->deltaX), util::endian::big(packet->deltaY));
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, PNV_ABS_MOUSE_MOVE_PACKET packet) {
|
||||
if(!config::input.mouse) {
|
||||
void
|
||||
passthrough(std::shared_ptr<input_t> &input, PNV_ABS_MOUSE_MOVE_PACKET packet) {
|
||||
if (!config::input.mouse) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(input->mouse_left_button_timeout == DISABLE_LEFT_BUTTON_DELAY) {
|
||||
if (input->mouse_left_button_timeout == DISABLE_LEFT_BUTTON_DELAY) {
|
||||
input->mouse_left_button_timeout = ENABLE_LEFT_BUTTON_DELAY;
|
||||
}
|
||||
|
||||
auto &touch_port_event = input->touch_port_event;
|
||||
auto &touch_port = input->touch_port;
|
||||
if(touch_port_event->peek()) {
|
||||
if (touch_port_event->peek()) {
|
||||
touch_port = *touch_port_event->pop();
|
||||
}
|
||||
|
||||
|
|
@ -291,14 +307,14 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_ABS_MOUSE_MOVE_PACKET pack
|
|||
|
||||
// Prevent divide by zero
|
||||
// Don't expect it to happen, but just in case
|
||||
if(!packet->width || !packet->height) {
|
||||
if (!packet->width || !packet->height) {
|
||||
BOOST_LOG(warning) << "Moonlight passed invalid dimensions"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto width = (float)util::endian::big(packet->width);
|
||||
auto height = (float)util::endian::big(packet->height);
|
||||
auto width = (float) util::endian::big(packet->width);
|
||||
auto height = (float) util::endian::big(packet->height);
|
||||
|
||||
auto scalarX = touch_port.width / width;
|
||||
auto scalarY = touch_port.height / height;
|
||||
|
|
@ -318,17 +334,18 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_ABS_MOUSE_MOVE_PACKET pack
|
|||
};
|
||||
|
||||
platf::abs_mouse(platf_input, abs_port, (x - offsetX) * touch_port.scalar_inv, (y - offsetY) * touch_port.scalar_inv);
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet) {
|
||||
if(!config::input.mouse) {
|
||||
void
|
||||
passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet) {
|
||||
if (!config::input.mouse) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto release = util::endian::little(packet->header.magic) == MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5;
|
||||
auto button = util::endian::big(packet->button);
|
||||
if(button > 0 && button < mouse_press.size()) {
|
||||
if(mouse_press[button] != release) {
|
||||
if (button > 0 && button < mouse_press.size()) {
|
||||
if (mouse_press[button] != release) {
|
||||
// button state is already what we want
|
||||
return;
|
||||
}
|
||||
|
|
@ -350,10 +367,10 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet
|
|||
* input->mouse_left_button_timeout can only be nullptr
|
||||
* when the last mouse coordinates were absolute
|
||||
/*/
|
||||
if(button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) {
|
||||
if (button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) {
|
||||
auto f = [=]() {
|
||||
auto left_released = mouse_press[BUTTON_LEFT];
|
||||
if(left_released) {
|
||||
if (left_released) {
|
||||
// Already released left button
|
||||
return;
|
||||
}
|
||||
|
|
@ -367,7 +384,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet
|
|||
|
||||
return;
|
||||
}
|
||||
if(
|
||||
if (
|
||||
button == BUTTON_RIGHT && !release &&
|
||||
input->mouse_left_button_timeout > DISABLE_LEFT_BUTTON_DELAY) {
|
||||
platf::button_mouse(platf_input, BUTTON_RIGHT, false);
|
||||
|
|
@ -380,26 +397,28 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MOUSE_BUTTON_PACKET packet
|
|||
///////////////////////////////////
|
||||
|
||||
platf::button_mouse(platf_input, button, release);
|
||||
}
|
||||
}
|
||||
|
||||
short map_keycode(short keycode) {
|
||||
short
|
||||
map_keycode(short keycode) {
|
||||
auto it = config::input.keybindings.find(keycode);
|
||||
if(it != std::end(config::input.keybindings)) {
|
||||
if (it != std::end(config::input.keybindings)) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return keycode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Update flags for keyboard shortcut combo's
|
||||
*/
|
||||
inline void update_shortcutFlags(int *flags, short keyCode, bool release) {
|
||||
switch(keyCode) {
|
||||
inline void
|
||||
update_shortcutFlags(int *flags, short keyCode, bool release) {
|
||||
switch (keyCode) {
|
||||
case VKEY_SHIFT:
|
||||
case VKEY_LSHIFT:
|
||||
case VKEY_RSHIFT:
|
||||
if(release) {
|
||||
if (release) {
|
||||
*flags &= ~input_t::SHIFT;
|
||||
}
|
||||
else {
|
||||
|
|
@ -409,7 +428,7 @@ inline void update_shortcutFlags(int *flags, short keyCode, bool release) {
|
|||
case VKEY_CONTROL:
|
||||
case VKEY_LCONTROL:
|
||||
case VKEY_RCONTROL:
|
||||
if(release) {
|
||||
if (release) {
|
||||
*flags &= ~input_t::CTRL;
|
||||
}
|
||||
else {
|
||||
|
|
@ -419,7 +438,7 @@ inline void update_shortcutFlags(int *flags, short keyCode, bool release) {
|
|||
case VKEY_MENU:
|
||||
case VKEY_LMENU:
|
||||
case VKEY_RMENU:
|
||||
if(release) {
|
||||
if (release) {
|
||||
*flags &= ~input_t::ALT;
|
||||
}
|
||||
else {
|
||||
|
|
@ -427,11 +446,12 @@ inline void update_shortcutFlags(int *flags, short keyCode, bool release) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void repeat_key(short key_code) {
|
||||
void
|
||||
repeat_key(short key_code) {
|
||||
// If key no longer pressed, stop repeating
|
||||
if(!key_press[key_code]) {
|
||||
if (!key_press[key_code]) {
|
||||
key_press_repeat_id = nullptr;
|
||||
return;
|
||||
}
|
||||
|
|
@ -439,10 +459,11 @@ void repeat_key(short key_code) {
|
|||
platf::keyboard(platf_input, map_keycode(key_code), false);
|
||||
|
||||
key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_period, key_code).task_id;
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, PNV_KEYBOARD_PACKET packet) {
|
||||
if(!config::input.keyboard) {
|
||||
void
|
||||
passthrough(std::shared_ptr<input_t> &input, PNV_KEYBOARD_PACKET packet) {
|
||||
if (!config::input.keyboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -450,19 +471,19 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_KEYBOARD_PACKET packet) {
|
|||
auto keyCode = packet->keyCode & 0x00FF;
|
||||
|
||||
auto &pressed = key_press[keyCode];
|
||||
if(!pressed) {
|
||||
if(!release) {
|
||||
if (!pressed) {
|
||||
if (!release) {
|
||||
// A new key has been pressed down, we need to check for key combo's
|
||||
// If a key-combo has been pressed down, don't pass it through
|
||||
if(input->shortcutFlags == input_t::SHORTCUT && apply_shortcut(keyCode) > 0) {
|
||||
if (input->shortcutFlags == input_t::SHORTCUT && apply_shortcut(keyCode) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(key_press_repeat_id) {
|
||||
if (key_press_repeat_id) {
|
||||
task_pool.cancel(key_press_repeat_id);
|
||||
}
|
||||
|
||||
if(config::input.key_repeat_delay.count() > 0) {
|
||||
if (config::input.key_repeat_delay.count() > 0) {
|
||||
key_press_repeat_id = task_pool.pushDelayed(repeat_key, config::input.key_repeat_delay, keyCode).task_id;
|
||||
}
|
||||
}
|
||||
|
|
@ -471,7 +492,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_KEYBOARD_PACKET packet) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if(!release) {
|
||||
else if (!release) {
|
||||
// Already pressed down key
|
||||
return;
|
||||
}
|
||||
|
|
@ -480,45 +501,49 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_KEYBOARD_PACKET packet) {
|
|||
|
||||
update_shortcutFlags(&input->shortcutFlags, map_keycode(keyCode), release);
|
||||
platf::keyboard(platf_input, map_keycode(keyCode), release);
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(PNV_SCROLL_PACKET packet) {
|
||||
if(!config::input.mouse) {
|
||||
void
|
||||
passthrough(PNV_SCROLL_PACKET packet) {
|
||||
if (!config::input.mouse) {
|
||||
return;
|
||||
}
|
||||
|
||||
platf::scroll(platf_input, util::endian::big(packet->scrollAmt1));
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(PSS_HSCROLL_PACKET packet) {
|
||||
if(!config::input.mouse) {
|
||||
void
|
||||
passthrough(PSS_HSCROLL_PACKET packet) {
|
||||
if (!config::input.mouse) {
|
||||
return;
|
||||
}
|
||||
|
||||
platf::hscroll(platf_input, util::endian::big(packet->scrollAmount));
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(PNV_UNICODE_PACKET packet) {
|
||||
if(!config::input.keyboard) {
|
||||
void
|
||||
passthrough(PNV_UNICODE_PACKET packet) {
|
||||
if (!config::input.keyboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto size = util::endian::big(packet->header.size) - sizeof(packet->header.magic);
|
||||
platf::unicode(platf_input, packet->text, size);
|
||||
}
|
||||
}
|
||||
|
||||
int updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std::int16_t new_state, const platf::rumble_queue_t &rumble_queue) {
|
||||
int
|
||||
updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std::int16_t new_state, const platf::rumble_queue_t &rumble_queue) {
|
||||
auto xorGamepadMask = old_state ^ new_state;
|
||||
if(!xorGamepadMask) {
|
||||
if (!xorGamepadMask) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(int x = 0; x < sizeof(std::int16_t) * 8; ++x) {
|
||||
if((xorGamepadMask >> x) & 1) {
|
||||
for (int x = 0; x < sizeof(std::int16_t) * 8; ++x) {
|
||||
if ((xorGamepadMask >> x) & 1) {
|
||||
auto &gamepad = gamepads[x];
|
||||
|
||||
if((old_state >> x) & 1) {
|
||||
if(gamepad.id < 0) {
|
||||
if ((old_state >> x) & 1) {
|
||||
if (gamepad.id < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -528,12 +553,12 @@ int updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std
|
|||
else {
|
||||
auto id = alloc_id(gamepadMask);
|
||||
|
||||
if(id < 0) {
|
||||
if (id < 0) {
|
||||
// Out of gamepads
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(platf::alloc_gamepad(platf_input, id, rumble_queue)) {
|
||||
if (platf::alloc_gamepad(platf_input, id, rumble_queue)) {
|
||||
free_id(gamepadMask, id);
|
||||
// allocating a gamepad failed: solution: ignore gamepads
|
||||
// The implementations of platf::alloc_gamepad already has logging
|
||||
|
|
@ -546,26 +571,27 @@ int updateGamepads(std::vector<gamepad_t> &gamepads, std::int16_t old_state, std
|
|||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET packet) {
|
||||
if(!config::input.controller) {
|
||||
void
|
||||
passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET packet) {
|
||||
if (!config::input.controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(updateGamepads(input->gamepads, input->active_gamepad_state, packet->activeGamepadMask, input->rumble_queue)) {
|
||||
if (updateGamepads(input->gamepads, input->active_gamepad_state, packet->activeGamepadMask, input->rumble_queue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
input->active_gamepad_state = packet->activeGamepadMask;
|
||||
|
||||
if(packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) {
|
||||
if (packet->controllerNumber < 0 || packet->controllerNumber >= input->gamepads.size()) {
|
||||
BOOST_LOG(warning) << "ControllerNumber out of range ["sv << packet->controllerNumber << ']';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!((input->active_gamepad_state >> packet->controllerNumber) & 1)) {
|
||||
if (!((input->active_gamepad_state >> packet->controllerNumber) & 1)) {
|
||||
BOOST_LOG(warning) << "ControllerNumber ["sv << packet->controllerNumber << "] not allocated"sv;
|
||||
|
||||
return;
|
||||
|
|
@ -575,7 +601,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
|||
|
||||
// If this gamepad has not been initialized, ignore it.
|
||||
// This could happen when platf::alloc_gamepad fails
|
||||
if(gamepad.id < 0) {
|
||||
if (gamepad.id < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -591,15 +617,15 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
|||
};
|
||||
|
||||
auto bf_new = gamepad_state.buttonFlags;
|
||||
switch(gamepad.back_button_state) {
|
||||
switch (gamepad.back_button_state) {
|
||||
case button_state_e::UP:
|
||||
if(!(platf::BACK & bf_new)) {
|
||||
if (!(platf::BACK & bf_new)) {
|
||||
gamepad.back_button_state = button_state_e::NONE;
|
||||
}
|
||||
gamepad_state.buttonFlags &= ~platf::BACK;
|
||||
break;
|
||||
case button_state_e::DOWN:
|
||||
if(platf::BACK & bf_new) {
|
||||
if (platf::BACK & bf_new) {
|
||||
gamepad.back_button_state = button_state_e::NONE;
|
||||
}
|
||||
gamepad_state.buttonFlags |= platf::BACK;
|
||||
|
|
@ -611,10 +637,10 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
|||
bf = gamepad_state.buttonFlags ^ gamepad.gamepad_state.buttonFlags;
|
||||
bf_new = gamepad_state.buttonFlags;
|
||||
|
||||
if(platf::BACK & bf) {
|
||||
if(platf::BACK & bf_new) {
|
||||
if (platf::BACK & bf) {
|
||||
if (platf::BACK & bf_new) {
|
||||
// Don't emulate home button if timeout < 0
|
||||
if(config::input.back_button_timeout >= 0ms) {
|
||||
if (config::input.back_button_timeout >= 0ms) {
|
||||
auto f = [input, controller = packet->controllerNumber]() {
|
||||
auto &gamepad = input->gamepads[controller];
|
||||
|
||||
|
|
@ -639,7 +665,7 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
|||
gamepad.back_timeout_id = task_pool.pushDelayed(std::move(f), config::input.back_button_timeout).task_id;
|
||||
}
|
||||
}
|
||||
else if(gamepad.back_timeout_id) {
|
||||
else if (gamepad.back_timeout_id) {
|
||||
task_pool.cancel(gamepad.back_timeout_id);
|
||||
gamepad.back_timeout_id = nullptr;
|
||||
}
|
||||
|
|
@ -648,80 +674,85 @@ void passthrough(std::shared_ptr<input_t> &input, PNV_MULTI_CONTROLLER_PACKET pa
|
|||
platf::gamepad(platf_input, gamepad.id, gamepad_state);
|
||||
|
||||
gamepad.gamepad_state = gamepad_state;
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough_helper(std::shared_ptr<input_t> input, std::vector<std::uint8_t> &&input_data) {
|
||||
void
|
||||
passthrough_helper(std::shared_ptr<input_t> input, std::vector<std::uint8_t> &&input_data) {
|
||||
void *payload = input_data.data();
|
||||
auto header = (PNV_INPUT_HEADER)payload;
|
||||
auto header = (PNV_INPUT_HEADER) payload;
|
||||
|
||||
switch(util::endian::little(header->magic)) {
|
||||
switch (util::endian::little(header->magic)) {
|
||||
case MOUSE_MOVE_REL_MAGIC_GEN5:
|
||||
passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET)payload);
|
||||
passthrough(input, (PNV_REL_MOUSE_MOVE_PACKET) payload);
|
||||
break;
|
||||
case MOUSE_MOVE_ABS_MAGIC:
|
||||
passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET)payload);
|
||||
passthrough(input, (PNV_ABS_MOUSE_MOVE_PACKET) payload);
|
||||
break;
|
||||
case MOUSE_BUTTON_DOWN_EVENT_MAGIC_GEN5:
|
||||
case MOUSE_BUTTON_UP_EVENT_MAGIC_GEN5:
|
||||
passthrough(input, (PNV_MOUSE_BUTTON_PACKET)payload);
|
||||
passthrough(input, (PNV_MOUSE_BUTTON_PACKET) payload);
|
||||
break;
|
||||
case SCROLL_MAGIC_GEN5:
|
||||
passthrough((PNV_SCROLL_PACKET)payload);
|
||||
passthrough((PNV_SCROLL_PACKET) payload);
|
||||
break;
|
||||
case SS_HSCROLL_MAGIC:
|
||||
passthrough((PSS_HSCROLL_PACKET)payload);
|
||||
passthrough((PSS_HSCROLL_PACKET) payload);
|
||||
break;
|
||||
case KEY_DOWN_EVENT_MAGIC:
|
||||
case KEY_UP_EVENT_MAGIC:
|
||||
passthrough(input, (PNV_KEYBOARD_PACKET)payload);
|
||||
passthrough(input, (PNV_KEYBOARD_PACKET) payload);
|
||||
break;
|
||||
case UTF8_TEXT_EVENT_MAGIC:
|
||||
passthrough((PNV_UNICODE_PACKET)payload);
|
||||
passthrough((PNV_UNICODE_PACKET) payload);
|
||||
break;
|
||||
case MULTI_CONTROLLER_MAGIC_GEN5:
|
||||
passthrough(input, (PNV_MULTI_CONTROLLER_PACKET)payload);
|
||||
passthrough(input, (PNV_MULTI_CONTROLLER_PACKET) payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data) {
|
||||
void
|
||||
passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data) {
|
||||
task_pool.push(passthrough_helper, input, move_by_copy_util::cmove(input_data));
|
||||
}
|
||||
}
|
||||
|
||||
void reset(std::shared_ptr<input_t> &input) {
|
||||
void
|
||||
reset(std::shared_ptr<input_t> &input) {
|
||||
task_pool.cancel(key_press_repeat_id);
|
||||
task_pool.cancel(input->mouse_left_button_timeout);
|
||||
|
||||
// Ensure input is synchronous, by using the task_pool
|
||||
task_pool.push([]() {
|
||||
for(int x = 0; x < mouse_press.size(); ++x) {
|
||||
if(mouse_press[x]) {
|
||||
for (int x = 0; x < mouse_press.size(); ++x) {
|
||||
if (mouse_press[x]) {
|
||||
platf::button_mouse(platf_input, x, true);
|
||||
mouse_press[x] = false;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &kp : key_press) {
|
||||
for (auto &kp : key_press) {
|
||||
platf::keyboard(platf_input, kp.first & 0x00FF, true);
|
||||
key_press[kp.first] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class deinit_t : public platf::deinit_t {
|
||||
public:
|
||||
class deinit_t: public platf::deinit_t {
|
||||
public:
|
||||
~deinit_t() override {
|
||||
platf_input.reset();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t> init() {
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t>
|
||||
init() {
|
||||
platf_input = platf::input();
|
||||
|
||||
return std::make_unique<deinit_t>();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<input_t> alloc(safe::mail_t mail) {
|
||||
std::shared_ptr<input_t>
|
||||
alloc(safe::mail_t mail) {
|
||||
auto input = std::make_shared<input_t>(
|
||||
mail->event<input::touch_port_t>(mail::touch_port),
|
||||
mail->queue<platf::rumble_t>(mail::rumble));
|
||||
|
|
@ -734,5 +765,5 @@ std::shared_ptr<input_t> alloc(safe::mail_t mail) {
|
|||
100ms);
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
} // namespace input
|
||||
|
|
|
|||
22
src/input.h
22
src/input.h
|
|
@ -9,25 +9,29 @@
|
|||
#include "thread_safe.h"
|
||||
|
||||
namespace input {
|
||||
struct input_t;
|
||||
struct input_t;
|
||||
|
||||
void print(void *input);
|
||||
void reset(std::shared_ptr<input_t> &input);
|
||||
void passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data);
|
||||
void
|
||||
print(void *input);
|
||||
void
|
||||
reset(std::shared_ptr<input_t> &input);
|
||||
void
|
||||
passthrough(std::shared_ptr<input_t> &input, std::vector<std::uint8_t> &&input_data);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t>
|
||||
init();
|
||||
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t> init();
|
||||
std::shared_ptr<input_t>
|
||||
alloc(safe::mail_t mail);
|
||||
|
||||
std::shared_ptr<input_t> alloc(safe::mail_t mail);
|
||||
|
||||
struct touch_port_t : public platf::touch_port_t {
|
||||
struct touch_port_t: public platf::touch_port_t {
|
||||
int env_width, env_height;
|
||||
|
||||
// Offset x and y coordinates of the client
|
||||
float client_offsetX, client_offsetY;
|
||||
|
||||
float scalar_inv;
|
||||
};
|
||||
};
|
||||
} // namespace input
|
||||
|
||||
#endif // SUNSHINE_INPUT_H
|
||||
|
|
|
|||
86
src/main.cpp
86
src/main.cpp
|
|
@ -55,7 +55,8 @@ using text_sink = bl::sinks::asynchronous_sink<bl::sinks::text_ostream_backend>;
|
|||
boost::shared_ptr<text_sink> sink;
|
||||
|
||||
struct NoDelete {
|
||||
void operator()(void *) {}
|
||||
void
|
||||
operator()(void *) {}
|
||||
};
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
|
||||
|
|
@ -69,7 +70,8 @@ BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
|
|||
* print_help("sunshine");
|
||||
* ```
|
||||
*/
|
||||
void print_help(const char *name) {
|
||||
void
|
||||
print_help(const char *name) {
|
||||
std::cout
|
||||
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
|
||||
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
|
||||
|
|
@ -90,20 +92,21 @@ void print_help(const char *name) {
|
|||
}
|
||||
|
||||
namespace help {
|
||||
int entry(const char *name, int argc, char *argv[]) {
|
||||
int
|
||||
entry(const char *name, int argc, char *argv[]) {
|
||||
print_help(name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace help
|
||||
|
||||
namespace version {
|
||||
int entry(const char *name, int argc, char *argv[]) {
|
||||
int
|
||||
entry(const char *name, int argc, char *argv[]) {
|
||||
std::cout << PROJECT_NAME << " version: v" << PROJECT_VER << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace version
|
||||
|
||||
|
||||
/**
|
||||
* @brief Flush the log.
|
||||
*
|
||||
|
|
@ -112,25 +115,29 @@ int entry(const char *name, int argc, char *argv[]) {
|
|||
* log_flush();
|
||||
* ```
|
||||
*/
|
||||
void log_flush() {
|
||||
void
|
||||
log_flush() {
|
||||
sink->flush();
|
||||
}
|
||||
|
||||
std::map<int, std::function<void()>> signal_handlers;
|
||||
void on_signal_forwarder(int sig) {
|
||||
void
|
||||
on_signal_forwarder(int sig) {
|
||||
signal_handlers.at(sig)();
|
||||
}
|
||||
|
||||
template<class FN>
|
||||
void on_signal(int sig, FN &&fn) {
|
||||
template <class FN>
|
||||
void
|
||||
on_signal(int sig, FN &&fn) {
|
||||
signal_handlers.emplace(sig, std::forward<FN>(fn));
|
||||
|
||||
std::signal(sig, on_signal_forwarder);
|
||||
}
|
||||
|
||||
namespace gen_creds {
|
||||
int entry(const char *name, int argc, char *argv[]) {
|
||||
if(argc < 2 || argv[0] == "help"sv || argv[1] == "help"sv) {
|
||||
int
|
||||
entry(const char *name, int argc, char *argv[]) {
|
||||
if (argc < 2 || argv[0] == "help"sv || argv[1] == "help"sv) {
|
||||
print_help(name);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -138,7 +145,7 @@ int entry(const char *name, int argc, char *argv[]) {
|
|||
http::save_user_creds(config::sunshine.credentials_file, argv[0], argv[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace gen_creds
|
||||
|
||||
std::map<std::string_view, std::function<int(const char *name, int argc, char **argv)>> cmd_to_func {
|
||||
|
|
@ -148,8 +155,9 @@ std::map<std::string_view, std::function<int(const char *name, int argc, char **
|
|||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
LRESULT CALLBACK SessionMonitorWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
switch(uMsg) {
|
||||
LRESULT CALLBACK
|
||||
SessionMonitorWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
switch (uMsg) {
|
||||
case WM_ENDSESSION: {
|
||||
// Raise a SIGINT to trigger our cleanup logic and terminate ourselves
|
||||
std::cout << "Received WM_ENDSESSION"sv << std::endl;
|
||||
|
|
@ -176,7 +184,8 @@ LRESULT CALLBACK SessionMonitorWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, L
|
|||
* main(1, const char* args[] = {"sunshine", nullptr});
|
||||
* ```
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
task_pool_util::TaskPool::task_id_t force_shutdown = nullptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
@ -188,7 +197,7 @@ int main(int argc, char *argv[]) {
|
|||
WNDCLASSA wnd_class {};
|
||||
wnd_class.lpszClassName = "SunshineSessionMonitorClass";
|
||||
wnd_class.lpfnWndProc = SessionMonitorWindowProc;
|
||||
if(!RegisterClassA(&wnd_class)) {
|
||||
if (!RegisterClassA(&wnd_class)) {
|
||||
std::cout << "Failed to register session monitor window class"sv << std::endl;
|
||||
return;
|
||||
}
|
||||
|
|
@ -206,7 +215,7 @@ int main(int argc, char *argv[]) {
|
|||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if(!wnd) {
|
||||
if (!wnd) {
|
||||
std::cout << "Failed to create session monitor window"sv << std::endl;
|
||||
return;
|
||||
}
|
||||
|
|
@ -215,7 +224,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
// Run the message loop for our window
|
||||
MSG msg {};
|
||||
while(GetMessage(&msg, nullptr, 0, 0) > 0) {
|
||||
while (GetMessage(&msg, nullptr, 0, 0) > 0) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
|
@ -225,11 +234,11 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
mail::man = std::make_shared<safe::mail_raw_t>();
|
||||
|
||||
if(config::parse(argc, argv)) {
|
||||
if (config::parse(argc, argv)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(config::sunshine.min_log_level >= 1) {
|
||||
if (config::sunshine.min_log_level >= 1) {
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
}
|
||||
else {
|
||||
|
|
@ -249,7 +258,7 @@ int main(int argc, char *argv[]) {
|
|||
auto log_level = view.attribute_values()[severity].extract<int>().get();
|
||||
|
||||
std::string_view log_type;
|
||||
switch(log_level) {
|
||||
switch (log_level) {
|
||||
case 0:
|
||||
log_type = "Verbose: "sv;
|
||||
break;
|
||||
|
|
@ -284,13 +293,13 @@ int main(int argc, char *argv[]) {
|
|||
bl::core::get()->add_sink(sink);
|
||||
auto fg = util::fail_guard(log_flush);
|
||||
|
||||
if(!config::sunshine.cmd.name.empty()) {
|
||||
if (!config::sunshine.cmd.name.empty()) {
|
||||
auto fn = cmd_to_func.find(config::sunshine.cmd.name);
|
||||
if(fn == std::end(cmd_to_func)) {
|
||||
if (fn == std::end(cmd_to_func)) {
|
||||
BOOST_LOG(fatal) << "Unknown command: "sv << config::sunshine.cmd.name;
|
||||
|
||||
BOOST_LOG(info) << "Possible commands:"sv;
|
||||
for(auto &[key, _] : cmd_to_func) {
|
||||
for (auto &[key, _] : cmd_to_func) {
|
||||
BOOST_LOG(info) << '\t' << key;
|
||||
}
|
||||
|
||||
|
|
@ -338,16 +347,16 @@ int main(int argc, char *argv[]) {
|
|||
proc::refresh(config::stream.file_apps);
|
||||
|
||||
auto deinit_guard = platf::init();
|
||||
if(!deinit_guard) {
|
||||
if (!deinit_guard) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
reed_solomon_init();
|
||||
auto input_deinit_guard = input::init();
|
||||
if(video::init()) {
|
||||
if (video::init()) {
|
||||
return 2;
|
||||
}
|
||||
if(http::init()) {
|
||||
if (http::init()) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
|
@ -362,7 +371,7 @@ int main(int argc, char *argv[]) {
|
|||
});
|
||||
|
||||
// FIXME: Temporary workaround: Simple-Web_server needs to be updated or replaced
|
||||
if(shutdown_event->peek()) {
|
||||
if (shutdown_event->peek()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -395,8 +404,9 @@ int main(int argc, char *argv[]) {
|
|||
* std::string contents = read_file("path/to/file");
|
||||
* ```
|
||||
*/
|
||||
std::string read_file(const char *path) {
|
||||
if(!std::filesystem::exists(path)) {
|
||||
std::string
|
||||
read_file(const char *path) {
|
||||
if (!std::filesystem::exists(path)) {
|
||||
BOOST_LOG(debug) << "Missing file: " << path;
|
||||
return {};
|
||||
}
|
||||
|
|
@ -406,7 +416,7 @@ std::string read_file(const char *path) {
|
|||
std::string input;
|
||||
std::string base64_cert;
|
||||
|
||||
while(!in.eof()) {
|
||||
while (!in.eof()) {
|
||||
std::getline(in, input);
|
||||
base64_cert += input + '\n';
|
||||
}
|
||||
|
|
@ -425,10 +435,11 @@ std::string read_file(const char *path) {
|
|||
* int write_status = write_file("path/to/file", "file contents");
|
||||
* ```
|
||||
*/
|
||||
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()) {
|
||||
if (!out.is_open()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -447,8 +458,9 @@ int write_file(const char *path, const std::string_view &contents) {
|
|||
* std::uint16_t mapped_port = map_port(1);
|
||||
* ```
|
||||
*/
|
||||
std::uint16_t map_port(int port) {
|
||||
std::uint16_t
|
||||
map_port(int port) {
|
||||
// TODO: Ensure port is in the range of 21-65535
|
||||
// TODO: Ensure port is not already in use by another application
|
||||
return (std::uint16_t)((int)config::sunshine.port + port);
|
||||
return (std::uint16_t)((int) config::sunshine.port + port);
|
||||
}
|
||||
|
|
|
|||
65
src/main.h
65
src/main.h
|
|
@ -28,41 +28,54 @@ extern boost::log::sources::severity_logger<int> error;
|
|||
extern boost::log::sources::severity_logger<int> fatal;
|
||||
|
||||
// functions
|
||||
int main(int argc, char *argv[]);
|
||||
void log_flush();
|
||||
void open_url(const std::string &url);
|
||||
void tray_open_ui_cb(struct tray_menu *item);
|
||||
void tray_donate_github_cb(struct tray_menu *item);
|
||||
void tray_donate_mee6_cb(struct tray_menu *item);
|
||||
void tray_donate_patreon_cb(struct tray_menu *item);
|
||||
void tray_donate_paypal_cb(struct tray_menu *item);
|
||||
void tray_quit_cb(struct tray_menu *item);
|
||||
void print_help(const char *name);
|
||||
std::string read_file(const char *path);
|
||||
int write_file(const char *path, const std::string_view &contents);
|
||||
std::uint16_t map_port(int port);
|
||||
int
|
||||
main(int argc, char *argv[]);
|
||||
void
|
||||
log_flush();
|
||||
void
|
||||
open_url(const std::string &url);
|
||||
void
|
||||
tray_open_ui_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_donate_github_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_donate_mee6_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_donate_patreon_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_donate_paypal_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_quit_cb(struct tray_menu *item);
|
||||
void
|
||||
print_help(const char *name);
|
||||
std::string
|
||||
read_file(const char *path);
|
||||
int
|
||||
write_file(const char *path, const std::string_view &contents);
|
||||
std::uint16_t
|
||||
map_port(int port);
|
||||
|
||||
// namespaces
|
||||
namespace mail {
|
||||
#define MAIL(x) \
|
||||
constexpr auto x = std::string_view { \
|
||||
#x \
|
||||
#x \
|
||||
}
|
||||
|
||||
extern safe::mail_t man;
|
||||
extern safe::mail_t man;
|
||||
|
||||
// Global mail
|
||||
MAIL(shutdown);
|
||||
MAIL(broadcast_shutdown);
|
||||
MAIL(video_packets);
|
||||
MAIL(audio_packets);
|
||||
MAIL(switch_display);
|
||||
// Global mail
|
||||
MAIL(shutdown);
|
||||
MAIL(broadcast_shutdown);
|
||||
MAIL(video_packets);
|
||||
MAIL(audio_packets);
|
||||
MAIL(switch_display);
|
||||
|
||||
// Local mail
|
||||
MAIL(touch_port);
|
||||
MAIL(idr);
|
||||
MAIL(rumble);
|
||||
MAIL(hdr);
|
||||
// Local mail
|
||||
MAIL(touch_port);
|
||||
MAIL(idr);
|
||||
MAIL(rumble);
|
||||
MAIL(hdr);
|
||||
#undef MAIL
|
||||
|
||||
} // namespace mail
|
||||
|
|
|
|||
|
|
@ -3,20 +3,21 @@
|
|||
|
||||
#include <utility>
|
||||
namespace move_by_copy_util {
|
||||
/*
|
||||
/*
|
||||
* When a copy is made, it moves the object
|
||||
* This allows you to move an object when a move can't be done.
|
||||
*/
|
||||
template<class T>
|
||||
class MoveByCopy {
|
||||
public:
|
||||
template <class T>
|
||||
class MoveByCopy {
|
||||
public:
|
||||
typedef T move_type;
|
||||
|
||||
private:
|
||||
private:
|
||||
move_type _to_move;
|
||||
|
||||
public:
|
||||
explicit MoveByCopy(move_type &&to_move) : _to_move(std::move(to_move)) {}
|
||||
public:
|
||||
explicit MoveByCopy(move_type &&to_move):
|
||||
_to_move(std::move(to_move)) {}
|
||||
|
||||
MoveByCopy(MoveByCopy &&other) = default;
|
||||
|
||||
|
|
@ -24,9 +25,11 @@ public:
|
|||
*this = other;
|
||||
}
|
||||
|
||||
MoveByCopy &operator=(MoveByCopy &&other) = default;
|
||||
MoveByCopy &
|
||||
operator=(MoveByCopy &&other) = default;
|
||||
|
||||
MoveByCopy &operator=(const MoveByCopy &other) {
|
||||
MoveByCopy &
|
||||
operator=(const MoveByCopy &other) {
|
||||
this->_to_move = std::move(const_cast<MoveByCopy &>(other)._to_move);
|
||||
|
||||
return *this;
|
||||
|
|
@ -35,17 +38,19 @@ public:
|
|||
operator move_type() {
|
||||
return std::move(_to_move);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
MoveByCopy<T> cmove(T &movable) {
|
||||
template <class T>
|
||||
MoveByCopy<T>
|
||||
cmove(T &movable) {
|
||||
return MoveByCopy<T>(std::move(movable));
|
||||
}
|
||||
}
|
||||
|
||||
// Do NOT use this unless you are absolutely certain the object to be moved is no longer used by the caller
|
||||
template<class T>
|
||||
MoveByCopy<T> const_cmove(const T &movable) {
|
||||
// Do NOT use this unless you are absolutely certain the object to be moved is no longer used by the caller
|
||||
template <class T>
|
||||
MoveByCopy<T>
|
||||
const_cmove(const T &movable) {
|
||||
return MoveByCopy<T>(std::move(const_cast<T &>(movable)));
|
||||
}
|
||||
}
|
||||
} // namespace move_by_copy_util
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,26 +6,28 @@
|
|||
|
||||
using namespace std::literals;
|
||||
namespace net {
|
||||
// In the format "xxx.xxx.xxx.xxx/x"
|
||||
std::pair<std::uint32_t, std::uint32_t> ip_block(const std::string_view &ip);
|
||||
// In the format "xxx.xxx.xxx.xxx/x"
|
||||
std::pair<std::uint32_t, std::uint32_t>
|
||||
ip_block(const std::string_view &ip);
|
||||
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>> pc_ips {
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>> pc_ips {
|
||||
ip_block("127.0.0.1/32"sv)
|
||||
};
|
||||
std::vector<std::tuple<std::uint32_t, std::uint32_t>> lan_ips {
|
||||
};
|
||||
std::vector<std::tuple<std::uint32_t, std::uint32_t>> lan_ips {
|
||||
ip_block("192.168.0.0/16"sv),
|
||||
ip_block("172.16.0.0/12"sv),
|
||||
ip_block("10.0.0.0/8"sv)
|
||||
};
|
||||
};
|
||||
|
||||
std::uint32_t ip(const std::string_view &ip_str) {
|
||||
std::uint32_t
|
||||
ip(const std::string_view &ip_str) {
|
||||
auto begin = std::begin(ip_str);
|
||||
auto end = std::end(ip_str);
|
||||
auto temp_end = std::find(begin, end, '.');
|
||||
|
||||
std::uint32_t ip = 0;
|
||||
auto shift = 24;
|
||||
while(temp_end != end) {
|
||||
while (temp_end != end) {
|
||||
ip += (util::from_chars(begin, temp_end) << shift);
|
||||
shift -= 8;
|
||||
|
||||
|
|
@ -36,10 +38,11 @@ std::uint32_t ip(const std::string_view &ip_str) {
|
|||
ip += util::from_chars(begin, end);
|
||||
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
||||
// In the format "xxx.xxx.xxx.xxx/x"
|
||||
std::pair<std::uint32_t, std::uint32_t> ip_block(const std::string_view &ip_str) {
|
||||
// In the format "xxx.xxx.xxx.xxx/x"
|
||||
std::pair<std::uint32_t, std::uint32_t>
|
||||
ip_block(const std::string_view &ip_str) {
|
||||
auto begin = std::begin(ip_str);
|
||||
auto end = std::find(begin, std::end(ip_str), '/');
|
||||
|
||||
|
|
@ -48,38 +51,41 @@ std::pair<std::uint32_t, std::uint32_t> ip_block(const std::string_view &ip_str)
|
|||
auto bits = 32 - util::from_chars(end + 1, std::end(ip_str));
|
||||
|
||||
return { addr, addr + ((1 << bits) - 1) };
|
||||
}
|
||||
}
|
||||
|
||||
net_e from_enum_string(const std::string_view &view) {
|
||||
if(view == "wan") {
|
||||
net_e
|
||||
from_enum_string(const std::string_view &view) {
|
||||
if (view == "wan") {
|
||||
return WAN;
|
||||
}
|
||||
if(view == "lan") {
|
||||
if (view == "lan") {
|
||||
return LAN;
|
||||
}
|
||||
|
||||
return PC;
|
||||
}
|
||||
net_e from_address(const std::string_view &view) {
|
||||
}
|
||||
net_e
|
||||
from_address(const std::string_view &view) {
|
||||
auto addr = ip(view);
|
||||
|
||||
for(auto [ip_low, ip_high] : pc_ips) {
|
||||
if(addr >= ip_low && addr <= ip_high) {
|
||||
for (auto [ip_low, ip_high] : pc_ips) {
|
||||
if (addr >= ip_low && addr <= ip_high) {
|
||||
return PC;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto [ip_low, ip_high] : lan_ips) {
|
||||
if(addr >= ip_low && addr <= ip_high) {
|
||||
for (auto [ip_low, ip_high] : lan_ips) {
|
||||
if (addr >= ip_low && addr <= ip_high) {
|
||||
return LAN;
|
||||
}
|
||||
}
|
||||
|
||||
return WAN;
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view to_enum_string(net_e net) {
|
||||
switch(net) {
|
||||
std::string_view
|
||||
to_enum_string(net_e net) {
|
||||
switch (net) {
|
||||
case PC:
|
||||
return "pc"sv;
|
||||
case LAN:
|
||||
|
|
@ -90,24 +96,26 @@ std::string_view to_enum_string(net_e net) {
|
|||
|
||||
// avoid warning
|
||||
return "wan"sv;
|
||||
}
|
||||
}
|
||||
|
||||
host_t host_create(ENetAddress &addr, std::size_t peers, std::uint16_t port) {
|
||||
host_t
|
||||
host_create(ENetAddress &addr, std::size_t peers, std::uint16_t port) {
|
||||
enet_address_set_host(&addr, "0.0.0.0");
|
||||
enet_address_set_port(&addr, port);
|
||||
|
||||
return host_t { enet_host_create(AF_INET, &addr, peers, 1, 0, 0) };
|
||||
}
|
||||
}
|
||||
|
||||
void free_host(ENetHost *host) {
|
||||
void
|
||||
free_host(ENetHost *host) {
|
||||
std::for_each(host->peers, host->peers + host->peerCount, [](ENetPeer &peer_ref) {
|
||||
ENetPeer *peer = &peer_ref;
|
||||
|
||||
if(peer) {
|
||||
if (peer) {
|
||||
enet_peer_disconnect_now(peer, 0);
|
||||
}
|
||||
});
|
||||
|
||||
enet_host_destroy(host);
|
||||
}
|
||||
}
|
||||
} // namespace net
|
||||
|
|
|
|||
|
|
@ -10,24 +10,29 @@
|
|||
#include "utility.h"
|
||||
|
||||
namespace net {
|
||||
void free_host(ENetHost *host);
|
||||
void
|
||||
free_host(ENetHost *host);
|
||||
|
||||
using host_t = util::safe_ptr<ENetHost, free_host>;
|
||||
using peer_t = ENetPeer *;
|
||||
using packet_t = util::safe_ptr<ENetPacket, enet_packet_destroy>;
|
||||
using host_t = util::safe_ptr<ENetHost, free_host>;
|
||||
using peer_t = ENetPeer *;
|
||||
using packet_t = util::safe_ptr<ENetPacket, enet_packet_destroy>;
|
||||
|
||||
enum net_e : int {
|
||||
enum net_e : int {
|
||||
PC,
|
||||
LAN,
|
||||
WAN
|
||||
};
|
||||
};
|
||||
|
||||
net_e from_enum_string(const std::string_view &view);
|
||||
std::string_view to_enum_string(net_e net);
|
||||
net_e
|
||||
from_enum_string(const std::string_view &view);
|
||||
std::string_view
|
||||
to_enum_string(net_e net);
|
||||
|
||||
net_e from_address(const std::string_view &view);
|
||||
net_e
|
||||
from_address(const std::string_view &view);
|
||||
|
||||
host_t host_create(ENetAddress &addr, std::size_t peers, std::uint16_t port);
|
||||
host_t
|
||||
host_create(ENetAddress &addr, std::size_t peers, std::uint16_t port);
|
||||
} // namespace net
|
||||
|
||||
#endif // SUNSHINE_NETWORK_H
|
||||
|
|
|
|||
351
src/nvhttp.cpp
351
src/nvhttp.cpp
|
|
@ -33,22 +33,23 @@
|
|||
using namespace std::literals;
|
||||
namespace nvhttp {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
namespace fs = std::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
class SunshineHttpsServer : public SimpleWeb::Server<SimpleWeb::HTTPS> {
|
||||
public:
|
||||
SunshineHttpsServer(const std::string &certification_file, const std::string &private_key_file)
|
||||
: SimpleWeb::Server<SimpleWeb::HTTPS>::Server(certification_file, private_key_file) {}
|
||||
class SunshineHttpsServer: public SimpleWeb::Server<SimpleWeb::HTTPS> {
|
||||
public:
|
||||
SunshineHttpsServer(const std::string &certification_file, const std::string &private_key_file):
|
||||
SimpleWeb::Server<SimpleWeb::HTTPS>::Server(certification_file, private_key_file) {}
|
||||
|
||||
std::function<int(SSL *)> verify;
|
||||
std::function<void(std::shared_ptr<Response>, std::shared_ptr<Request>)> on_verify_failed;
|
||||
|
||||
protected:
|
||||
void after_bind() override {
|
||||
protected:
|
||||
void
|
||||
after_bind() override {
|
||||
SimpleWeb::Server<SimpleWeb::HTTPS>::after_bind();
|
||||
|
||||
if(verify) {
|
||||
if (verify) {
|
||||
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once);
|
||||
context.set_verify_callback([](int verified, boost::asio::ssl::verify_context &ctx) {
|
||||
// To respond with an error message, a connection must be established
|
||||
|
|
@ -58,20 +59,21 @@ protected:
|
|||
}
|
||||
|
||||
// This is Server<HTTPS>::accept() with SSL validation support added
|
||||
void accept() override {
|
||||
void
|
||||
accept() override {
|
||||
auto connection = create_connection(*io_service, context);
|
||||
|
||||
acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const SimpleWeb::error_code &ec) {
|
||||
auto lock = connection->handler_runner->continue_lock();
|
||||
if(!lock)
|
||||
if (!lock)
|
||||
return;
|
||||
|
||||
if(ec != SimpleWeb::error::operation_aborted)
|
||||
if (ec != SimpleWeb::error::operation_aborted)
|
||||
this->accept();
|
||||
|
||||
auto session = std::make_shared<Session>(config.max_request_streambuf_size, connection);
|
||||
|
||||
if(!ec) {
|
||||
if (!ec) {
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
SimpleWeb::error_code ec;
|
||||
session->connection->socket->lowest_layer().set_option(option, ec);
|
||||
|
|
@ -80,38 +82,38 @@ protected:
|
|||
session->connection->socket->async_handshake(boost::asio::ssl::stream_base::server, [this, session](const SimpleWeb::error_code &ec) {
|
||||
session->connection->cancel_timeout();
|
||||
auto lock = session->connection->handler_runner->continue_lock();
|
||||
if(!lock)
|
||||
if (!lock)
|
||||
return;
|
||||
if(!ec) {
|
||||
if(verify && !verify(session->connection->socket->native_handle()))
|
||||
if (!ec) {
|
||||
if (verify && !verify(session->connection->socket->native_handle()))
|
||||
this->write(session, on_verify_failed);
|
||||
else
|
||||
this->read(session);
|
||||
}
|
||||
else if(this->on_error)
|
||||
else if (this->on_error)
|
||||
this->on_error(session->request, ec);
|
||||
});
|
||||
}
|
||||
else if(this->on_error)
|
||||
else if (this->on_error)
|
||||
this->on_error(session->request, ec);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using https_server_t = SunshineHttpsServer;
|
||||
using http_server_t = SimpleWeb::Server<SimpleWeb::HTTP>;
|
||||
using https_server_t = SunshineHttpsServer;
|
||||
using http_server_t = SimpleWeb::Server<SimpleWeb::HTTP>;
|
||||
|
||||
struct conf_intern_t {
|
||||
struct conf_intern_t {
|
||||
std::string servercert;
|
||||
std::string pkey;
|
||||
} conf_intern;
|
||||
} conf_intern;
|
||||
|
||||
struct client_t {
|
||||
struct client_t {
|
||||
std::string uniqueID;
|
||||
std::vector<std::string> certs;
|
||||
};
|
||||
};
|
||||
|
||||
struct pair_session_t {
|
||||
struct pair_session_t {
|
||||
struct {
|
||||
std::string uniqueID;
|
||||
std::string cert;
|
||||
|
|
@ -130,39 +132,41 @@ struct pair_session_t {
|
|||
response;
|
||||
std::string salt;
|
||||
} async_insert_pin;
|
||||
};
|
||||
};
|
||||
|
||||
// uniqueID, session
|
||||
std::unordered_map<std::string, pair_session_t> map_id_sess;
|
||||
std::unordered_map<std::string, client_t> map_id_client;
|
||||
// uniqueID, session
|
||||
std::unordered_map<std::string, pair_session_t> map_id_sess;
|
||||
std::unordered_map<std::string, client_t> map_id_client;
|
||||
|
||||
using args_t = SimpleWeb::CaseInsensitiveMultimap;
|
||||
using resp_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>;
|
||||
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
|
||||
using resp_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Response>;
|
||||
using req_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Request>;
|
||||
using args_t = SimpleWeb::CaseInsensitiveMultimap;
|
||||
using resp_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Response>;
|
||||
using req_https_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTPS>::Request>;
|
||||
using resp_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Response>;
|
||||
using req_http_t = std::shared_ptr<typename SimpleWeb::ServerBase<SimpleWeb::HTTP>::Request>;
|
||||
|
||||
enum class op_e {
|
||||
enum class op_e {
|
||||
ADD,
|
||||
REMOVE
|
||||
};
|
||||
};
|
||||
|
||||
std::string get_arg(const args_t &args, const char *name) {
|
||||
std::string
|
||||
get_arg(const args_t &args, const char *name) {
|
||||
auto it = args.find(name);
|
||||
if(it == std::end(args)) {
|
||||
if (it == std::end(args)) {
|
||||
throw std::out_of_range(name);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void save_state() {
|
||||
void
|
||||
save_state() {
|
||||
pt::ptree root;
|
||||
|
||||
if(fs::exists(config::nvhttp.file_state)) {
|
||||
if (fs::exists(config::nvhttp.file_state)) {
|
||||
try {
|
||||
pt::read_json(config::nvhttp.file_state, root);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "Couldn't read "sv << config::nvhttp.file_state << ": "sv << e.what();
|
||||
return;
|
||||
}
|
||||
|
|
@ -172,13 +176,13 @@ void save_state() {
|
|||
|
||||
root.put("root.uniqueid", http::unique_id);
|
||||
auto &nodes = root.add_child("root.devices", pt::ptree {});
|
||||
for(auto &[_, client] : map_id_client) {
|
||||
for (auto &[_, client] : map_id_client) {
|
||||
pt::ptree node;
|
||||
|
||||
node.put("uniqueid"s, client.uniqueID);
|
||||
|
||||
pt::ptree cert_nodes;
|
||||
for(auto &cert : client.certs) {
|
||||
for (auto &cert : client.certs) {
|
||||
pt::ptree cert_node;
|
||||
cert_node.put_value(cert);
|
||||
cert_nodes.push_back(std::make_pair(""s, cert_node));
|
||||
|
|
@ -191,14 +195,15 @@ void save_state() {
|
|||
try {
|
||||
pt::write_json(config::nvhttp.file_state, root);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "Couldn't write "sv << config::nvhttp.file_state << ": "sv << e.what();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void load_state() {
|
||||
if(!fs::exists(config::nvhttp.file_state)) {
|
||||
void
|
||||
load_state() {
|
||||
if (!fs::exists(config::nvhttp.file_state)) {
|
||||
BOOST_LOG(info) << "File "sv << config::nvhttp.file_state << " doesn't exist"sv;
|
||||
http::unique_id = uuid_util::uuid_t::generate().string();
|
||||
return;
|
||||
|
|
@ -208,14 +213,14 @@ void load_state() {
|
|||
try {
|
||||
pt::read_json(config::nvhttp.file_state, root);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << "Couldn't read "sv << config::nvhttp.file_state << ": "sv << e.what();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto unique_id_p = root.get_optional<std::string>("root.uniqueid");
|
||||
if(!unique_id_p) {
|
||||
if (!unique_id_p) {
|
||||
// This file doesn't contain moonlight credentials
|
||||
http::unique_id = uuid_util::uuid_t::generate().string();
|
||||
return;
|
||||
|
|
@ -224,20 +229,21 @@ void load_state() {
|
|||
|
||||
auto device_nodes = root.get_child("root.devices");
|
||||
|
||||
for(auto &[_, device_node] : device_nodes) {
|
||||
for (auto &[_, device_node] : device_nodes) {
|
||||
auto uniqID = device_node.get<std::string>("uniqueid");
|
||||
auto &client = map_id_client.emplace(uniqID, client_t {}).first->second;
|
||||
|
||||
client.uniqueID = uniqID;
|
||||
|
||||
for(auto &[_, el] : device_node.get_child("certs")) {
|
||||
for (auto &[_, el] : device_node.get_child("certs")) {
|
||||
client.certs.emplace_back(el.get_value<std::string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) {
|
||||
switch(op) {
|
||||
void
|
||||
update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) {
|
||||
switch (op) {
|
||||
case op_e::ADD: {
|
||||
auto &client = map_id_client[uniqueID];
|
||||
client.certs.emplace_back(std::move(cert));
|
||||
|
|
@ -248,27 +254,29 @@ void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op)
|
|||
break;
|
||||
}
|
||||
|
||||
if(!config::sunshine.flags[config::flag::FRESH_STATE]) {
|
||||
if (!config::sunshine.flags[config::flag::FRESH_STATE]) {
|
||||
save_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rtsp_stream::launch_session_t make_launch_session(bool host_audio, const args_t &args) {
|
||||
rtsp_stream::launch_session_t
|
||||
make_launch_session(bool host_audio, const args_t &args) {
|
||||
rtsp_stream::launch_session_t launch_session;
|
||||
|
||||
launch_session.host_audio = host_audio;
|
||||
launch_session.gcm_key = util::from_hex<crypto::aes_t>(get_arg(args, "rikey"), true);
|
||||
uint32_t prepend_iv = util::endian::big<uint32_t>(util::from_view(get_arg(args, "rikeyid")));
|
||||
auto prepend_iv_p = (uint8_t *)&prepend_iv;
|
||||
auto prepend_iv_p = (uint8_t *) &prepend_iv;
|
||||
|
||||
auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv));
|
||||
std::fill(next, std::end(launch_session.iv), 0);
|
||||
|
||||
return launch_session;
|
||||
}
|
||||
}
|
||||
|
||||
void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) {
|
||||
if(sess.async_insert_pin.salt.size() < 32) {
|
||||
void
|
||||
getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) {
|
||||
if (sess.async_insert_pin.salt.size() < 32) {
|
||||
tree.put("root.paired", 0);
|
||||
tree.put("root.<xmlattr>.status_code", 400);
|
||||
return;
|
||||
|
|
@ -284,8 +292,9 @@ void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin
|
|||
tree.put("root.paired", 1);
|
||||
tree.put("root.plaincert", util::hex_vec(conf_intern.servercert, true));
|
||||
tree.put("root.<xmlattr>.status_code", 200);
|
||||
}
|
||||
void serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &args) {
|
||||
}
|
||||
void
|
||||
serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &args) {
|
||||
auto encrypted_response = util::from_hex_vec(get_arg(args, "serverchallengeresp"), true);
|
||||
|
||||
std::vector<uint8_t> decrypted;
|
||||
|
|
@ -303,9 +312,10 @@ void serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &ar
|
|||
tree.put("root.pairingsecret", util::hex_vec(serversecret, true));
|
||||
tree.put("root.paired", 1);
|
||||
tree.put("root.<xmlattr>.status_code", 200);
|
||||
}
|
||||
}
|
||||
|
||||
void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args) {
|
||||
void
|
||||
clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args) {
|
||||
auto challenge = util::from_hex_vec(get_arg(args, "clientchallenge"), true);
|
||||
|
||||
crypto::cipher::ecb_t cipher(*sess.cipher_key, false);
|
||||
|
|
@ -320,7 +330,7 @@ void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args)
|
|||
decrypted.insert(std::end(decrypted), std::begin(sign), std::end(sign));
|
||||
decrypted.insert(std::end(decrypted), std::begin(serversecret), std::end(serversecret));
|
||||
|
||||
auto hash = crypto::hash({ (char *)decrypted.data(), decrypted.size() });
|
||||
auto hash = crypto::hash({ (char *) decrypted.data(), decrypted.size() });
|
||||
auto serverchallenge = crypto::rand(16);
|
||||
|
||||
std::string plaintext;
|
||||
|
|
@ -338,9 +348,10 @@ void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args)
|
|||
tree.put("root.paired", 1);
|
||||
tree.put("root.challengeresponse", util::hex_vec(encrypted, true));
|
||||
tree.put("root.<xmlattr>.status_code", 200);
|
||||
}
|
||||
}
|
||||
|
||||
void clientpairingsecret(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, pair_session_t &sess, pt::ptree &tree, const args_t &args) {
|
||||
void
|
||||
clientpairingsecret(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, pair_session_t &sess, pt::ptree &tree, const args_t &args) {
|
||||
auto &client = sess.client;
|
||||
|
||||
auto pairingsecret = util::from_hex_vec(get_arg(args, "clientpairingsecret"), true);
|
||||
|
|
@ -363,14 +374,14 @@ void clientpairingsecret(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cer
|
|||
auto hash = crypto::hash(data);
|
||||
|
||||
// if hash not correct, probably MITM
|
||||
if(std::memcmp(hash.data(), sess.clienthash.data(), hash.size())) {
|
||||
if (std::memcmp(hash.data(), sess.clienthash.data(), hash.size())) {
|
||||
// TODO: log
|
||||
|
||||
map_id_sess.erase(client.uniqueID);
|
||||
tree.put("root.paired", 0);
|
||||
}
|
||||
|
||||
if(crypto::verify256(crypto::x509(client.cert), secret, sign)) {
|
||||
if (crypto::verify256(crypto::x509(client.cert), secret, sign)) {
|
||||
tree.put("root.paired", 1);
|
||||
add_cert->raise(crypto::x509(client.cert));
|
||||
|
||||
|
|
@ -385,43 +396,45 @@ void clientpairingsecret(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cer
|
|||
}
|
||||
|
||||
tree.put("root.<xmlattr>.status_code", 200);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
struct tunnel;
|
||||
template <class T>
|
||||
struct tunnel;
|
||||
|
||||
template<>
|
||||
struct tunnel<SimpleWeb::HTTPS> {
|
||||
template <>
|
||||
struct tunnel<SimpleWeb::HTTPS> {
|
||||
static auto constexpr to_string = "HTTPS"sv;
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
struct tunnel<SimpleWeb::HTTP> {
|
||||
template <>
|
||||
struct tunnel<SimpleWeb::HTTP> {
|
||||
static auto constexpr to_string = "NONE"sv;
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
void print_req(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
template <class T>
|
||||
void
|
||||
print_req(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
BOOST_LOG(debug) << "TUNNEL :: "sv << tunnel<T>::to_string;
|
||||
|
||||
BOOST_LOG(debug) << "METHOD :: "sv << request->method;
|
||||
BOOST_LOG(debug) << "DESTINATION :: "sv << request->path;
|
||||
|
||||
for(auto &[name, val] : request->header) {
|
||||
for (auto &[name, val] : request->header) {
|
||||
BOOST_LOG(debug) << name << " -- " << val;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << " [--] "sv;
|
||||
|
||||
for(auto &[name, val] : request->parse_query_string()) {
|
||||
for (auto &[name, val] : request->parse_query_string()) {
|
||||
BOOST_LOG(debug) << name << " -- " << val;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << " [--] "sv;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void not_found(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
template <class T>
|
||||
void
|
||||
not_found(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
print_req<T>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
|
|
@ -437,10 +450,11 @@ void not_found(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> resp
|
|||
<< data.str();
|
||||
|
||||
response->close_connection_after_response = true;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
template <class T>
|
||||
void
|
||||
pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
print_req<T>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
|
|
@ -454,7 +468,7 @@ void pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_
|
|||
});
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
if(args.find("uniqueid"s) == std::end(args)) {
|
||||
if (args.find("uniqueid"s) == std::end(args)) {
|
||||
tree.put("root.<xmlattr>.status_code", 400);
|
||||
|
||||
return;
|
||||
|
|
@ -464,8 +478,8 @@ void pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_
|
|||
auto sess_it = map_id_sess.find(uniqID);
|
||||
|
||||
args_t::const_iterator it;
|
||||
if(it = args.find("phrase"); it != std::end(args)) {
|
||||
if(it->second == "getservercert"sv) {
|
||||
if (it = args.find("phrase"); it != std::end(args)) {
|
||||
if (it->second == "getservercert"sv) {
|
||||
pair_session_t sess;
|
||||
|
||||
sess.client.uniqueID = std::move(uniqID);
|
||||
|
|
@ -476,7 +490,7 @@ void pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_
|
|||
|
||||
ptr->second.async_insert_pin.salt = std::move(get_arg(args, "salt"));
|
||||
|
||||
if(config::sunshine.flags[config::flag::PIN_STDIN]) {
|
||||
if (config::sunshine.flags[config::flag::PIN_STDIN]) {
|
||||
std::string pin;
|
||||
|
||||
std::cout << "Please insert pin: "sv;
|
||||
|
|
@ -491,26 +505,26 @@ void pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if(it->second == "pairchallenge"sv) {
|
||||
else if (it->second == "pairchallenge"sv) {
|
||||
tree.put("root.paired", 1);
|
||||
tree.put("root.<xmlattr>.status_code", 200);
|
||||
}
|
||||
}
|
||||
else if(it = args.find("clientchallenge"); it != std::end(args)) {
|
||||
else if (it = args.find("clientchallenge"); it != std::end(args)) {
|
||||
clientchallenge(sess_it->second, tree, args);
|
||||
}
|
||||
else if(it = args.find("serverchallengeresp"); it != std::end(args)) {
|
||||
else if (it = args.find("serverchallengeresp"); it != std::end(args)) {
|
||||
serverchallengeresp(sess_it->second, tree, args);
|
||||
}
|
||||
else if(it = args.find("clientpairingsecret"); it != std::end(args)) {
|
||||
else if (it = args.find("clientpairingsecret"); it != std::end(args)) {
|
||||
clientpairingsecret(add_cert, sess_it->second, tree, args);
|
||||
}
|
||||
else {
|
||||
tree.put("root.<xmlattr>.status_code", 404);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Compare the user supplied pin to the Moonlight pin.
|
||||
* @param pin The user supplied pin.
|
||||
* @return `true` if the pin is correct, `false` otherwise.
|
||||
|
|
@ -520,9 +534,10 @@ void pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_
|
|||
* bool pin_status = nvhttp::pin("1234");
|
||||
* ```
|
||||
*/
|
||||
bool pin(std::string pin) {
|
||||
bool
|
||||
pin(std::string pin) {
|
||||
pt::ptree tree;
|
||||
if(map_id_sess.empty()) {
|
||||
if (map_id_sess.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -534,10 +549,10 @@ bool pin(std::string pin) {
|
|||
pt::write_xml(data, tree);
|
||||
|
||||
auto &async_response = sess.async_insert_pin.response;
|
||||
if(async_response.has_left() && async_response.left()) {
|
||||
if (async_response.has_left() && async_response.left()) {
|
||||
async_response.left()->write(data.str());
|
||||
}
|
||||
else if(async_response.has_right() && async_response.right()) {
|
||||
else if (async_response.has_right() && async_response.right()) {
|
||||
async_response.right()->write(data.str());
|
||||
}
|
||||
else {
|
||||
|
|
@ -548,17 +563,18 @@ bool pin(std::string pin) {
|
|||
async_response = std::decay_t<decltype(async_response.left())>();
|
||||
// response to the current request
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void pin(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
template <class T>
|
||||
void
|
||||
pin(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
print_req<T>(request);
|
||||
|
||||
response->close_connection_after_response = true;
|
||||
|
||||
auto address = request->remote_endpoint().address().to_string();
|
||||
auto ip_type = net::from_address(address);
|
||||
if(ip_type > http::origin_pin_allowed) {
|
||||
if (ip_type > http::origin_pin_allowed) {
|
||||
BOOST_LOG(info) << "/pin: ["sv << address << "] -- denied"sv;
|
||||
|
||||
response->write(SimpleWeb::StatusCode::client_error_forbidden);
|
||||
|
|
@ -567,26 +583,26 @@ void pin(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response,
|
|||
}
|
||||
|
||||
bool pinResponse = pin(request->path_match[1]);
|
||||
if(pinResponse) {
|
||||
if (pinResponse) {
|
||||
response->write(SimpleWeb::StatusCode::success_ok);
|
||||
}
|
||||
else {
|
||||
response->write(SimpleWeb::StatusCode::client_error_im_a_teapot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
template <class T>
|
||||
void
|
||||
serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> response, std::shared_ptr<typename SimpleWeb::ServerBase<T>::Request> request) {
|
||||
print_req<T>(request);
|
||||
|
||||
int pair_status = 0;
|
||||
if constexpr(std::is_same_v<SimpleWeb::HTTPS, T>) {
|
||||
if constexpr (std::is_same_v<SimpleWeb::HTTPS, T>) {
|
||||
auto args = request->parse_query_string();
|
||||
auto clientID = args.find("uniqueid"s);
|
||||
|
||||
|
||||
if(clientID != std::end(args)) {
|
||||
if(auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) {
|
||||
if (clientID != std::end(args)) {
|
||||
if (auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) {
|
||||
pair_status = 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -608,33 +624,33 @@ void serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> res
|
|||
tree.put("root.MaxLumaPixelsHEVC", config::video.hevc_mode > 1 ? "1869449984" : "0");
|
||||
tree.put("root.LocalIP", local_endpoint.address().to_string());
|
||||
|
||||
if(config::video.hevc_mode == 3) {
|
||||
if (config::video.hevc_mode == 3) {
|
||||
tree.put("root.ServerCodecModeSupport", "3843");
|
||||
}
|
||||
else if(config::video.hevc_mode == 2) {
|
||||
else if (config::video.hevc_mode == 2) {
|
||||
tree.put("root.ServerCodecModeSupport", "259");
|
||||
}
|
||||
else {
|
||||
tree.put("root.ServerCodecModeSupport", "3");
|
||||
}
|
||||
|
||||
if(!config::nvhttp.external_ip.empty()) {
|
||||
if (!config::nvhttp.external_ip.empty()) {
|
||||
tree.put("root.ExternalIP", config::nvhttp.external_ip);
|
||||
}
|
||||
|
||||
pt::ptree display_nodes;
|
||||
for(auto &resolution : config::nvhttp.resolutions) {
|
||||
for (auto &resolution : config::nvhttp.resolutions) {
|
||||
auto pred = [](auto ch) { return ch == ' ' || ch == '\t' || ch == 'x'; };
|
||||
|
||||
auto middle = std::find_if(std::begin(resolution), std::end(resolution), pred);
|
||||
if(middle == std::end(resolution)) {
|
||||
if (middle == std::end(resolution)) {
|
||||
BOOST_LOG(warning) << resolution << " is not in the proper format for a resolution: WIDTHxHEIGHT"sv;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto width = util::from_chars(&*std::begin(resolution), &*middle);
|
||||
auto height = util::from_chars(&*(middle + 1), &*std::end(resolution));
|
||||
for(auto fps : config::nvhttp.fps) {
|
||||
for (auto fps : config::nvhttp.fps) {
|
||||
pt::ptree display_node;
|
||||
display_node.put("Width", width);
|
||||
display_node.put("Height", height);
|
||||
|
|
@ -644,7 +660,7 @@ void serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> res
|
|||
}
|
||||
}
|
||||
|
||||
if(!config::nvhttp.resolutions.empty()) {
|
||||
if (!config::nvhttp.resolutions.empty()) {
|
||||
tree.add_child("root.SupportedDisplayMode", display_nodes);
|
||||
}
|
||||
auto current_appid = proc::proc.running();
|
||||
|
|
@ -657,9 +673,10 @@ void serverinfo(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> res
|
|||
pt::write_xml(data, tree);
|
||||
response->write(data.str());
|
||||
response->close_connection_after_response = true;
|
||||
}
|
||||
}
|
||||
|
||||
void applist(resp_https_t response, req_https_t request) {
|
||||
void
|
||||
applist(resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
|
|
@ -673,7 +690,7 @@ void applist(resp_https_t response, req_https_t request) {
|
|||
});
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
if(args.find("uniqueid"s) == std::end(args)) {
|
||||
if (args.find("uniqueid"s) == std::end(args)) {
|
||||
tree.put("root.<xmlattr>.status_code", 400);
|
||||
|
||||
return;
|
||||
|
|
@ -682,7 +699,7 @@ void applist(resp_https_t response, req_https_t request) {
|
|||
auto clientID = get_arg(args, "uniqueid");
|
||||
|
||||
auto client = map_id_client.find(clientID);
|
||||
if(client == std::end(map_id_client)) {
|
||||
if (client == std::end(map_id_client)) {
|
||||
tree.put("root.<xmlattr>.status_code", 501);
|
||||
|
||||
return;
|
||||
|
|
@ -692,7 +709,7 @@ void applist(resp_https_t response, req_https_t request) {
|
|||
|
||||
apps.put("<xmlattr>.status_code", 200);
|
||||
|
||||
for(auto &proc : proc::proc.get_apps()) {
|
||||
for (auto &proc : proc::proc.get_apps()) {
|
||||
pt::ptree app;
|
||||
|
||||
app.put("IsHdrSupported"s, config::video.hevc_mode == 3 ? 1 : 0);
|
||||
|
|
@ -701,9 +718,10 @@ void applist(resp_https_t response, req_https_t request) {
|
|||
|
||||
apps.push_back(std::make_pair("App", std::move(app)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void launch(bool &host_audio, resp_https_t response, req_https_t request) {
|
||||
void
|
||||
launch(bool &host_audio, resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
|
|
@ -715,7 +733,7 @@ void launch(bool &host_audio, resp_https_t response, req_https_t request) {
|
|||
response->close_connection_after_response = true;
|
||||
});
|
||||
|
||||
if(rtsp_stream::session_count() == config::stream.channels) {
|
||||
if (rtsp_stream::session_count() == config::stream.channels) {
|
||||
tree.put("root.resume", 0);
|
||||
tree.put("root.<xmlattr>.status_code", 503);
|
||||
|
||||
|
|
@ -723,12 +741,11 @@ void launch(bool &host_audio, resp_https_t response, req_https_t request) {
|
|||
}
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
if(
|
||||
if (
|
||||
args.find("rikey"s) == std::end(args) ||
|
||||
args.find("rikeyid"s) == std::end(args) ||
|
||||
args.find("localAudioPlayMode"s) == std::end(args) ||
|
||||
args.find("appid"s) == std::end(args)) {
|
||||
|
||||
tree.put("root.resume", 0);
|
||||
tree.put("root.<xmlattr>.status_code", 400);
|
||||
|
||||
|
|
@ -738,16 +755,16 @@ void launch(bool &host_audio, resp_https_t response, req_https_t request) {
|
|||
auto appid = util::from_view(get_arg(args, "appid"));
|
||||
|
||||
auto current_appid = proc::proc.running();
|
||||
if(current_appid > 0) {
|
||||
if (current_appid > 0) {
|
||||
tree.put("root.resume", 0);
|
||||
tree.put("root.<xmlattr>.status_code", 400);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(appid > 0) {
|
||||
if (appid > 0) {
|
||||
auto err = proc::proc.execute(appid);
|
||||
if(err) {
|
||||
if (err) {
|
||||
tree.put("root.<xmlattr>.status_code", err);
|
||||
tree.put("root.gamesession", 0);
|
||||
|
||||
|
|
@ -761,9 +778,10 @@ void launch(bool &host_audio, resp_https_t response, req_https_t request) {
|
|||
tree.put("root.<xmlattr>.status_code", 200);
|
||||
tree.put("root.sessionUrl0", "rtsp://"s + request->local_endpoint().address().to_string() + ':' + std::to_string(map_port(rtsp_stream::RTSP_SETUP_PORT)));
|
||||
tree.put("root.gamesession", 1);
|
||||
}
|
||||
}
|
||||
|
||||
void resume(bool &host_audio, resp_https_t response, req_https_t request) {
|
||||
void
|
||||
resume(bool &host_audio, resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
|
|
@ -777,7 +795,7 @@ void resume(bool &host_audio, resp_https_t response, req_https_t request) {
|
|||
|
||||
// It is possible that due a race condition that this if-statement gives a false negative,
|
||||
// that is automatically resolved in rtsp_server_t
|
||||
if(rtsp_stream::session_count() == config::stream.channels) {
|
||||
if (rtsp_stream::session_count() == config::stream.channels) {
|
||||
tree.put("root.resume", 0);
|
||||
tree.put("root.<xmlattr>.status_code", 503);
|
||||
|
||||
|
|
@ -785,7 +803,7 @@ void resume(bool &host_audio, resp_https_t response, req_https_t request) {
|
|||
}
|
||||
|
||||
auto current_appid = proc::proc.running();
|
||||
if(current_appid == 0) {
|
||||
if (current_appid == 0) {
|
||||
tree.put("root.resume", 0);
|
||||
tree.put("root.<xmlattr>.status_code", 503);
|
||||
|
||||
|
|
@ -793,10 +811,9 @@ void resume(bool &host_audio, resp_https_t response, req_https_t request) {
|
|||
}
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
if(
|
||||
if (
|
||||
args.find("rikey"s) == std::end(args) ||
|
||||
args.find("rikeyid"s) == std::end(args)) {
|
||||
|
||||
tree.put("root.resume", 0);
|
||||
tree.put("root.<xmlattr>.status_code", 400);
|
||||
|
||||
|
|
@ -808,9 +825,10 @@ void resume(bool &host_audio, resp_https_t response, req_https_t request) {
|
|||
tree.put("root.<xmlattr>.status_code", 200);
|
||||
tree.put("root.sessionUrl0", "rtsp://"s + request->local_endpoint().address().to_string() + ':' + std::to_string(map_port(rtsp_stream::RTSP_SETUP_PORT)));
|
||||
tree.put("root.resume", 1);
|
||||
}
|
||||
}
|
||||
|
||||
void cancel(resp_https_t response, req_https_t request) {
|
||||
void
|
||||
cancel(resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
|
||||
pt::ptree tree;
|
||||
|
|
@ -824,7 +842,7 @@ void cancel(resp_https_t response, req_https_t request) {
|
|||
|
||||
// It is possible that due a race condition that this if-statement gives a false positive,
|
||||
// the client should try again
|
||||
if(rtsp_stream::session_count() != 0) {
|
||||
if (rtsp_stream::session_count() != 0) {
|
||||
tree.put("root.resume", 0);
|
||||
tree.put("root.<xmlattr>.status_code", 503);
|
||||
|
||||
|
|
@ -834,13 +852,13 @@ void cancel(resp_https_t response, req_https_t request) {
|
|||
tree.put("root.cancel", 1);
|
||||
tree.put("root.<xmlattr>.status_code", 200);
|
||||
|
||||
if(proc::proc.running() > 0) {
|
||||
if (proc::proc.running() > 0) {
|
||||
proc::proc.terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void appasset(resp_https_t response, req_https_t request) {
|
||||
void
|
||||
appasset(resp_https_t response, req_https_t request) {
|
||||
print_req<SimpleWeb::HTTPS>(request);
|
||||
|
||||
auto args = request->parse_query_string();
|
||||
|
|
@ -851,9 +869,9 @@ void appasset(resp_https_t response, req_https_t request) {
|
|||
headers.emplace("Content-Type", "image/png");
|
||||
response->write(SimpleWeb::StatusCode::success_ok, in, headers);
|
||||
response->close_connection_after_response = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Start the nvhttp server.
|
||||
*
|
||||
* EXAMPLES:
|
||||
|
|
@ -861,7 +879,8 @@ void appasset(resp_https_t response, req_https_t request) {
|
|||
* nvhttp::start();
|
||||
* ```
|
||||
*/
|
||||
void start() {
|
||||
void
|
||||
start() {
|
||||
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
|
||||
|
||||
auto port_http = map_port(PORT_HTTP);
|
||||
|
|
@ -869,7 +888,7 @@ void start() {
|
|||
|
||||
bool clean_slate = config::sunshine.flags[config::flag::FRESH_STATE];
|
||||
|
||||
if(!clean_slate) {
|
||||
if (!clean_slate) {
|
||||
load_state();
|
||||
}
|
||||
|
||||
|
|
@ -877,8 +896,8 @@ void start() {
|
|||
conf_intern.servercert = read_file(config::nvhttp.cert.c_str());
|
||||
|
||||
crypto::cert_chain_t cert_chain;
|
||||
for(auto &[_, client] : map_id_client) {
|
||||
for(auto &cert : client.certs) {
|
||||
for (auto &[_, client] : map_id_client) {
|
||||
for (auto &cert : client.certs) {
|
||||
cert_chain.add(crypto::x509(cert));
|
||||
}
|
||||
}
|
||||
|
|
@ -895,7 +914,7 @@ void start() {
|
|||
// Verify certificates after establishing connection
|
||||
https_server.verify = [&cert_chain, add_cert](SSL *ssl) {
|
||||
auto x509 = SSL_get_peer_certificate(ssl);
|
||||
if(!x509) {
|
||||
if (!x509) {
|
||||
BOOST_LOG(info) << "unknown -- denied"sv;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -905,13 +924,12 @@ void start() {
|
|||
auto fg = util::fail_guard([&]() {
|
||||
char subject_name[256];
|
||||
|
||||
|
||||
X509_NAME_oneline(X509_get_subject_name(x509), subject_name, sizeof(subject_name));
|
||||
|
||||
BOOST_LOG(debug) << subject_name << " -- "sv << (verified ? "verified"sv : "denied"sv);
|
||||
});
|
||||
|
||||
while(add_cert->peek()) {
|
||||
while (add_cert->peek()) {
|
||||
char subject_name[256];
|
||||
|
||||
auto cert = add_cert->pop();
|
||||
|
|
@ -922,7 +940,7 @@ void start() {
|
|||
}
|
||||
|
||||
auto err_str = cert_chain.verify(x509);
|
||||
if(err_str) {
|
||||
if (err_str) {
|
||||
BOOST_LOG(warning) << "SSL Verification error :: "sv << err_str;
|
||||
|
||||
return verified;
|
||||
|
|
@ -975,9 +993,9 @@ void start() {
|
|||
try {
|
||||
http_server->start();
|
||||
}
|
||||
catch(boost::system::system_error &err) {
|
||||
catch (boost::system::system_error &err) {
|
||||
// It's possible the exception gets thrown after calling http_server->stop() from a different thread
|
||||
if(shutdown_event->peek()) {
|
||||
if (shutdown_event->peek()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -997,9 +1015,9 @@ void start() {
|
|||
|
||||
ssl.join();
|
||||
tcp.join();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Remove all paired clients.
|
||||
*
|
||||
* EXAMPLES:
|
||||
|
|
@ -1007,8 +1025,9 @@ void start() {
|
|||
* nvhttp::erase_all_clients();
|
||||
* ```
|
||||
*/
|
||||
void erase_all_clients() {
|
||||
void
|
||||
erase_all_clients() {
|
||||
map_id_client.clear();
|
||||
save_state();
|
||||
}
|
||||
}
|
||||
} // namespace nvhttp
|
||||
|
|
|
|||
29
src/nvhttp.h
29
src/nvhttp.h
|
|
@ -17,31 +17,34 @@
|
|||
*/
|
||||
namespace nvhttp {
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief The protocol version.
|
||||
*/
|
||||
constexpr auto VERSION = "7.1.431.-1";
|
||||
// The negative 4th version number tells Moonlight that this is Sunshine
|
||||
constexpr auto VERSION = "7.1.431.-1";
|
||||
// The negative 4th version number tells Moonlight that this is Sunshine
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief The GFE version we are replicating.
|
||||
*/
|
||||
constexpr auto GFE_VERSION = "3.23.0.74";
|
||||
constexpr auto GFE_VERSION = "3.23.0.74";
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief The HTTP port, as a difference from the config port.
|
||||
*/
|
||||
constexpr auto PORT_HTTP = 0;
|
||||
constexpr auto PORT_HTTP = 0;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief The HTTPS port, as a difference from the config port.
|
||||
*/
|
||||
constexpr auto PORT_HTTPS = -5;
|
||||
constexpr auto PORT_HTTPS = -5;
|
||||
|
||||
// functions
|
||||
void start();
|
||||
bool pin(std::string pin);
|
||||
void erase_all_clients();
|
||||
// functions
|
||||
void
|
||||
start();
|
||||
bool
|
||||
pin(std::string pin);
|
||||
void
|
||||
erase_all_clients();
|
||||
} // namespace nvhttp
|
||||
|
||||
#endif // SUNSHINE_NVHTTP_H
|
||||
|
|
|
|||
|
|
@ -27,59 +27,59 @@ struct AVHWFramesContext;
|
|||
// Forward declarations of boost classes to avoid having to include boost headers
|
||||
// here, which results in issues with Windows.h and WinSock2.h include order.
|
||||
namespace boost {
|
||||
namespace asio {
|
||||
namespace ip {
|
||||
class address;
|
||||
} // namespace ip
|
||||
} // namespace asio
|
||||
namespace filesystem {
|
||||
class path;
|
||||
}
|
||||
namespace process {
|
||||
class child;
|
||||
class group;
|
||||
template<typename Char>
|
||||
class basic_environment;
|
||||
typedef basic_environment<char> environment;
|
||||
} // namespace process
|
||||
namespace asio {
|
||||
namespace ip {
|
||||
class address;
|
||||
} // namespace ip
|
||||
} // namespace asio
|
||||
namespace filesystem {
|
||||
class path;
|
||||
}
|
||||
namespace process {
|
||||
class child;
|
||||
class group;
|
||||
template <typename Char>
|
||||
class basic_environment;
|
||||
typedef basic_environment<char> environment;
|
||||
} // namespace process
|
||||
} // namespace boost
|
||||
namespace video {
|
||||
struct config_t;
|
||||
struct config_t;
|
||||
} // namespace video
|
||||
|
||||
namespace platf {
|
||||
constexpr auto MAX_GAMEPADS = 32;
|
||||
constexpr auto MAX_GAMEPADS = 32;
|
||||
|
||||
constexpr std::uint16_t DPAD_UP = 0x0001;
|
||||
constexpr std::uint16_t DPAD_DOWN = 0x0002;
|
||||
constexpr std::uint16_t DPAD_LEFT = 0x0004;
|
||||
constexpr std::uint16_t DPAD_RIGHT = 0x0008;
|
||||
constexpr std::uint16_t START = 0x0010;
|
||||
constexpr std::uint16_t BACK = 0x0020;
|
||||
constexpr std::uint16_t LEFT_STICK = 0x0040;
|
||||
constexpr std::uint16_t RIGHT_STICK = 0x0080;
|
||||
constexpr std::uint16_t LEFT_BUTTON = 0x0100;
|
||||
constexpr std::uint16_t RIGHT_BUTTON = 0x0200;
|
||||
constexpr std::uint16_t HOME = 0x0400;
|
||||
constexpr std::uint16_t A = 0x1000;
|
||||
constexpr std::uint16_t B = 0x2000;
|
||||
constexpr std::uint16_t X = 0x4000;
|
||||
constexpr std::uint16_t Y = 0x8000;
|
||||
constexpr std::uint16_t DPAD_UP = 0x0001;
|
||||
constexpr std::uint16_t DPAD_DOWN = 0x0002;
|
||||
constexpr std::uint16_t DPAD_LEFT = 0x0004;
|
||||
constexpr std::uint16_t DPAD_RIGHT = 0x0008;
|
||||
constexpr std::uint16_t START = 0x0010;
|
||||
constexpr std::uint16_t BACK = 0x0020;
|
||||
constexpr std::uint16_t LEFT_STICK = 0x0040;
|
||||
constexpr std::uint16_t RIGHT_STICK = 0x0080;
|
||||
constexpr std::uint16_t LEFT_BUTTON = 0x0100;
|
||||
constexpr std::uint16_t RIGHT_BUTTON = 0x0200;
|
||||
constexpr std::uint16_t HOME = 0x0400;
|
||||
constexpr std::uint16_t A = 0x1000;
|
||||
constexpr std::uint16_t B = 0x2000;
|
||||
constexpr std::uint16_t X = 0x4000;
|
||||
constexpr std::uint16_t Y = 0x8000;
|
||||
|
||||
struct rumble_t {
|
||||
struct rumble_t {
|
||||
KITTY_DEFAULT_CONSTR(rumble_t)
|
||||
|
||||
rumble_t(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq)
|
||||
: id { id }, lowfreq { lowfreq }, highfreq { highfreq } {}
|
||||
rumble_t(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq):
|
||||
id { id }, lowfreq { lowfreq }, highfreq { highfreq } {}
|
||||
|
||||
std::uint16_t id;
|
||||
std::uint16_t lowfreq;
|
||||
std::uint16_t highfreq;
|
||||
};
|
||||
using rumble_queue_t = safe::mail_raw_t::queue_t<rumble_t>;
|
||||
};
|
||||
using rumble_queue_t = safe::mail_raw_t::queue_t<rumble_t>;
|
||||
|
||||
namespace speaker {
|
||||
enum speaker_e {
|
||||
namespace speaker {
|
||||
enum speaker_e {
|
||||
FRONT_LEFT,
|
||||
FRONT_RIGHT,
|
||||
FRONT_CENTER,
|
||||
|
|
@ -89,20 +89,20 @@ enum speaker_e {
|
|||
SIDE_LEFT,
|
||||
SIDE_RIGHT,
|
||||
MAX_SPEAKERS,
|
||||
};
|
||||
};
|
||||
|
||||
constexpr std::uint8_t map_stereo[] {
|
||||
constexpr std::uint8_t map_stereo[] {
|
||||
FRONT_LEFT, FRONT_RIGHT
|
||||
};
|
||||
constexpr std::uint8_t map_surround51[] {
|
||||
};
|
||||
constexpr std::uint8_t map_surround51[] {
|
||||
FRONT_LEFT,
|
||||
FRONT_RIGHT,
|
||||
FRONT_CENTER,
|
||||
LOW_FREQUENCY,
|
||||
BACK_LEFT,
|
||||
BACK_RIGHT,
|
||||
};
|
||||
constexpr std::uint8_t map_surround71[] {
|
||||
};
|
||||
constexpr std::uint8_t map_surround71[] {
|
||||
FRONT_LEFT,
|
||||
FRONT_RIGHT,
|
||||
FRONT_CENTER,
|
||||
|
|
@ -111,31 +111,32 @@ constexpr std::uint8_t map_surround71[] {
|
|||
BACK_RIGHT,
|
||||
SIDE_LEFT,
|
||||
SIDE_RIGHT,
|
||||
};
|
||||
} // namespace speaker
|
||||
};
|
||||
} // namespace speaker
|
||||
|
||||
enum class mem_type_e {
|
||||
enum class mem_type_e {
|
||||
system,
|
||||
vaapi,
|
||||
dxgi,
|
||||
cuda,
|
||||
unknown
|
||||
};
|
||||
};
|
||||
|
||||
enum class pix_fmt_e {
|
||||
enum class pix_fmt_e {
|
||||
yuv420p,
|
||||
yuv420p10,
|
||||
nv12,
|
||||
p010,
|
||||
unknown
|
||||
};
|
||||
};
|
||||
|
||||
inline std::string_view from_pix_fmt(pix_fmt_e pix_fmt) {
|
||||
inline std::string_view
|
||||
from_pix_fmt(pix_fmt_e pix_fmt) {
|
||||
using namespace std::literals;
|
||||
#define _CONVERT(x) \
|
||||
case pix_fmt_e::x: \
|
||||
return #x##sv
|
||||
switch(pix_fmt) {
|
||||
switch (pix_fmt) {
|
||||
_CONVERT(yuv420p);
|
||||
_CONVERT(yuv420p10);
|
||||
_CONVERT(nv12);
|
||||
|
|
@ -145,15 +146,15 @@ inline std::string_view from_pix_fmt(pix_fmt_e pix_fmt) {
|
|||
#undef _CONVERT
|
||||
|
||||
return "unknown"sv;
|
||||
}
|
||||
}
|
||||
|
||||
// Dimensions for touchscreen input
|
||||
struct touch_port_t {
|
||||
// Dimensions for touchscreen input
|
||||
struct touch_port_t {
|
||||
int offset_x, offset_y;
|
||||
int width, height;
|
||||
};
|
||||
};
|
||||
|
||||
struct gamepad_state_t {
|
||||
struct gamepad_state_t {
|
||||
std::uint16_t buttonFlags;
|
||||
std::uint8_t lt;
|
||||
std::uint8_t rt;
|
||||
|
|
@ -161,21 +162,23 @@ struct gamepad_state_t {
|
|||
std::int16_t lsY;
|
||||
std::int16_t rsX;
|
||||
std::int16_t rsY;
|
||||
};
|
||||
};
|
||||
|
||||
class deinit_t {
|
||||
public:
|
||||
class deinit_t {
|
||||
public:
|
||||
virtual ~deinit_t() = default;
|
||||
};
|
||||
};
|
||||
|
||||
struct img_t {
|
||||
public:
|
||||
struct img_t {
|
||||
public:
|
||||
img_t() = default;
|
||||
|
||||
img_t(img_t &&) = delete;
|
||||
img_t(const img_t &) = delete;
|
||||
img_t &operator=(img_t &&) = delete;
|
||||
img_t &operator=(const img_t &) = delete;
|
||||
img_t &
|
||||
operator=(img_t &&) = delete;
|
||||
img_t &
|
||||
operator=(const img_t &) = delete;
|
||||
|
||||
std::uint8_t *data {};
|
||||
std::int32_t width {};
|
||||
|
|
@ -184,9 +187,9 @@ public:
|
|||
std::int32_t row_pitch {};
|
||||
|
||||
virtual ~img_t() = default;
|
||||
};
|
||||
};
|
||||
|
||||
struct sink_t {
|
||||
struct sink_t {
|
||||
// Play on host PC
|
||||
std::string host;
|
||||
|
||||
|
|
@ -198,50 +201,55 @@ struct sink_t {
|
|||
std::string surround71;
|
||||
};
|
||||
std::optional<null_t> null;
|
||||
};
|
||||
};
|
||||
|
||||
struct hwdevice_t {
|
||||
struct hwdevice_t {
|
||||
void *data {};
|
||||
AVFrame *frame {};
|
||||
|
||||
virtual int convert(platf::img_t &img) {
|
||||
virtual int
|
||||
convert(platf::img_t &img) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* implementations must take ownership of 'frame'
|
||||
*/
|
||||
virtual int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
||||
virtual int
|
||||
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
||||
BOOST_LOG(error) << "Illegal call to hwdevice_t::set_frame(). Did you forget to override it?";
|
||||
return -1;
|
||||
};
|
||||
|
||||
virtual void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {};
|
||||
virtual void
|
||||
set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {};
|
||||
|
||||
/**
|
||||
* Implementations may set parameters during initialization of the hwframes context
|
||||
*/
|
||||
virtual void init_hwframes(AVHWFramesContext *frames) {};
|
||||
virtual void
|
||||
init_hwframes(AVHWFramesContext *frames) {};
|
||||
|
||||
/**
|
||||
* Implementations may make modifications required before context derivation
|
||||
*/
|
||||
virtual int prepare_to_derive_context(int hw_device_type) {
|
||||
virtual int
|
||||
prepare_to_derive_context(int hw_device_type) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
virtual ~hwdevice_t() = default;
|
||||
};
|
||||
};
|
||||
|
||||
enum class capture_e : int {
|
||||
enum class capture_e : int {
|
||||
ok,
|
||||
reinit,
|
||||
timeout,
|
||||
error
|
||||
};
|
||||
};
|
||||
|
||||
class display_t {
|
||||
public:
|
||||
class display_t {
|
||||
public:
|
||||
/**
|
||||
* When display has a new image ready or a timeout occurs, this callback will be called with the image.
|
||||
* If a frame was captured, frame_captured will be true. If a timeout occurred, it will be false.
|
||||
|
|
@ -255,7 +263,8 @@ public:
|
|||
*/
|
||||
using snapshot_cb_t = std::function<std::shared_ptr<img_t>(std::shared_ptr<img_t> &img, bool frame_captured)>;
|
||||
|
||||
display_t() noexcept : offset_x { 0 }, offset_y { 0 } {}
|
||||
display_t() noexcept:
|
||||
offset_x { 0 }, offset_y { 0 } {}
|
||||
|
||||
/**
|
||||
* snapshot_cb --> the callback
|
||||
|
|
@ -267,21 +276,27 @@ public:
|
|||
* capture_e::error on error
|
||||
* capture_e::reinit when need of reinitialization
|
||||
*/
|
||||
virtual capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) = 0;
|
||||
virtual capture_e
|
||||
capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) = 0;
|
||||
|
||||
virtual std::shared_ptr<img_t> alloc_img() = 0;
|
||||
virtual std::shared_ptr<img_t>
|
||||
alloc_img() = 0;
|
||||
|
||||
virtual int dummy_img(img_t *img) = 0;
|
||||
virtual int
|
||||
dummy_img(img_t *img) = 0;
|
||||
|
||||
virtual std::shared_ptr<hwdevice_t> make_hwdevice(pix_fmt_e pix_fmt) {
|
||||
virtual std::shared_ptr<hwdevice_t>
|
||||
make_hwdevice(pix_fmt_e pix_fmt) {
|
||||
return std::make_shared<hwdevice_t>();
|
||||
}
|
||||
|
||||
virtual bool is_hdr() {
|
||||
virtual bool
|
||||
is_hdr() {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
||||
virtual bool
|
||||
get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
||||
std::memset(&metadata, 0, sizeof(metadata));
|
||||
return false;
|
||||
}
|
||||
|
|
@ -293,40 +308,50 @@ public:
|
|||
int env_width, env_height;
|
||||
|
||||
int width, height;
|
||||
};
|
||||
};
|
||||
|
||||
class mic_t {
|
||||
public:
|
||||
virtual capture_e sample(std::vector<std::int16_t> &frame_buffer) = 0;
|
||||
class mic_t {
|
||||
public:
|
||||
virtual capture_e
|
||||
sample(std::vector<std::int16_t> &frame_buffer) = 0;
|
||||
|
||||
virtual ~mic_t() = default;
|
||||
};
|
||||
};
|
||||
|
||||
class audio_control_t {
|
||||
public:
|
||||
virtual int set_sink(const std::string &sink) = 0;
|
||||
class audio_control_t {
|
||||
public:
|
||||
virtual int
|
||||
set_sink(const std::string &sink) = 0;
|
||||
|
||||
virtual std::unique_ptr<mic_t> microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) = 0;
|
||||
virtual std::unique_ptr<mic_t>
|
||||
microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) = 0;
|
||||
|
||||
virtual std::optional<sink_t> sink_info() = 0;
|
||||
virtual std::optional<sink_t>
|
||||
sink_info() = 0;
|
||||
|
||||
virtual ~audio_control_t() = default;
|
||||
};
|
||||
};
|
||||
|
||||
void freeInput(void *);
|
||||
void
|
||||
freeInput(void *);
|
||||
|
||||
using input_t = util::safe_ptr<void, freeInput>;
|
||||
using input_t = util::safe_ptr<void, freeInput>;
|
||||
|
||||
std::filesystem::path appdata();
|
||||
std::filesystem::path
|
||||
appdata();
|
||||
|
||||
std::string get_mac_address(const std::string_view &address);
|
||||
std::string
|
||||
get_mac_address(const std::string_view &address);
|
||||
|
||||
std::string from_sockaddr(const sockaddr *const);
|
||||
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const);
|
||||
std::string
|
||||
from_sockaddr(const sockaddr *const);
|
||||
std::pair<std::uint16_t, std::string>
|
||||
from_sockaddr_ex(const sockaddr *const);
|
||||
|
||||
std::unique_ptr<audio_control_t> audio_control();
|
||||
std::unique_ptr<audio_control_t>
|
||||
audio_control();
|
||||
|
||||
/**
|
||||
/**
|
||||
* display_name --> The name of the monitor that SHOULD be displayed
|
||||
* If display_name is empty --> Use the first monitor that's compatible you can find
|
||||
* If you require to use this parameter in a seperate thread --> make a copy of it.
|
||||
|
|
@ -335,29 +360,37 @@ std::unique_ptr<audio_control_t> audio_control();
|
|||
*
|
||||
* Returns display_t based on hwdevice_type
|
||||
*/
|
||||
std::shared_ptr<display_t> display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
std::shared_ptr<display_t>
|
||||
display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
|
||||
// A list of names of displays accepted as display_name with the mem_type_e
|
||||
std::vector<std::string> display_names(mem_type_e hwdevice_type);
|
||||
// A list of names of displays accepted as display_name with the mem_type_e
|
||||
std::vector<std::string>
|
||||
display_names(mem_type_e hwdevice_type);
|
||||
|
||||
boost::process::child run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, boost::process::environment &env, FILE *file, std::error_code &ec, boost::process::group *group);
|
||||
boost::process::child
|
||||
run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, boost::process::environment &env, FILE *file, std::error_code &ec, boost::process::group *group);
|
||||
|
||||
enum class thread_priority_e : int {
|
||||
enum class thread_priority_e : int {
|
||||
low,
|
||||
normal,
|
||||
high,
|
||||
critical
|
||||
};
|
||||
void adjust_thread_priority(thread_priority_e priority);
|
||||
};
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority);
|
||||
|
||||
// Allow OS-specific actions to be taken to prepare for streaming
|
||||
void streaming_will_start();
|
||||
void streaming_will_stop();
|
||||
// Allow OS-specific actions to be taken to prepare for streaming
|
||||
void
|
||||
streaming_will_start();
|
||||
void
|
||||
streaming_will_stop();
|
||||
|
||||
bool restart_supported();
|
||||
bool restart();
|
||||
bool
|
||||
restart_supported();
|
||||
bool
|
||||
restart();
|
||||
|
||||
struct batched_send_info_t {
|
||||
struct batched_send_info_t {
|
||||
const char *buffer;
|
||||
size_t block_size;
|
||||
size_t block_count;
|
||||
|
|
@ -365,38 +398,54 @@ struct batched_send_info_t {
|
|||
std::uintptr_t native_socket;
|
||||
boost::asio::ip::address &target_address;
|
||||
uint16_t target_port;
|
||||
};
|
||||
bool send_batch(batched_send_info_t &send_info);
|
||||
};
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info);
|
||||
|
||||
enum class qos_data_type_e : int {
|
||||
enum class qos_data_type_e : int {
|
||||
audio,
|
||||
video
|
||||
};
|
||||
std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type);
|
||||
};
|
||||
std::unique_ptr<deinit_t>
|
||||
enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type);
|
||||
|
||||
input_t input();
|
||||
void move_mouse(input_t &input, int deltaX, int deltaY);
|
||||
void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y);
|
||||
void button_mouse(input_t &input, int button, bool release);
|
||||
void scroll(input_t &input, int distance);
|
||||
void hscroll(input_t &input, int distance);
|
||||
void keyboard(input_t &input, uint16_t modcode, bool release);
|
||||
void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state);
|
||||
void unicode(input_t &input, char *utf8, int size);
|
||||
input_t
|
||||
input();
|
||||
void
|
||||
move_mouse(input_t &input, int deltaX, int deltaY);
|
||||
void
|
||||
abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y);
|
||||
void
|
||||
button_mouse(input_t &input, int button, bool release);
|
||||
void
|
||||
scroll(input_t &input, int distance);
|
||||
void
|
||||
hscroll(input_t &input, int distance);
|
||||
void
|
||||
keyboard(input_t &input, uint16_t modcode, bool release);
|
||||
void
|
||||
gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state);
|
||||
void
|
||||
unicode(input_t &input, char *utf8, int size);
|
||||
|
||||
int alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue);
|
||||
void free_gamepad(input_t &input, int nr);
|
||||
int
|
||||
alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue);
|
||||
void
|
||||
free_gamepad(input_t &input, int nr);
|
||||
|
||||
#define SERVICE_NAME "Sunshine"
|
||||
#define SERVICE_TYPE "_nvstream._tcp"
|
||||
|
||||
namespace publish {
|
||||
[[nodiscard]] std::unique_ptr<deinit_t> start();
|
||||
}
|
||||
namespace publish {
|
||||
[[nodiscard]] std::unique_ptr<deinit_t>
|
||||
start();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<deinit_t> init();
|
||||
[[nodiscard]] std::unique_ptr<deinit_t>
|
||||
init();
|
||||
|
||||
std::vector<std::string_view> &supported_gamepads();
|
||||
std::vector<std::string_view> &
|
||||
supported_gamepads();
|
||||
} // namespace platf
|
||||
|
||||
#endif //SUNSHINE_COMMON_H
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
#include "src/thread_safe.h"
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
using namespace std::literals;
|
||||
|
||||
constexpr pa_channel_position_t position_mapping[] {
|
||||
constexpr pa_channel_position_t position_mapping[] {
|
||||
PA_CHANNEL_POSITION_FRONT_LEFT,
|
||||
PA_CHANNEL_POSITION_FRONT_RIGHT,
|
||||
PA_CHANNEL_POSITION_FRONT_CENTER,
|
||||
|
|
@ -28,9 +28,10 @@ constexpr pa_channel_position_t position_mapping[] {
|
|||
PA_CHANNEL_POSITION_REAR_RIGHT,
|
||||
PA_CHANNEL_POSITION_SIDE_LEFT,
|
||||
PA_CHANNEL_POSITION_SIDE_RIGHT,
|
||||
};
|
||||
};
|
||||
|
||||
std::string to_string(const char *name, const std::uint8_t *mapping, int channels) {
|
||||
std::string
|
||||
to_string(const char *name, const std::uint8_t *mapping, int channels) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "rate=48000 sink_name="sv << name << " format=s16le channels="sv << channels << " channel_map="sv;
|
||||
|
|
@ -45,17 +46,18 @@ std::string to_string(const char *name, const std::uint8_t *mapping, int channel
|
|||
|
||||
BOOST_LOG(debug) << "null-sink args: "sv << result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
struct mic_attr_t : public mic_t {
|
||||
struct mic_attr_t: public mic_t {
|
||||
util::safe_ptr<pa_simple, pa_simple_free> mic;
|
||||
|
||||
capture_e sample(std::vector<std::int16_t> &sample_buf) override {
|
||||
capture_e
|
||||
sample(std::vector<std::int16_t> &sample_buf) override {
|
||||
auto sample_size = sample_buf.size();
|
||||
|
||||
auto buf = sample_buf.data();
|
||||
int status;
|
||||
if(pa_simple_read(mic.get(), buf, sample_size * 2, &status)) {
|
||||
if (pa_simple_read(mic.get(), buf, sample_size * 2, &status)) {
|
||||
BOOST_LOG(error) << "pa_simple_read() failed: "sv << pa_strerror(status);
|
||||
|
||||
return capture_e::error;
|
||||
|
|
@ -63,12 +65,13 @@ struct mic_attr_t : public mic_t {
|
|||
|
||||
return capture_e::ok;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<mic_t> microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size, std::string source_name) {
|
||||
std::unique_ptr<mic_t>
|
||||
microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size, std::string source_name) {
|
||||
auto mic = std::make_unique<mic_attr_t>();
|
||||
|
||||
pa_sample_spec ss { PA_SAMPLE_S16LE, sample_rate, (std::uint8_t)channels };
|
||||
pa_sample_spec ss { PA_SAMPLE_S16LE, sample_rate, (std::uint8_t) channels };
|
||||
pa_channel_map pa_map;
|
||||
|
||||
pa_map.channels = channels;
|
||||
|
|
@ -86,95 +89,101 @@ std::unique_ptr<mic_t> microphone(const std::uint8_t *mapping, int channels, std
|
|||
pa_stream_direction_t::PA_STREAM_RECORD, source_name.c_str(),
|
||||
"sunshine-record", &ss, &pa_map, &pa_attr, &status));
|
||||
|
||||
if(!mic->mic) {
|
||||
if (!mic->mic) {
|
||||
auto err_str = pa_strerror(status);
|
||||
BOOST_LOG(error) << "pa_simple_new() failed: "sv << err_str;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mic;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pa {
|
||||
template<bool B, class T>
|
||||
struct add_const_helper;
|
||||
namespace pa {
|
||||
template <bool B, class T>
|
||||
struct add_const_helper;
|
||||
|
||||
template<class T>
|
||||
struct add_const_helper<true, T> {
|
||||
template <class T>
|
||||
struct add_const_helper<true, T> {
|
||||
using type = const std::remove_pointer_t<T> *;
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct add_const_helper<false, T> {
|
||||
template <class T>
|
||||
struct add_const_helper<false, T> {
|
||||
using type = const T *;
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using add_const_t = typename add_const_helper<std::is_pointer_v<T>, T>::type;
|
||||
template <class T>
|
||||
using add_const_t = typename add_const_helper<std::is_pointer_v<T>, T>::type;
|
||||
|
||||
template<class T>
|
||||
void pa_free(T *p) {
|
||||
template <class T>
|
||||
void
|
||||
pa_free(T *p) {
|
||||
pa_xfree(p);
|
||||
}
|
||||
using ctx_t = util::safe_ptr<pa_context, pa_context_unref>;
|
||||
using loop_t = util::safe_ptr<pa_mainloop, pa_mainloop_free>;
|
||||
using op_t = util::safe_ptr<pa_operation, pa_operation_unref>;
|
||||
using string_t = util::safe_ptr<char, pa_free<char>>;
|
||||
}
|
||||
using ctx_t = util::safe_ptr<pa_context, pa_context_unref>;
|
||||
using loop_t = util::safe_ptr<pa_mainloop, pa_mainloop_free>;
|
||||
using op_t = util::safe_ptr<pa_operation, pa_operation_unref>;
|
||||
using string_t = util::safe_ptr<char, pa_free<char>>;
|
||||
|
||||
template<class T>
|
||||
using cb_simple_t = std::function<void(ctx_t::pointer, add_const_t<T> i)>;
|
||||
template <class T>
|
||||
using cb_simple_t = std::function<void(ctx_t::pointer, add_const_t<T> i)>;
|
||||
|
||||
template<class T>
|
||||
void cb(ctx_t::pointer ctx, add_const_t<T> i, void *userdata) {
|
||||
auto &f = *(cb_simple_t<T> *)userdata;
|
||||
template <class T>
|
||||
void
|
||||
cb(ctx_t::pointer ctx, add_const_t<T> i, void *userdata) {
|
||||
auto &f = *(cb_simple_t<T> *) userdata;
|
||||
|
||||
// Cannot similarly filter on eol here. Unless reported otherwise assume
|
||||
// we have no need for special filtering like cb?
|
||||
f(ctx, i);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
using cb_t = std::function<void(ctx_t::pointer, add_const_t<T> i, int eol)>;
|
||||
template <class T>
|
||||
using cb_t = std::function<void(ctx_t::pointer, add_const_t<T> i, int eol)>;
|
||||
|
||||
template<class T>
|
||||
void cb(ctx_t::pointer ctx, add_const_t<T> i, int eol, void *userdata) {
|
||||
auto &f = *(cb_t<T> *)userdata;
|
||||
template <class T>
|
||||
void
|
||||
cb(ctx_t::pointer ctx, add_const_t<T> i, int eol, void *userdata) {
|
||||
auto &f = *(cb_t<T> *) userdata;
|
||||
|
||||
// For some reason, pulseaudio calls this callback after disconnecting
|
||||
if(i && eol) {
|
||||
if (i && eol) {
|
||||
return;
|
||||
}
|
||||
|
||||
f(ctx, i, eol);
|
||||
}
|
||||
}
|
||||
|
||||
void cb_i(ctx_t::pointer ctx, std::uint32_t i, void *userdata) {
|
||||
auto alarm = (safe::alarm_raw_t<int> *)userdata;
|
||||
void
|
||||
cb_i(ctx_t::pointer ctx, std::uint32_t i, void *userdata) {
|
||||
auto alarm = (safe::alarm_raw_t<int> *) userdata;
|
||||
|
||||
alarm->ring(i);
|
||||
}
|
||||
}
|
||||
|
||||
void ctx_state_cb(ctx_t::pointer ctx, void *userdata) {
|
||||
auto &f = *(std::function<void(ctx_t::pointer)> *)userdata;
|
||||
void
|
||||
ctx_state_cb(ctx_t::pointer ctx, void *userdata) {
|
||||
auto &f = *(std::function<void(ctx_t::pointer)> *) userdata;
|
||||
|
||||
f(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void success_cb(ctx_t::pointer ctx, int status, void *userdata) {
|
||||
void
|
||||
success_cb(ctx_t::pointer ctx, int status, void *userdata) {
|
||||
assert(userdata != nullptr);
|
||||
|
||||
auto alarm = (safe::alarm_raw_t<int> *)userdata;
|
||||
auto alarm = (safe::alarm_raw_t<int> *) userdata;
|
||||
alarm->ring(status ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
class server_t : public audio_control_t {
|
||||
class server_t: public audio_control_t {
|
||||
enum ctx_event_e : int {
|
||||
ready,
|
||||
terminated,
|
||||
failed
|
||||
};
|
||||
|
||||
public:
|
||||
public:
|
||||
loop_t loop;
|
||||
ctx_t ctx;
|
||||
std::string requested_sink;
|
||||
|
|
@ -189,13 +198,14 @@ public:
|
|||
std::unique_ptr<std::function<void(ctx_t::pointer)>> events_cb;
|
||||
|
||||
std::thread worker;
|
||||
int init() {
|
||||
int
|
||||
init() {
|
||||
events = std::make_unique<safe::event_t<ctx_event_e>>();
|
||||
loop.reset(pa_mainloop_new());
|
||||
ctx.reset(pa_context_new(pa_mainloop_get_api(loop.get()), "sunshine"));
|
||||
|
||||
events_cb = std::make_unique<std::function<void(ctx_t::pointer)>>([this](ctx_t::pointer ctx) {
|
||||
switch(pa_context_get_state(ctx)) {
|
||||
switch (pa_context_get_state(ctx)) {
|
||||
case PA_CONTEXT_READY:
|
||||
events->raise(ready);
|
||||
break;
|
||||
|
|
@ -219,7 +229,7 @@ public:
|
|||
pa_context_set_state_callback(ctx.get(), ctx_state_cb, events_cb.get());
|
||||
|
||||
auto status = pa_context_connect(ctx.get(), nullptr, PA_CONTEXT_NOFLAGS, nullptr);
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't connect to pulseaudio: "sv << pa_strerror(status);
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -229,7 +239,7 @@ public:
|
|||
int retval;
|
||||
auto status = pa_mainloop_run(loop, &retval);
|
||||
|
||||
if(status < 0) {
|
||||
if (status < 0) {
|
||||
BOOST_LOG(error) << "Couldn't run pulseaudio main loop"sv;
|
||||
return;
|
||||
}
|
||||
|
|
@ -238,14 +248,15 @@ public:
|
|||
};
|
||||
|
||||
auto event = events->pop();
|
||||
if(event == failed) {
|
||||
if (event == failed) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_null(const char *name, const std::uint8_t *channel_mapping, int channels) {
|
||||
int
|
||||
load_null(const char *name, const std::uint8_t *channel_mapping, int channels) {
|
||||
auto alarm = safe::make_alarm<int>();
|
||||
|
||||
op_t op {
|
||||
|
|
@ -261,8 +272,9 @@ public:
|
|||
return *alarm->status();
|
||||
}
|
||||
|
||||
int unload_null(std::uint32_t i) {
|
||||
if(i == PA_INVALID_INDEX) {
|
||||
int
|
||||
unload_null(std::uint32_t i) {
|
||||
if (i == PA_INVALID_INDEX) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +286,7 @@ public:
|
|||
|
||||
alarm->wait();
|
||||
|
||||
if(*alarm->status()) {
|
||||
if (*alarm->status()) {
|
||||
BOOST_LOG(error) << "Couldn't unload null-sink with index ["sv << i << "]: "sv << pa_strerror(pa_context_errno(ctx.get()));
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -282,7 +294,8 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::optional<sink_t> sink_info() override {
|
||||
std::optional<sink_t>
|
||||
sink_info() override {
|
||||
constexpr auto stereo = "sink-sunshine-stereo";
|
||||
constexpr auto surround51 = "sink-sunshine-surround51";
|
||||
constexpr auto surround71 = "sink-sunshine-surround71";
|
||||
|
|
@ -295,8 +308,8 @@ public:
|
|||
int nullcount = 0;
|
||||
|
||||
cb_t<pa_sink_info *> f = [&](ctx_t::pointer ctx, const pa_sink_info *sink_info, int eol) {
|
||||
if(!sink_info) {
|
||||
if(!eol) {
|
||||
if (!sink_info) {
|
||||
if (!eol) {
|
||||
BOOST_LOG(error) << "Couldn't get pulseaudio sink info: "sv << pa_strerror(pa_context_errno(ctx));
|
||||
|
||||
alarm->ring(-1);
|
||||
|
|
@ -307,17 +320,17 @@ public:
|
|||
}
|
||||
|
||||
// Ensure Sunshine won't create a sink that already exists.
|
||||
if(!std::strcmp(sink_info->name, stereo)) {
|
||||
if (!std::strcmp(sink_info->name, stereo)) {
|
||||
index.stereo = sink_info->owner_module;
|
||||
|
||||
++nullcount;
|
||||
}
|
||||
else if(!std::strcmp(sink_info->name, surround51)) {
|
||||
else if (!std::strcmp(sink_info->name, surround51)) {
|
||||
index.surround51 = sink_info->owner_module;
|
||||
|
||||
++nullcount;
|
||||
}
|
||||
else if(!std::strcmp(sink_info->name, surround71)) {
|
||||
else if (!std::strcmp(sink_info->name, surround71)) {
|
||||
index.surround71 = sink_info->owner_module;
|
||||
|
||||
++nullcount;
|
||||
|
|
@ -326,7 +339,7 @@ public:
|
|||
|
||||
op_t op { pa_context_get_sink_info_list(ctx.get(), cb<pa_sink_info *>, &f) };
|
||||
|
||||
if(!op) {
|
||||
if (!op) {
|
||||
BOOST_LOG(error) << "Couldn't create card info operation: "sv << pa_strerror(pa_context_errno(ctx.get()));
|
||||
|
||||
return std::nullopt;
|
||||
|
|
@ -334,16 +347,16 @@ public:
|
|||
|
||||
alarm->wait();
|
||||
|
||||
if(*alarm->status()) {
|
||||
if (*alarm->status()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto sink_name = get_default_sink_name();
|
||||
sink.host = sink_name;
|
||||
|
||||
if(index.stereo == PA_INVALID_INDEX) {
|
||||
if (index.stereo == PA_INVALID_INDEX) {
|
||||
index.stereo = load_null(stereo, speaker::map_stereo, sizeof(speaker::map_stereo));
|
||||
if(index.stereo == PA_INVALID_INDEX) {
|
||||
if (index.stereo == PA_INVALID_INDEX) {
|
||||
BOOST_LOG(warning) << "Couldn't create virtual sink for stereo: "sv << pa_strerror(pa_context_errno(ctx.get()));
|
||||
}
|
||||
else {
|
||||
|
|
@ -351,9 +364,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if(index.surround51 == PA_INVALID_INDEX) {
|
||||
if (index.surround51 == PA_INVALID_INDEX) {
|
||||
index.surround51 = load_null(surround51, speaker::map_surround51, sizeof(speaker::map_surround51));
|
||||
if(index.surround51 == PA_INVALID_INDEX) {
|
||||
if (index.surround51 == PA_INVALID_INDEX) {
|
||||
BOOST_LOG(warning) << "Couldn't create virtual sink for surround-51: "sv << pa_strerror(pa_context_errno(ctx.get()));
|
||||
}
|
||||
else {
|
||||
|
|
@ -361,9 +374,9 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if(index.surround71 == PA_INVALID_INDEX) {
|
||||
if (index.surround71 == PA_INVALID_INDEX) {
|
||||
index.surround71 = load_null(surround71, speaker::map_surround71, sizeof(speaker::map_surround71));
|
||||
if(index.surround71 == PA_INVALID_INDEX) {
|
||||
if (index.surround71 == PA_INVALID_INDEX) {
|
||||
BOOST_LOG(warning) << "Couldn't create virtual sink for surround-71: "sv << pa_strerror(pa_context_errno(ctx.get()));
|
||||
}
|
||||
else {
|
||||
|
|
@ -371,28 +384,29 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if(sink_name.empty()) {
|
||||
if (sink_name.empty()) {
|
||||
BOOST_LOG(warning) << "Couldn't find an active default sink. Continuing with virtual audio only."sv;
|
||||
}
|
||||
|
||||
if(nullcount == 3) {
|
||||
if (nullcount == 3) {
|
||||
sink.null = std::make_optional(sink_t::null_t { stereo, surround51, surround71 });
|
||||
}
|
||||
|
||||
return std::make_optional(std::move(sink));
|
||||
}
|
||||
|
||||
std::string get_default_sink_name() {
|
||||
std::string
|
||||
get_default_sink_name() {
|
||||
std::string sink_name;
|
||||
auto alarm = safe::make_alarm<int>();
|
||||
|
||||
cb_simple_t<pa_server_info *> server_f = [&](ctx_t::pointer ctx, const pa_server_info *server_info) {
|
||||
if(!server_info) {
|
||||
if (!server_info) {
|
||||
BOOST_LOG(error) << "Couldn't get pulseaudio server info: "sv << pa_strerror(pa_context_errno(ctx));
|
||||
alarm->ring(-1);
|
||||
}
|
||||
|
||||
if(server_info->default_sink_name) {
|
||||
if (server_info->default_sink_name) {
|
||||
sink_name = server_info->default_sink_name;
|
||||
}
|
||||
alarm->ring(0);
|
||||
|
|
@ -404,17 +418,18 @@ public:
|
|||
return sink_name;
|
||||
}
|
||||
|
||||
std::string get_monitor_name(const std::string &sink_name) {
|
||||
std::string
|
||||
get_monitor_name(const std::string &sink_name) {
|
||||
std::string monitor_name;
|
||||
auto alarm = safe::make_alarm<int>();
|
||||
|
||||
if(sink_name.empty()) {
|
||||
if (sink_name.empty()) {
|
||||
return monitor_name;
|
||||
}
|
||||
|
||||
cb_t<pa_sink_info *> sink_f = [&](ctx_t::pointer ctx, const pa_sink_info *sink_info, int eol) {
|
||||
if(!sink_info) {
|
||||
if(!eol) {
|
||||
if (!sink_info) {
|
||||
if (!eol) {
|
||||
BOOST_LOG(error) << "Couldn't get pulseaudio sink info for ["sv << sink_name
|
||||
<< "]: "sv << pa_strerror(pa_context_errno(ctx));
|
||||
alarm->ring(-1);
|
||||
|
|
@ -435,7 +450,8 @@ public:
|
|||
return monitor_name;
|
||||
}
|
||||
|
||||
std::unique_ptr<mic_t> microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
|
||||
std::unique_ptr<mic_t>
|
||||
microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
|
||||
// Sink choice priority:
|
||||
// 1. Config sink
|
||||
// 2. Last sink swapped to (Usually virtual in this case)
|
||||
|
|
@ -444,13 +460,14 @@ public:
|
|||
// but this happens right after the swap so the default returned by PA was not
|
||||
// the new one just set!
|
||||
auto sink_name = config::audio.sink;
|
||||
if(sink_name.empty()) sink_name = requested_sink;
|
||||
if(sink_name.empty()) sink_name = get_default_sink_name();
|
||||
if (sink_name.empty()) sink_name = requested_sink;
|
||||
if (sink_name.empty()) sink_name = get_default_sink_name();
|
||||
|
||||
return ::platf::microphone(mapping, channels, sample_rate, frame_size, get_monitor_name(sink_name));
|
||||
}
|
||||
|
||||
int set_sink(const std::string &sink) override {
|
||||
int
|
||||
set_sink(const std::string &sink) override {
|
||||
auto alarm = safe::make_alarm<int>();
|
||||
|
||||
BOOST_LOG(info) << "Setting default sink to: ["sv << sink << "]"sv;
|
||||
|
|
@ -459,13 +476,13 @@ public:
|
|||
ctx.get(), sink.c_str(), success_cb, alarm.get()),
|
||||
};
|
||||
|
||||
if(!op) {
|
||||
if (!op) {
|
||||
BOOST_LOG(error) << "Couldn't create set default-sink operation: "sv << pa_strerror(pa_context_errno(ctx.get()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
alarm->wait();
|
||||
if(*alarm->status()) {
|
||||
if (*alarm->status()) {
|
||||
BOOST_LOG(error) << "Couldn't set default-sink ["sv << sink << "]: "sv << pa_strerror(pa_context_errno(ctx.get()));
|
||||
|
||||
return -1;
|
||||
|
|
@ -481,7 +498,7 @@ public:
|
|||
unload_null(index.surround51);
|
||||
unload_null(index.surround71);
|
||||
|
||||
if(worker.joinable()) {
|
||||
if (worker.joinable()) {
|
||||
pa_context_disconnect(ctx.get());
|
||||
|
||||
KITTY_WHILE_LOOP(auto event = events->pop(), event != terminated && event != failed, {
|
||||
|
|
@ -492,16 +509,17 @@ public:
|
|||
worker.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace pa
|
||||
};
|
||||
} // namespace pa
|
||||
|
||||
std::unique_ptr<audio_control_t> audio_control() {
|
||||
std::unique_ptr<audio_control_t>
|
||||
audio_control() {
|
||||
auto audio = std::make_unique<pa::server_t>();
|
||||
|
||||
if(audio->init()) {
|
||||
if (audio->init()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audio;
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
@ -20,30 +20,33 @@ extern "C" {
|
|||
#define SUNSHINE_STRINGVIEW(x) SUNSHINE_STRINGVIEW_HELPER(x)
|
||||
|
||||
#define CU_CHECK(x, y) \
|
||||
if(check((x), SUNSHINE_STRINGVIEW(y ": "))) return -1
|
||||
if (check((x), SUNSHINE_STRINGVIEW(y ": "))) return -1
|
||||
|
||||
#define CU_CHECK_IGNORE(x, y) \
|
||||
check((x), SUNSHINE_STRINGVIEW(y ": "))
|
||||
|
||||
using namespace std::literals;
|
||||
namespace cuda {
|
||||
constexpr auto cudaDevAttrMaxThreadsPerBlock = (CUdevice_attribute)1;
|
||||
constexpr auto cudaDevAttrMaxThreadsPerMultiProcessor = (CUdevice_attribute)39;
|
||||
constexpr auto cudaDevAttrMaxThreadsPerBlock = (CUdevice_attribute) 1;
|
||||
constexpr auto cudaDevAttrMaxThreadsPerMultiProcessor = (CUdevice_attribute) 39;
|
||||
|
||||
void pass_error(const std::string_view &sv, const char *name, const char *description) {
|
||||
void
|
||||
pass_error(const std::string_view &sv, const char *name, const char *description) {
|
||||
BOOST_LOG(error) << sv << name << ':' << description;
|
||||
}
|
||||
}
|
||||
|
||||
void cff(CudaFunctions *cf) {
|
||||
void
|
||||
cff(CudaFunctions *cf) {
|
||||
cuda_free_functions(&cf);
|
||||
}
|
||||
}
|
||||
|
||||
using cdf_t = util::safe_ptr<CudaFunctions, cff>;
|
||||
using cdf_t = util::safe_ptr<CudaFunctions, cff>;
|
||||
|
||||
static cdf_t cdf;
|
||||
static cdf_t cdf;
|
||||
|
||||
inline static int check(CUresult result, const std::string_view &sv) {
|
||||
if(result != CUDA_SUCCESS) {
|
||||
inline static int
|
||||
check(CUresult result, const std::string_view &sv) {
|
||||
if (result != CUDA_SUCCESS) {
|
||||
const char *name;
|
||||
const char *description;
|
||||
|
||||
|
|
@ -55,20 +58,22 @@ inline static int check(CUresult result, const std::string_view &sv) {
|
|||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void freeStream(CUstream stream) {
|
||||
void
|
||||
freeStream(CUstream stream) {
|
||||
CU_CHECK_IGNORE(cdf->cuStreamDestroy(stream), "Couldn't destroy cuda stream");
|
||||
}
|
||||
}
|
||||
|
||||
class img_t : public platf::img_t {
|
||||
public:
|
||||
class img_t: public platf::img_t {
|
||||
public:
|
||||
tex_t tex;
|
||||
};
|
||||
};
|
||||
|
||||
int init() {
|
||||
int
|
||||
init() {
|
||||
auto status = cuda_load_functions(&cdf, nullptr);
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't load cuda: "sv << status;
|
||||
|
||||
return -1;
|
||||
|
|
@ -77,17 +82,18 @@ int init() {
|
|||
CU_CHECK(cdf->cuInit(0), "Couldn't initialize cuda");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class cuda_t : public platf::hwdevice_t {
|
||||
public:
|
||||
int init(int in_width, int in_height) {
|
||||
if(!cdf) {
|
||||
class cuda_t: public platf::hwdevice_t {
|
||||
public:
|
||||
int
|
||||
init(int in_width, int in_height) {
|
||||
if (!cdf) {
|
||||
BOOST_LOG(warning) << "cuda not initialized"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = (void *)0x1;
|
||||
data = (void *) 0x1;
|
||||
|
||||
width = in_width;
|
||||
height = in_height;
|
||||
|
|
@ -95,34 +101,35 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
|
||||
int
|
||||
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
|
||||
this->hwframe.reset(frame);
|
||||
this->frame = frame;
|
||||
|
||||
auto hwframe_ctx = (AVHWFramesContext *)hw_frames_ctx->data;
|
||||
if(hwframe_ctx->sw_format != AV_PIX_FMT_NV12) {
|
||||
auto hwframe_ctx = (AVHWFramesContext *) hw_frames_ctx->data;
|
||||
if (hwframe_ctx->sw_format != AV_PIX_FMT_NV12) {
|
||||
BOOST_LOG(error) << "cuda::cuda_t doesn't support any format other than AV_PIX_FMT_NV12"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!frame->buf[0]) {
|
||||
if(av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) {
|
||||
if (!frame->buf[0]) {
|
||||
if (av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) {
|
||||
BOOST_LOG(error) << "Couldn't get hwframe for NVENC"sv;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
auto cuda_ctx = (AVCUDADeviceContext *)hwframe_ctx->device_ctx->hwctx;
|
||||
auto cuda_ctx = (AVCUDADeviceContext *) hwframe_ctx->device_ctx->hwctx;
|
||||
|
||||
stream = make_stream();
|
||||
if(!stream) {
|
||||
if (!stream) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
cuda_ctx->stream = stream.get();
|
||||
|
||||
auto sws_opt = sws_t::make(width, height, frame->width, frame->height, width * 4);
|
||||
if(!sws_opt) {
|
||||
if (!sws_opt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -133,11 +140,12 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override {
|
||||
void
|
||||
set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override {
|
||||
sws.set_colorspace(colorspace, color_range);
|
||||
|
||||
auto tex = tex_t::make(height, width * 4);
|
||||
if(!tex) {
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -154,14 +162,15 @@ public:
|
|||
|
||||
img.data = image_data.data();
|
||||
|
||||
if(sws.load_ram(img, tex->array)) {
|
||||
if (sws.load_ram(img, tex->array)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex->texture.linear, stream.get(), { frame->width, frame->height, 0, 0 });
|
||||
}
|
||||
|
||||
cudaTextureObject_t tex_obj(const tex_t &tex) const {
|
||||
cudaTextureObject_t
|
||||
tex_obj(const tex_t &tex) const {
|
||||
return linear_interpolation ? tex.texture.linear : tex.texture.point;
|
||||
}
|
||||
|
||||
|
|
@ -174,21 +183,23 @@ public:
|
|||
bool linear_interpolation;
|
||||
|
||||
sws_t sws;
|
||||
};
|
||||
};
|
||||
|
||||
class cuda_ram_t : public cuda_t {
|
||||
public:
|
||||
int convert(platf::img_t &img) override {
|
||||
class cuda_ram_t: public cuda_t {
|
||||
public:
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
return sws.load_ram(img, tex.array) || sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex_obj(tex), stream.get());
|
||||
}
|
||||
|
||||
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
||||
if(cuda_t::set_frame(frame, hw_frames_ctx)) {
|
||||
int
|
||||
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
||||
if (cuda_t::set_frame(frame, hw_frames_ctx)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto tex_opt = tex_t::make(height, width * 4);
|
||||
if(!tex_opt) {
|
||||
if (!tex_opt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -198,62 +209,66 @@ public:
|
|||
}
|
||||
|
||||
tex_t tex;
|
||||
};
|
||||
};
|
||||
|
||||
class cuda_vram_t : public cuda_t {
|
||||
public:
|
||||
int convert(platf::img_t &img) override {
|
||||
return sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex_obj(((img_t *)&img)->tex), stream.get());
|
||||
class cuda_vram_t: public cuda_t {
|
||||
public:
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
return sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex_obj(((img_t *) &img)->tex), stream.get());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, bool vram) {
|
||||
if(init()) {
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(int width, int height, bool vram) {
|
||||
if (init()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<cuda_t> cuda;
|
||||
|
||||
if(vram) {
|
||||
if (vram) {
|
||||
cuda = std::make_shared<cuda_vram_t>();
|
||||
}
|
||||
else {
|
||||
cuda = std::make_shared<cuda_ram_t>();
|
||||
}
|
||||
|
||||
if(cuda->init(width, height)) {
|
||||
if (cuda->init(width, height)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return cuda;
|
||||
}
|
||||
}
|
||||
|
||||
namespace nvfbc {
|
||||
static PNVFBCCREATEINSTANCE createInstance {};
|
||||
static NVFBC_API_FUNCTION_LIST func { NVFBC_VERSION };
|
||||
namespace nvfbc {
|
||||
static PNVFBCCREATEINSTANCE createInstance {};
|
||||
static NVFBC_API_FUNCTION_LIST func { NVFBC_VERSION };
|
||||
|
||||
static constexpr inline NVFBC_BOOL nv_bool(bool b) {
|
||||
static constexpr inline NVFBC_BOOL
|
||||
nv_bool(bool b) {
|
||||
return b ? NVFBC_TRUE : NVFBC_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void *handle { nullptr };
|
||||
int init() {
|
||||
static void *handle { nullptr };
|
||||
int
|
||||
init() {
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libnvidia-fbc.so.1", "libnvidia-fbc.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&createInstance, "NvFBCCreateInstance" },
|
||||
{ (dyn::apiproc *) &createInstance, "NvFBCCreateInstance" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
dlclose(handle);
|
||||
handle = nullptr;
|
||||
|
||||
|
|
@ -261,7 +276,7 @@ int init() {
|
|||
}
|
||||
|
||||
auto status = cuda::nvfbc::createInstance(&cuda::nvfbc::func);
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Unable to create NvFBC instance"sv;
|
||||
|
||||
dlclose(handle);
|
||||
|
|
@ -271,14 +286,14 @@ int init() {
|
|||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class ctx_t {
|
||||
public:
|
||||
class ctx_t {
|
||||
public:
|
||||
ctx_t(NVFBC_SESSION_HANDLE handle) {
|
||||
NVFBC_BIND_CONTEXT_PARAMS params { NVFBC_BIND_CONTEXT_PARAMS_VER };
|
||||
|
||||
if(func.nvFBCBindContext(handle, ¶ms)) {
|
||||
if (func.nvFBCBindContext(handle, ¶ms)) {
|
||||
BOOST_LOG(error) << "Couldn't bind NvFBC context to current thread: " << func.nvFBCGetLastErrorStr(handle);
|
||||
}
|
||||
|
||||
|
|
@ -287,40 +302,43 @@ public:
|
|||
|
||||
~ctx_t() {
|
||||
NVFBC_RELEASE_CONTEXT_PARAMS params { NVFBC_RELEASE_CONTEXT_PARAMS_VER };
|
||||
if(func.nvFBCReleaseContext(handle, ¶ms)) {
|
||||
if (func.nvFBCReleaseContext(handle, ¶ms)) {
|
||||
BOOST_LOG(error) << "Couldn't release NvFBC context from current thread: " << func.nvFBCGetLastErrorStr(handle);
|
||||
}
|
||||
}
|
||||
|
||||
NVFBC_SESSION_HANDLE handle;
|
||||
};
|
||||
};
|
||||
|
||||
class handle_t {
|
||||
class handle_t {
|
||||
enum flag_e {
|
||||
SESSION_HANDLE,
|
||||
SESSION_CAPTURE,
|
||||
MAX_FLAGS,
|
||||
};
|
||||
|
||||
public:
|
||||
public:
|
||||
handle_t() = default;
|
||||
handle_t(handle_t &&other) : handle_flags { other.handle_flags }, handle { other.handle } {
|
||||
handle_t(handle_t &&other):
|
||||
handle_flags { other.handle_flags }, handle { other.handle } {
|
||||
other.handle_flags.reset();
|
||||
}
|
||||
|
||||
handle_t &operator=(handle_t &&other) {
|
||||
handle_t &
|
||||
operator=(handle_t &&other) {
|
||||
std::swap(handle_flags, other.handle_flags);
|
||||
std::swap(handle, other.handle);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
static std::optional<handle_t> make() {
|
||||
static std::optional<handle_t>
|
||||
make() {
|
||||
NVFBC_CREATE_HANDLE_PARAMS params { NVFBC_CREATE_HANDLE_PARAMS_VER };
|
||||
|
||||
handle_t handle;
|
||||
auto status = func.nvFBCCreateHandle(&handle.handle, ¶ms);
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create session: "sv << handle.last_error();
|
||||
|
||||
return std::nullopt;
|
||||
|
|
@ -331,15 +349,17 @@ public:
|
|||
return std::move(handle);
|
||||
}
|
||||
|
||||
const char *last_error() {
|
||||
const char *
|
||||
last_error() {
|
||||
return func.nvFBCGetLastErrorStr(handle);
|
||||
}
|
||||
|
||||
std::optional<NVFBC_GET_STATUS_PARAMS> status() {
|
||||
std::optional<NVFBC_GET_STATUS_PARAMS>
|
||||
status() {
|
||||
NVFBC_GET_STATUS_PARAMS params { NVFBC_GET_STATUS_PARAMS_VER };
|
||||
|
||||
auto status = func.nvFBCGetStatus(handle, ¶ms);
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to get NvFBC status: "sv << last_error();
|
||||
|
||||
return std::nullopt;
|
||||
|
|
@ -348,8 +368,9 @@ public:
|
|||
return params;
|
||||
}
|
||||
|
||||
int capture(NVFBC_CREATE_CAPTURE_SESSION_PARAMS &capture_params) {
|
||||
if(func.nvFBCCreateCaptureSession(handle, &capture_params)) {
|
||||
int
|
||||
capture(NVFBC_CREATE_CAPTURE_SESSION_PARAMS &capture_params) {
|
||||
if (func.nvFBCCreateCaptureSession(handle, &capture_params)) {
|
||||
BOOST_LOG(error) << "Failed to start capture session: "sv << last_error();
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -361,21 +382,22 @@ public:
|
|||
NVFBC_BUFFER_FORMAT_BGRA,
|
||||
};
|
||||
|
||||
if(func.nvFBCToCudaSetUp(handle, &setup_params)) {
|
||||
if (func.nvFBCToCudaSetUp(handle, &setup_params)) {
|
||||
BOOST_LOG(error) << "Failed to setup cuda interop with nvFBC: "sv << last_error();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stop() {
|
||||
if(!handle_flags[SESSION_CAPTURE]) {
|
||||
int
|
||||
stop() {
|
||||
if (!handle_flags[SESSION_CAPTURE]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NVFBC_DESTROY_CAPTURE_SESSION_PARAMS params { NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER };
|
||||
|
||||
if(func.nvFBCDestroyCaptureSession(handle, ¶ms)) {
|
||||
if (func.nvFBCDestroyCaptureSession(handle, ¶ms)) {
|
||||
BOOST_LOG(error) << "Couldn't destroy capture session: "sv << last_error();
|
||||
|
||||
return -1;
|
||||
|
|
@ -386,8 +408,9 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int reset() {
|
||||
if(!handle_flags[SESSION_HANDLE]) {
|
||||
int
|
||||
reset() {
|
||||
if (!handle_flags[SESSION_HANDLE]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -395,7 +418,7 @@ public:
|
|||
|
||||
NVFBC_DESTROY_HANDLE_PARAMS params { NVFBC_DESTROY_HANDLE_PARAMS_VER };
|
||||
|
||||
if(func.nvFBCDestroyHandle(handle, ¶ms)) {
|
||||
if (func.nvFBCDestroyHandle(handle, ¶ms)) {
|
||||
BOOST_LOG(error) << "Couldn't destroy session handle: "sv << func.nvFBCGetLastErrorStr(handle);
|
||||
}
|
||||
|
||||
|
|
@ -411,29 +434,30 @@ public:
|
|||
std::bitset<MAX_FLAGS> handle_flags;
|
||||
|
||||
NVFBC_SESSION_HANDLE handle;
|
||||
};
|
||||
};
|
||||
|
||||
class display_t : public platf::display_t {
|
||||
public:
|
||||
int init(const std::string_view &display_name, const ::video::config_t &config) {
|
||||
class display_t: public platf::display_t {
|
||||
public:
|
||||
int
|
||||
init(const std::string_view &display_name, const ::video::config_t &config) {
|
||||
auto handle = handle_t::make();
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx_t ctx { handle->handle };
|
||||
|
||||
auto status_params = handle->status();
|
||||
if(!status_params) {
|
||||
if (!status_params) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int streamedMonitor = -1;
|
||||
if(!display_name.empty()) {
|
||||
if(status_params->bXRandRAvailable) {
|
||||
if (!display_name.empty()) {
|
||||
if (status_params->bXRandRAvailable) {
|
||||
auto monitor_nr = util::from_view(display_name);
|
||||
|
||||
if(monitor_nr < 0 || monitor_nr >= status_params->dwOutputNum) {
|
||||
if (monitor_nr < 0 || monitor_nr >= status_params->dwOutputNum) {
|
||||
BOOST_LOG(warning) << "Can't stream monitor ["sv << monitor_nr << "], it needs to be between [0] and ["sv << status_params->dwOutputNum - 1 << "], defaulting to virtual desktop"sv;
|
||||
}
|
||||
else {
|
||||
|
|
@ -454,7 +478,7 @@ public:
|
|||
|
||||
capture_params.dwSamplingRateMs = 1000 /* ms */ / config.framerate;
|
||||
|
||||
if(streamedMonitor != -1) {
|
||||
if (streamedMonitor != -1) {
|
||||
auto &output = status_params->outputs[streamedMonitor];
|
||||
|
||||
width = output.trackedBox.w;
|
||||
|
|
@ -479,7 +503,8 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
platf::capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<platf::img_t> img, bool *cursor) override {
|
||||
platf::capture_e
|
||||
capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<platf::img_t> img, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
// Force display_t::capture to initialize handle_t::capture
|
||||
|
|
@ -490,19 +515,19 @@ public:
|
|||
handle.reset();
|
||||
});
|
||||
|
||||
while(img) {
|
||||
while (img) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if(next_frame > now) {
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
}
|
||||
while(next_frame > now) {
|
||||
while (next_frame > now) {
|
||||
std::this_thread::sleep_for(1ns);
|
||||
now = std::chrono::steady_clock::now();
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
auto status = snapshot(img.get(), 150ms, *cursor);
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case platf::capture_e::reinit:
|
||||
case platf::capture_e::error:
|
||||
return status;
|
||||
|
|
@ -513,7 +538,7 @@ public:
|
|||
img = snapshot_cb(img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
@ -522,13 +547,14 @@ public:
|
|||
}
|
||||
|
||||
// Reinitialize the capture session.
|
||||
platf::capture_e reinit(bool cursor) {
|
||||
if(handle.stop()) {
|
||||
platf::capture_e
|
||||
reinit(bool cursor) {
|
||||
if (handle.stop()) {
|
||||
return platf::capture_e::error;
|
||||
}
|
||||
|
||||
cursor_visible = cursor;
|
||||
if(cursor) {
|
||||
if (cursor) {
|
||||
capture_params.bPushModel = nv_bool(false);
|
||||
capture_params.bWithCursor = nv_bool(true);
|
||||
capture_params.bAllowDirectCapture = nv_bool(false);
|
||||
|
|
@ -539,12 +565,12 @@ public:
|
|||
capture_params.bAllowDirectCapture = nv_bool(true);
|
||||
}
|
||||
|
||||
if(handle.capture(capture_params)) {
|
||||
if (handle.capture(capture_params)) {
|
||||
return platf::capture_e::error;
|
||||
}
|
||||
|
||||
// If trying to capture directly, test if it actually does.
|
||||
if(capture_params.bAllowDirectCapture) {
|
||||
if (capture_params.bAllowDirectCapture) {
|
||||
CUdeviceptr device_ptr;
|
||||
NVFBC_FRAME_GRAB_INFO info;
|
||||
|
||||
|
|
@ -557,9 +583,9 @@ public:
|
|||
};
|
||||
|
||||
// Direct Capture may fail the first few times, even if it's possible
|
||||
for(int x = 0; x < 3; ++x) {
|
||||
if(auto status = func.nvFBCToCudaGrabFrame(handle.handle, &grab)) {
|
||||
if(status == NVFBC_ERR_MUST_RECREATE) {
|
||||
for (int x = 0; x < 3; ++x) {
|
||||
if (auto status = func.nvFBCToCudaGrabFrame(handle.handle, &grab)) {
|
||||
if (status == NVFBC_ERR_MUST_RECREATE) {
|
||||
return platf::capture_e::reinit;
|
||||
}
|
||||
|
||||
|
|
@ -568,21 +594,21 @@ public:
|
|||
return platf::capture_e::error;
|
||||
}
|
||||
|
||||
if(info.bDirectCapture) {
|
||||
if (info.bDirectCapture) {
|
||||
break;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << "Direct capture failed attempt ["sv << x << ']';
|
||||
}
|
||||
|
||||
if(!info.bDirectCapture) {
|
||||
if (!info.bDirectCapture) {
|
||||
BOOST_LOG(debug) << "Direct capture failed, trying the extra copy method"sv;
|
||||
// Direct capture failed
|
||||
capture_params.bPushModel = nv_bool(false);
|
||||
capture_params.bWithCursor = nv_bool(false);
|
||||
capture_params.bAllowDirectCapture = nv_bool(false);
|
||||
|
||||
if(handle.stop() || handle.capture(capture_params)) {
|
||||
if (handle.stop() || handle.capture(capture_params)) {
|
||||
return platf::capture_e::error;
|
||||
}
|
||||
}
|
||||
|
|
@ -591,10 +617,11 @@ public:
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
platf::capture_e snapshot(platf::img_t *img, std::chrono::milliseconds timeout, bool cursor) {
|
||||
if(cursor != cursor_visible) {
|
||||
platf::capture_e
|
||||
snapshot(platf::img_t *img, std::chrono::milliseconds timeout, bool cursor) {
|
||||
if (cursor != cursor_visible) {
|
||||
auto status = reinit(cursor);
|
||||
if(status != platf::capture_e::ok) {
|
||||
if (status != platf::capture_e::ok) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
@ -607,11 +634,11 @@ public:
|
|||
NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT,
|
||||
&device_ptr,
|
||||
&info,
|
||||
(std::uint32_t)timeout.count(),
|
||||
(std::uint32_t) timeout.count(),
|
||||
};
|
||||
|
||||
if(auto status = func.nvFBCToCudaGrabFrame(handle.handle, &grab)) {
|
||||
if(status == NVFBC_ERR_MUST_RECREATE) {
|
||||
if (auto status = func.nvFBCToCudaGrabFrame(handle.handle, &grab)) {
|
||||
if (status == NVFBC_ERR_MUST_RECREATE) {
|
||||
return platf::capture_e::reinit;
|
||||
}
|
||||
|
||||
|
|
@ -619,18 +646,20 @@ public:
|
|||
return platf::capture_e::error;
|
||||
}
|
||||
|
||||
if(((img_t *)img)->tex.copy((std::uint8_t *)device_ptr, img->height, img->row_pitch)) {
|
||||
if (((img_t *) img)->tex.copy((std::uint8_t *) device_ptr, img->height, img->row_pitch)) {
|
||||
return platf::capture_e::error;
|
||||
}
|
||||
|
||||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(platf::pix_fmt_e pix_fmt) override {
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(platf::pix_fmt_e pix_fmt) override {
|
||||
return ::cuda::make_hwdevice(width, height, true);
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::img_t> alloc_img() override {
|
||||
std::shared_ptr<platf::img_t>
|
||||
alloc_img() override {
|
||||
auto img = std::make_shared<cuda::img_t>();
|
||||
|
||||
img->data = nullptr;
|
||||
|
|
@ -640,7 +669,7 @@ public:
|
|||
img->row_pitch = img->width * img->pixel_pitch;
|
||||
|
||||
auto tex_opt = tex_t::make(height, width * img->pixel_pitch);
|
||||
if(!tex_opt) {
|
||||
if (!tex_opt) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -649,7 +678,8 @@ public:
|
|||
return img;
|
||||
};
|
||||
|
||||
int dummy_img(platf::img_t *) override {
|
||||
int
|
||||
dummy_img(platf::img_t *) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -659,44 +689,46 @@ public:
|
|||
handle_t handle;
|
||||
|
||||
NVFBC_CREATE_CAPTURE_SESSION_PARAMS capture_params;
|
||||
};
|
||||
} // namespace nvfbc
|
||||
};
|
||||
} // namespace nvfbc
|
||||
} // namespace cuda
|
||||
|
||||
namespace platf {
|
||||
std::shared_ptr<display_t> nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if(hwdevice_type != mem_type_e::cuda) {
|
||||
std::shared_ptr<display_t>
|
||||
nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if (hwdevice_type != mem_type_e::cuda) {
|
||||
BOOST_LOG(error) << "Could not initialize nvfbc display with the given hw device type"sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto display = std::make_shared<cuda::nvfbc::display_t>();
|
||||
|
||||
if(display->init(display_name, config)) {
|
||||
if (display->init(display_name, config)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> nvfbc_display_names() {
|
||||
if(cuda::init() || cuda::nvfbc::init()) {
|
||||
std::vector<std::string>
|
||||
nvfbc_display_names() {
|
||||
if (cuda::init() || cuda::nvfbc::init()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> display_names;
|
||||
|
||||
auto handle = cuda::nvfbc::handle_t::make();
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto status_params = handle->status();
|
||||
if(!status_params) {
|
||||
if (!status_params) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if(!status_params->bIsCapturePossible) {
|
||||
if (!status_params->bIsCapturePossible) {
|
||||
BOOST_LOG(error) << "NVidia driver doesn't support NvFBC screencasting"sv;
|
||||
}
|
||||
|
||||
|
|
@ -704,7 +736,7 @@ std::vector<std::string> nvfbc_display_names() {
|
|||
BOOST_LOG(info) << "Virtual Desktop: "sv << status_params->screenSize.w << 'x' << status_params->screenSize.h;
|
||||
BOOST_LOG(info) << "XrandR: "sv << (status_params->bXRandRAvailable ? "available"sv : "unavailable"sv);
|
||||
|
||||
for(auto x = 0; x < status_params->dwOutputNum; ++x) {
|
||||
for (auto x = 0; x < status_params->dwOutputNum; ++x) {
|
||||
auto &output = status_params->outputs[x];
|
||||
BOOST_LOG(info) << "-- Output --"sv;
|
||||
BOOST_LOG(debug) << " ID: "sv << output.dwId;
|
||||
|
|
@ -715,5 +747,5 @@ std::vector<std::string> nvfbc_display_names() {
|
|||
}
|
||||
|
||||
return display_names;
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
@ -1,69 +1,78 @@
|
|||
#if !defined(SUNSHINE_PLATFORM_CUDA_H) && defined(SUNSHINE_BUILD_CUDA)
|
||||
#define SUNSHINE_PLATFORM_CUDA_H
|
||||
#define SUNSHINE_PLATFORM_CUDA_H
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace platf {
|
||||
class hwdevice_t;
|
||||
class img_t;
|
||||
class hwdevice_t;
|
||||
class img_t;
|
||||
} // namespace platf
|
||||
|
||||
namespace cuda {
|
||||
|
||||
namespace nvfbc {
|
||||
std::vector<std::string> display_names();
|
||||
}
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, bool vram);
|
||||
int init();
|
||||
namespace nvfbc {
|
||||
std::vector<std::string>
|
||||
display_names();
|
||||
}
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(int width, int height, bool vram);
|
||||
int
|
||||
init();
|
||||
} // namespace cuda
|
||||
|
||||
typedef struct cudaArray *cudaArray_t;
|
||||
|
||||
#if !defined(__CUDACC__)
|
||||
#if !defined(__CUDACC__)
|
||||
typedef struct CUstream_st *cudaStream_t;
|
||||
typedef unsigned long long cudaTextureObject_t;
|
||||
#else /* defined(__CUDACC__) */
|
||||
#else /* defined(__CUDACC__) */
|
||||
typedef __location__(device_builtin) struct CUstream_st *cudaStream_t;
|
||||
typedef __location__(device_builtin) unsigned long long cudaTextureObject_t;
|
||||
#endif /* !defined(__CUDACC__) */
|
||||
#endif /* !defined(__CUDACC__) */
|
||||
|
||||
namespace cuda {
|
||||
|
||||
class freeCudaPtr_t {
|
||||
public:
|
||||
void operator()(void *ptr);
|
||||
};
|
||||
class freeCudaPtr_t {
|
||||
public:
|
||||
void
|
||||
operator()(void *ptr);
|
||||
};
|
||||
|
||||
class freeCudaStream_t {
|
||||
public:
|
||||
void operator()(cudaStream_t ptr);
|
||||
};
|
||||
class freeCudaStream_t {
|
||||
public:
|
||||
void
|
||||
operator()(cudaStream_t ptr);
|
||||
};
|
||||
|
||||
using ptr_t = std::unique_ptr<void, freeCudaPtr_t>;
|
||||
using stream_t = std::unique_ptr<CUstream_st, freeCudaStream_t>;
|
||||
using ptr_t = std::unique_ptr<void, freeCudaPtr_t>;
|
||||
using stream_t = std::unique_ptr<CUstream_st, freeCudaStream_t>;
|
||||
|
||||
stream_t make_stream(int flags = 0);
|
||||
stream_t
|
||||
make_stream(int flags = 0);
|
||||
|
||||
struct viewport_t {
|
||||
struct viewport_t {
|
||||
int width, height;
|
||||
int offsetX, offsetY;
|
||||
};
|
||||
};
|
||||
|
||||
class tex_t {
|
||||
public:
|
||||
static std::optional<tex_t> make(int height, int pitch);
|
||||
class tex_t {
|
||||
public:
|
||||
static std::optional<tex_t>
|
||||
make(int height, int pitch);
|
||||
|
||||
tex_t();
|
||||
tex_t(tex_t &&);
|
||||
|
||||
tex_t &operator=(tex_t &&other);
|
||||
tex_t &
|
||||
operator=(tex_t &&other);
|
||||
|
||||
~tex_t();
|
||||
|
||||
int copy(std::uint8_t *src, int height, int pitch);
|
||||
int
|
||||
copy(std::uint8_t *src, int height, int pitch);
|
||||
|
||||
cudaArray_t array;
|
||||
|
||||
|
|
@ -71,10 +80,10 @@ public:
|
|||
cudaTextureObject_t point;
|
||||
cudaTextureObject_t linear;
|
||||
} texture;
|
||||
};
|
||||
};
|
||||
|
||||
class sws_t {
|
||||
public:
|
||||
class sws_t {
|
||||
public:
|
||||
sws_t() = default;
|
||||
sws_t(int in_width, int in_height, int out_width, int out_height, int pitch, int threadsPerBlock, ptr_t &&color_matrix);
|
||||
|
||||
|
|
@ -84,15 +93,20 @@ public:
|
|||
*
|
||||
* pitch -- The size of a single row of pixels in bytes
|
||||
*/
|
||||
static std::optional<sws_t> make(int in_width, int in_height, int out_width, int out_height, int pitch);
|
||||
static std::optional<sws_t>
|
||||
make(int in_width, int in_height, int out_width, int out_height, int pitch);
|
||||
|
||||
// Converts loaded image into a CUDevicePtr
|
||||
int convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream);
|
||||
int convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream, const viewport_t &viewport);
|
||||
int
|
||||
convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream);
|
||||
int
|
||||
convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream, const viewport_t &viewport);
|
||||
|
||||
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range);
|
||||
void
|
||||
set_colorspace(std::uint32_t colorspace, std::uint32_t color_range);
|
||||
|
||||
int load_ram(platf::img_t &img, cudaArray_t array);
|
||||
int
|
||||
load_ram(platf::img_t &img, cudaArray_t array);
|
||||
|
||||
ptr_t color_matrix;
|
||||
|
||||
|
|
@ -101,7 +115,7 @@ public:
|
|||
viewport_t viewport;
|
||||
|
||||
float scale;
|
||||
};
|
||||
};
|
||||
} // namespace cuda
|
||||
|
||||
#endif
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
// They aren't likely to change any time soon.
|
||||
#define fourcc_code(a, b, c, d) ((std::uint32_t)(a) | ((std::uint32_t)(b) << 8) | \
|
||||
((std::uint32_t)(c) << 16) | ((std::uint32_t)(d) << 24))
|
||||
#define fourcc_mod_code(vendor, val) ((((uint64_t)vendor) << 56) | ((val)&0x00ffffffffffffffULL))
|
||||
#define fourcc_mod_code(vendor, val) ((((uint64_t) vendor) << 56) | ((val) &0x00ffffffffffffffULL))
|
||||
#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
|
||||
#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
|
||||
#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */
|
||||
|
|
@ -21,29 +21,31 @@
|
|||
|
||||
using namespace std::literals;
|
||||
namespace gl {
|
||||
GladGLContext ctx;
|
||||
GladGLContext ctx;
|
||||
|
||||
void drain_errors(const std::string_view &prefix) {
|
||||
void
|
||||
drain_errors(const std::string_view &prefix) {
|
||||
GLenum err;
|
||||
while((err = ctx.GetError()) != GL_NO_ERROR) {
|
||||
while ((err = ctx.GetError()) != GL_NO_ERROR) {
|
||||
BOOST_LOG(error) << "GL: "sv << prefix << ": ["sv << util::hex(err).to_string_view() << ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tex_t::~tex_t() {
|
||||
if(!size() == 0) {
|
||||
tex_t::~tex_t() {
|
||||
if (!size() == 0) {
|
||||
ctx.DeleteTextures(size(), begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tex_t tex_t::make(std::size_t count) {
|
||||
tex_t
|
||||
tex_t::make(std::size_t count) {
|
||||
tex_t textures { count };
|
||||
|
||||
ctx.GenTextures(textures.size(), textures.begin());
|
||||
|
||||
float color[] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
for(auto tex : textures) {
|
||||
for (auto tex : textures) {
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, tex);
|
||||
gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // x
|
||||
gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // y
|
||||
|
|
@ -53,30 +55,33 @@ tex_t tex_t::make(std::size_t count) {
|
|||
}
|
||||
|
||||
return textures;
|
||||
}
|
||||
}
|
||||
|
||||
frame_buf_t::~frame_buf_t() {
|
||||
if(begin()) {
|
||||
frame_buf_t::~frame_buf_t() {
|
||||
if (begin()) {
|
||||
ctx.DeleteFramebuffers(size(), begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frame_buf_t frame_buf_t::make(std::size_t count) {
|
||||
frame_buf_t
|
||||
frame_buf_t::make(std::size_t count) {
|
||||
frame_buf_t frame_buf { count };
|
||||
|
||||
ctx.GenFramebuffers(frame_buf.size(), frame_buf.begin());
|
||||
|
||||
return frame_buf;
|
||||
}
|
||||
}
|
||||
|
||||
void frame_buf_t::copy(int id, int texture, int offset_x, int offset_y, int width, int height) {
|
||||
void
|
||||
frame_buf_t::copy(int id, int texture, int offset_x, int offset_y, int width, int height) {
|
||||
gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, (*this)[id]);
|
||||
gl::ctx.ReadBuffer(GL_COLOR_ATTACHMENT0 + id);
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, texture);
|
||||
gl::ctx.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, offset_x, offset_y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
std::string shader_t::err_str() {
|
||||
std::string
|
||||
shader_t::err_str() {
|
||||
int length;
|
||||
ctx.GetShaderiv(handle(), GL_INFO_LOG_LENGTH, &length);
|
||||
|
||||
|
|
@ -88,9 +93,10 @@ std::string shader_t::err_str() {
|
|||
string.resize(length - 1);
|
||||
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
util::Either<shader_t, std::string> shader_t::compile(const std::string_view &source, GLenum type) {
|
||||
util::Either<shader_t, std::string>
|
||||
shader_t::compile(const std::string_view &source, GLenum type) {
|
||||
shader_t shader;
|
||||
|
||||
auto data = source.data();
|
||||
|
|
@ -103,18 +109,20 @@ util::Either<shader_t, std::string> shader_t::compile(const std::string_view &so
|
|||
int status = 0;
|
||||
ctx.GetShaderiv(shader.handle(), GL_COMPILE_STATUS, &status);
|
||||
|
||||
if(!status) {
|
||||
if (!status) {
|
||||
return shader.err_str();
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint shader_t::handle() const {
|
||||
GLuint
|
||||
shader_t::handle() const {
|
||||
return _shader.el;
|
||||
}
|
||||
}
|
||||
|
||||
buffer_t buffer_t::make(util::buffer_t<GLint> &&offsets, const char *block, const std::string_view &data) {
|
||||
buffer_t
|
||||
buffer_t::make(util::buffer_t<GLint> &&offsets, const char *block, const std::string_view &data) {
|
||||
buffer_t buffer;
|
||||
buffer._block = block;
|
||||
buffer._size = data.size();
|
||||
|
|
@ -122,37 +130,42 @@ buffer_t buffer_t::make(util::buffer_t<GLint> &&offsets, const char *block, cons
|
|||
|
||||
ctx.GenBuffers(1, &buffer._buffer.el);
|
||||
ctx.BindBuffer(GL_UNIFORM_BUFFER, buffer.handle());
|
||||
ctx.BufferData(GL_UNIFORM_BUFFER, data.size(), (const std::uint8_t *)data.data(), GL_DYNAMIC_DRAW);
|
||||
ctx.BufferData(GL_UNIFORM_BUFFER, data.size(), (const std::uint8_t *) data.data(), GL_DYNAMIC_DRAW);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint buffer_t::handle() const {
|
||||
GLuint
|
||||
buffer_t::handle() const {
|
||||
return _buffer.el;
|
||||
}
|
||||
}
|
||||
|
||||
const char *buffer_t::block() const {
|
||||
const char *
|
||||
buffer_t::block() const {
|
||||
return _block;
|
||||
}
|
||||
}
|
||||
|
||||
void buffer_t::update(const std::string_view &view, std::size_t offset) {
|
||||
void
|
||||
buffer_t::update(const std::string_view &view, std::size_t offset) {
|
||||
ctx.BindBuffer(GL_UNIFORM_BUFFER, handle());
|
||||
ctx.BufferSubData(GL_UNIFORM_BUFFER, offset, view.size(), (const void *)view.data());
|
||||
}
|
||||
ctx.BufferSubData(GL_UNIFORM_BUFFER, offset, view.size(), (const void *) view.data());
|
||||
}
|
||||
|
||||
void buffer_t::update(std::string_view *members, std::size_t count, std::size_t offset) {
|
||||
void
|
||||
buffer_t::update(std::string_view *members, std::size_t count, std::size_t offset) {
|
||||
util::buffer_t<std::uint8_t> buffer { _size };
|
||||
|
||||
for(int x = 0; x < count; ++x) {
|
||||
for (int x = 0; x < count; ++x) {
|
||||
auto val = members[x];
|
||||
|
||||
std::copy_n((const std::uint8_t *)val.data(), val.size(), &buffer[_offsets[x]]);
|
||||
std::copy_n((const std::uint8_t *) val.data(), val.size(), &buffer[_offsets[x]]);
|
||||
}
|
||||
|
||||
update(util::view(buffer.begin(), buffer.end()), offset);
|
||||
}
|
||||
}
|
||||
|
||||
std::string program_t::err_str() {
|
||||
std::string
|
||||
program_t::err_str() {
|
||||
int length;
|
||||
ctx.GetProgramiv(handle(), GL_INFO_LOG_LENGTH, &length);
|
||||
|
||||
|
|
@ -164,9 +177,10 @@ std::string program_t::err_str() {
|
|||
string.resize(length - 1);
|
||||
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
util::Either<program_t, std::string> program_t::link(const shader_t &vert, const shader_t &frag) {
|
||||
util::Either<program_t, std::string>
|
||||
program_t::link(const shader_t &vert, const shader_t &frag) {
|
||||
program_t program;
|
||||
|
||||
program._program.el = ctx.CreateProgram();
|
||||
|
|
@ -186,23 +200,25 @@ util::Either<program_t, std::string> program_t::link(const shader_t &vert, const
|
|||
int status = 0;
|
||||
ctx.GetProgramiv(program.handle(), GL_LINK_STATUS, &status);
|
||||
|
||||
if(!status) {
|
||||
if (!status) {
|
||||
return program.err_str();
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
}
|
||||
|
||||
void program_t::bind(const buffer_t &buffer) {
|
||||
void
|
||||
program_t::bind(const buffer_t &buffer) {
|
||||
ctx.UseProgram(handle());
|
||||
auto i = ctx.GetUniformBlockIndex(handle(), buffer.block());
|
||||
|
||||
ctx.BindBufferBase(GL_UNIFORM_BUFFER, i, buffer.handle());
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<buffer_t> program_t::uniform(const char *block, std::pair<const char *, std::string_view> *members, std::size_t count) {
|
||||
std::optional<buffer_t>
|
||||
program_t::uniform(const char *block, std::pair<const char *, std::string_view> *members, std::size_t count) {
|
||||
auto i = ctx.GetUniformBlockIndex(handle(), block);
|
||||
if(i == GL_INVALID_INDEX) {
|
||||
if (i == GL_INVALID_INDEX) {
|
||||
BOOST_LOG(error) << "Couldn't find index of ["sv << block << ']';
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -213,8 +229,8 @@ std::optional<buffer_t> program_t::uniform(const char *block, std::pair<const ch
|
|||
bool error_flag = false;
|
||||
|
||||
util::buffer_t<GLint> offsets { count };
|
||||
auto indices = (std::uint32_t *)alloca(count * sizeof(std::uint32_t));
|
||||
auto names = (const char **)alloca(count * sizeof(const char *));
|
||||
auto indices = (std::uint32_t *) alloca(count * sizeof(std::uint32_t));
|
||||
auto names = (const char **) alloca(count * sizeof(const char *));
|
||||
auto names_p = names;
|
||||
|
||||
std::for_each_n(members, count, [names_p](auto &member) mutable {
|
||||
|
|
@ -224,96 +240,100 @@ std::optional<buffer_t> program_t::uniform(const char *block, std::pair<const ch
|
|||
std::fill_n(indices, count, GL_INVALID_INDEX);
|
||||
ctx.GetUniformIndices(handle(), count, names, indices);
|
||||
|
||||
for(int x = 0; x < count; ++x) {
|
||||
if(indices[x] == GL_INVALID_INDEX) {
|
||||
for (int x = 0; x < count; ++x) {
|
||||
if (indices[x] == GL_INVALID_INDEX) {
|
||||
error_flag = true;
|
||||
|
||||
BOOST_LOG(error) << "Couldn't find ["sv << block << '.' << members[x].first << ']';
|
||||
}
|
||||
}
|
||||
|
||||
if(error_flag) {
|
||||
if (error_flag) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ctx.GetActiveUniformsiv(handle(), count, indices, GL_UNIFORM_OFFSET, offsets.begin());
|
||||
util::buffer_t<std::uint8_t> buffer { (std::size_t)size };
|
||||
util::buffer_t<std::uint8_t> buffer { (std::size_t) size };
|
||||
|
||||
for(int x = 0; x < count; ++x) {
|
||||
for (int x = 0; x < count; ++x) {
|
||||
auto val = std::get<1>(members[x]);
|
||||
|
||||
std::copy_n((const std::uint8_t *)val.data(), val.size(), &buffer[offsets[x]]);
|
||||
std::copy_n((const std::uint8_t *) val.data(), val.size(), &buffer[offsets[x]]);
|
||||
}
|
||||
|
||||
return buffer_t::make(std::move(offsets), block, std::string_view { (char *)buffer.begin(), buffer.size() });
|
||||
}
|
||||
return buffer_t::make(std::move(offsets), block, std::string_view { (char *) buffer.begin(), buffer.size() });
|
||||
}
|
||||
|
||||
GLuint program_t::handle() const {
|
||||
GLuint
|
||||
program_t::handle() const {
|
||||
return _program.el;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
|
||||
namespace gbm {
|
||||
device_destroy_fn device_destroy;
|
||||
create_device_fn create_device;
|
||||
device_destroy_fn device_destroy;
|
||||
create_device_fn create_device;
|
||||
|
||||
int init() {
|
||||
int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libgbm.so.1", "libgbm.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<GLADapiproc *, const char *>> funcs {
|
||||
{ (GLADapiproc *)&device_destroy, "gbm_device_destroy" },
|
||||
{ (GLADapiproc *)&create_device, "gbm_create_device" },
|
||||
{ (GLADapiproc *) &device_destroy, "gbm_device_destroy" },
|
||||
{ (GLADapiproc *) &create_device, "gbm_create_device" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace gbm
|
||||
|
||||
namespace egl {
|
||||
constexpr auto EGL_LINUX_DMA_BUF_EXT = 0x3270;
|
||||
constexpr auto EGL_LINUX_DRM_FOURCC_EXT = 0x3271;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_FD_EXT = 0x3272;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_OFFSET_EXT = 0x3273;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_PITCH_EXT = 0x3274;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_FD_EXT = 0x3275;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_OFFSET_EXT = 0x3276;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_PITCH_EXT = 0x3277;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_FD_EXT = 0x3278;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_OFFSET_EXT = 0x3279;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_PITCH_EXT = 0x327A;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_FD_EXT = 0x3440;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_OFFSET_EXT = 0x3441;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_PITCH_EXT = 0x3442;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT = 0x3443;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT = 0x3444;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT = 0x3445;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT = 0x3446;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT = 0x3447;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT = 0x3448;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT = 0x3449;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT = 0x344A;
|
||||
constexpr auto EGL_LINUX_DMA_BUF_EXT = 0x3270;
|
||||
constexpr auto EGL_LINUX_DRM_FOURCC_EXT = 0x3271;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_FD_EXT = 0x3272;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_OFFSET_EXT = 0x3273;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_PITCH_EXT = 0x3274;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_FD_EXT = 0x3275;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_OFFSET_EXT = 0x3276;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_PITCH_EXT = 0x3277;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_FD_EXT = 0x3278;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_OFFSET_EXT = 0x3279;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_PITCH_EXT = 0x327A;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_FD_EXT = 0x3440;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_OFFSET_EXT = 0x3441;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_PITCH_EXT = 0x3442;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT = 0x3443;
|
||||
constexpr auto EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT = 0x3444;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT = 0x3445;
|
||||
constexpr auto EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT = 0x3446;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT = 0x3447;
|
||||
constexpr auto EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT = 0x3448;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT = 0x3449;
|
||||
constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT = 0x344A;
|
||||
|
||||
bool fail() {
|
||||
bool
|
||||
fail() {
|
||||
return eglGetError() != EGL_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
display_t make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay *> native_display) {
|
||||
display_t
|
||||
make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay *> native_display) {
|
||||
constexpr auto EGL_PLATFORM_GBM_MESA = 0x31D7;
|
||||
constexpr auto EGL_PLATFORM_WAYLAND_KHR = 0x31D8;
|
||||
constexpr auto EGL_PLATFORM_X11_KHR = 0x31D5;
|
||||
|
|
@ -321,7 +341,7 @@ display_t make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay
|
|||
int egl_platform;
|
||||
void *native_display_p;
|
||||
|
||||
switch(native_display.index()) {
|
||||
switch (native_display.index()) {
|
||||
case 0:
|
||||
egl_platform = EGL_PLATFORM_GBM_MESA;
|
||||
native_display_p = std::get<0>(native_display);
|
||||
|
|
@ -342,13 +362,13 @@ display_t make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay
|
|||
// native_display.left() equals native_display.right()
|
||||
display_t display = eglGetPlatformDisplay(egl_platform, native_display_p, nullptr);
|
||||
|
||||
if(fail()) {
|
||||
if (fail()) {
|
||||
BOOST_LOG(error) << "Couldn't open EGL display: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int major, minor;
|
||||
if(!eglInitialize(display.get(), &major, &minor)) {
|
||||
if (!eglInitialize(display.get(), &major, &minor)) {
|
||||
BOOST_LOG(error) << "Couldn't initialize EGL display: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -368,29 +388,30 @@ display_t make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay
|
|||
"EGL_EXT_image_dma_buf_import_modifiers",
|
||||
};
|
||||
|
||||
for(auto ext : extensions) {
|
||||
if(!std::strstr(extension_st, ext)) {
|
||||
for (auto ext : extensions) {
|
||||
if (!std::strstr(extension_st, ext)) {
|
||||
BOOST_LOG(error) << "Missing extension: ["sv << ext << ']';
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ctx_t> make_ctx(display_t::pointer display) {
|
||||
std::optional<ctx_t>
|
||||
make_ctx(display_t::pointer display) {
|
||||
constexpr int conf_attr[] {
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE
|
||||
};
|
||||
|
||||
int count;
|
||||
EGLConfig conf;
|
||||
if(!eglChooseConfig(display, conf_attr, &conf, 1, &count)) {
|
||||
if (!eglChooseConfig(display, conf_attr, &conf, 1, &count)) {
|
||||
BOOST_LOG(error) << "Couldn't set config attributes: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(!eglBindAPI(EGL_OPENGL_API)) {
|
||||
if (!eglBindAPI(EGL_OPENGL_API)) {
|
||||
BOOST_LOG(error) << "Couldn't bind API: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -400,18 +421,18 @@ std::optional<ctx_t> make_ctx(display_t::pointer display) {
|
|||
};
|
||||
|
||||
ctx_t ctx { display, eglCreateContext(display, conf, EGL_NO_CONTEXT, attr) };
|
||||
if(fail()) {
|
||||
if (fail()) {
|
||||
BOOST_LOG(error) << "Couldn't create EGL context: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TUPLE_EL_REF(ctx_p, 1, ctx.el);
|
||||
if(!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx_p)) {
|
||||
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx_p)) {
|
||||
BOOST_LOG(error) << "Couldn't make current display"sv;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(!gladLoadGLContext(&gl::ctx, eglGetProcAddress)) {
|
||||
if (!gladLoadGLContext(&gl::ctx, eglGetProcAddress)) {
|
||||
BOOST_LOG(error) << "Couldn't load OpenGL library"sv;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -424,18 +445,19 @@ std::optional<ctx_t> make_ctx(display_t::pointer display) {
|
|||
gl::ctx.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
|
||||
struct plane_attr_t {
|
||||
struct plane_attr_t {
|
||||
EGLAttrib fd;
|
||||
EGLAttrib offset;
|
||||
EGLAttrib pitch;
|
||||
EGLAttrib lo;
|
||||
EGLAttrib hi;
|
||||
};
|
||||
};
|
||||
|
||||
inline plane_attr_t get_plane(std::uint32_t plane_indice) {
|
||||
switch(plane_indice) {
|
||||
inline plane_attr_t
|
||||
get_plane(std::uint32_t plane_indice) {
|
||||
switch (plane_indice) {
|
||||
case 0:
|
||||
return {
|
||||
EGL_DMA_BUF_PLANE0_FD_EXT,
|
||||
|
|
@ -472,9 +494,10 @@ inline plane_attr_t get_plane(std::uint32_t plane_indice) {
|
|||
|
||||
// Avoid warning
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<rgb_t> import_source(display_t::pointer egl_display, const surface_descriptor_t &xrgb) {
|
||||
std::optional<rgb_t>
|
||||
import_source(display_t::pointer egl_display, const surface_descriptor_t &xrgb) {
|
||||
EGLAttrib attribs[47];
|
||||
int atti = 0;
|
||||
attribs[atti++] = EGL_WIDTH;
|
||||
|
|
@ -484,10 +507,10 @@ std::optional<rgb_t> import_source(display_t::pointer egl_display, const surface
|
|||
attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
|
||||
attribs[atti++] = xrgb.fourcc;
|
||||
|
||||
for(auto x = 0; x < 4; ++x) {
|
||||
for (auto x = 0; x < 4; ++x) {
|
||||
auto fd = xrgb.fds[x];
|
||||
|
||||
if(fd < 0) {
|
||||
if (fd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -500,7 +523,7 @@ std::optional<rgb_t> import_source(display_t::pointer egl_display, const surface
|
|||
attribs[atti++] = plane_attr.pitch;
|
||||
attribs[atti++] = xrgb.pitches[x];
|
||||
|
||||
if(xrgb.modifier != DRM_FORMAT_MOD_INVALID) {
|
||||
if (xrgb.modifier != DRM_FORMAT_MOD_INVALID) {
|
||||
attribs[atti++] = plane_attr.lo;
|
||||
attribs[atti++] = xrgb.modifier & 0xFFFFFFFF;
|
||||
attribs[atti++] = plane_attr.hi;
|
||||
|
|
@ -515,7 +538,7 @@ std::optional<rgb_t> import_source(display_t::pointer egl_display, const surface
|
|||
gl::tex_t::make(1)
|
||||
};
|
||||
|
||||
if(!rgb->xrgb8) {
|
||||
if (!rgb->xrgb8) {
|
||||
BOOST_LOG(error) << "Couldn't import RGB Image: "sv << util::hex(eglGetError()).to_string_view();
|
||||
|
||||
return std::nullopt;
|
||||
|
|
@ -529,9 +552,10 @@ std::optional<rgb_t> import_source(display_t::pointer egl_display, const surface
|
|||
gl_drain_errors;
|
||||
|
||||
return rgb;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<nv12_t> import_target(display_t::pointer egl_display, std::array<file_t, nv12_img_t::num_fds> &&fds, const surface_descriptor_t &r8, const surface_descriptor_t &gr88) {
|
||||
std::optional<nv12_t>
|
||||
import_target(display_t::pointer egl_display, std::array<file_t, nv12_img_t::num_fds> &&fds, const surface_descriptor_t &r8, const surface_descriptor_t &gr88) {
|
||||
EGLAttrib img_attr_planes[2][13] {
|
||||
{ EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_R8,
|
||||
EGL_WIDTH, r8.width,
|
||||
|
|
@ -559,7 +583,7 @@ std::optional<nv12_t> import_target(display_t::pointer egl_display, std::array<f
|
|||
std::move(fds)
|
||||
};
|
||||
|
||||
if(!nv12->r8 || !nv12->bg88) {
|
||||
if (!nv12->r8 || !nv12->bg88) {
|
||||
BOOST_LOG(error) << "Couldn't create KHR Image"sv;
|
||||
|
||||
return std::nullopt;
|
||||
|
|
@ -576,11 +600,12 @@ std::optional<nv12_t> import_target(display_t::pointer egl_display, std::array<f
|
|||
gl_drain_errors;
|
||||
|
||||
return nv12;
|
||||
}
|
||||
}
|
||||
|
||||
void sws_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {
|
||||
void
|
||||
sws_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {
|
||||
video::color_t *color_p;
|
||||
switch(colorspace) {
|
||||
switch (colorspace) {
|
||||
case 5: // SWS_CS_SMPTE170M
|
||||
color_p = &video::colors[0];
|
||||
break;
|
||||
|
|
@ -595,7 +620,7 @@ void sws_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range)
|
|||
color_p = &video::colors[0];
|
||||
};
|
||||
|
||||
if(color_range > 1) {
|
||||
if (color_range > 1) {
|
||||
// Full range
|
||||
++color_p;
|
||||
}
|
||||
|
|
@ -612,15 +637,16 @@ void sws_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range)
|
|||
|
||||
program[0].bind(color_matrix);
|
||||
program[1].bind(color_matrix);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex) {
|
||||
std::optional<sws_t>
|
||||
sws_t::make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex) {
|
||||
sws_t sws;
|
||||
|
||||
sws.serial = std::numeric_limits<std::uint64_t>::max();
|
||||
|
||||
// Ensure aspect ratio is maintained
|
||||
auto scalar = std::fminf(out_width / (float)in_width, out_heigth / (float)in_height);
|
||||
auto scalar = std::fminf(out_width / (float) in_width, out_heigth / (float) in_height);
|
||||
auto out_width_f = in_width * scalar;
|
||||
auto out_height_f = in_height * scalar;
|
||||
|
||||
|
|
@ -658,24 +684,24 @@ std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int
|
|||
util::Either<gl::shader_t, std::string> compiled_sources[count];
|
||||
|
||||
bool error_flag = false;
|
||||
for(int x = 0; x < count; ++x) {
|
||||
for (int x = 0; x < count; ++x) {
|
||||
auto &compiled_source = compiled_sources[x];
|
||||
|
||||
compiled_source = gl::shader_t::compile(read_file(sources[x]), shader_type[x % 2]);
|
||||
gl_drain_errors;
|
||||
|
||||
if(compiled_source.has_right()) {
|
||||
if (compiled_source.has_right()) {
|
||||
BOOST_LOG(error) << sources[x] << ": "sv << compiled_source.right();
|
||||
error_flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(error_flag) {
|
||||
if (error_flag) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto program = gl::program_t::link(compiled_sources[3].left(), compiled_sources[4].left());
|
||||
if(program.has_right()) {
|
||||
if (program.has_right()) {
|
||||
BOOST_LOG(error) << "GL linker: "sv << program.right();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -684,7 +710,7 @@ std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int
|
|||
sws.program[2] = std::move(program.left());
|
||||
|
||||
program = gl::program_t::link(compiled_sources[1].left(), compiled_sources[0].left());
|
||||
if(program.has_right()) {
|
||||
if (program.has_right()) {
|
||||
BOOST_LOG(error) << "GL linker: "sv << program.right();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -693,7 +719,7 @@ std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int
|
|||
sws.program[1] = std::move(program.left());
|
||||
|
||||
program = gl::program_t::link(compiled_sources[3].left(), compiled_sources[2].left());
|
||||
if(program.has_right()) {
|
||||
if (program.has_right()) {
|
||||
BOOST_LOG(error) << "GL linker: "sv << program.right();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -703,7 +729,7 @@ std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int
|
|||
}
|
||||
|
||||
auto loc_width_i = gl::ctx.GetUniformLocation(sws.program[1].handle(), "width_i");
|
||||
if(loc_width_i < 0) {
|
||||
if (loc_width_i < 0) {
|
||||
BOOST_LOG(error) << "Couldn't find uniform [width_i]"sv;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -721,7 +747,7 @@ std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int
|
|||
};
|
||||
|
||||
auto color_matrix = sws.program[0].uniform("ColorMatrix", members, sizeof(members) / sizeof(decltype(members[0])));
|
||||
if(!color_matrix) {
|
||||
if (!color_matrix) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -740,9 +766,10 @@ std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int
|
|||
gl_drain_errors;
|
||||
|
||||
return std::move(sws);
|
||||
}
|
||||
}
|
||||
|
||||
int sws_t::blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height) {
|
||||
int
|
||||
sws_t::blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height) {
|
||||
auto f = [&]() {
|
||||
std::swap(offsetX, this->offsetX);
|
||||
std::swap(offsetY, this->offsetY);
|
||||
|
|
@ -754,27 +781,30 @@ int sws_t::blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int h
|
|||
auto fg = util::fail_guard(f);
|
||||
|
||||
return convert(fb);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int out_heigth) {
|
||||
std::optional<sws_t>
|
||||
sws_t::make(int in_width, int in_height, int out_width, int out_heigth) {
|
||||
auto tex = gl::tex_t::make(2);
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, tex[0]);
|
||||
gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, in_width, in_height);
|
||||
|
||||
return make(in_width, in_height, out_width, out_heigth, std::move(tex));
|
||||
}
|
||||
}
|
||||
|
||||
void sws_t::load_ram(platf::img_t &img) {
|
||||
void
|
||||
sws_t::load_ram(platf::img_t &img) {
|
||||
loaded_texture = tex[0];
|
||||
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, loaded_texture);
|
||||
gl::ctx.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width, img.height, GL_BGRA, GL_UNSIGNED_BYTE, img.data);
|
||||
}
|
||||
}
|
||||
|
||||
void sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture) {
|
||||
void
|
||||
sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture) {
|
||||
// When only a sub-part of the image must be encoded...
|
||||
const bool copy = offset_x || offset_y || img.sd.width != in_width || img.sd.height != in_height;
|
||||
if(copy) {
|
||||
if (copy) {
|
||||
auto framebuf = gl::frame_buf_t::make(1);
|
||||
framebuf.bind(&texture, &texture + 1);
|
||||
|
||||
|
|
@ -785,14 +815,14 @@ void sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int tex
|
|||
loaded_texture = texture;
|
||||
}
|
||||
|
||||
if(img.data) {
|
||||
if (img.data) {
|
||||
GLenum attachment = GL_COLOR_ATTACHMENT0;
|
||||
|
||||
gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, cursor_framebuffer[0]);
|
||||
gl::ctx.UseProgram(program[2].handle());
|
||||
|
||||
// When a copy has already been made...
|
||||
if(!copy) {
|
||||
if (!copy) {
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, texture);
|
||||
gl::ctx.DrawBuffers(1, &attachment);
|
||||
|
||||
|
|
@ -803,7 +833,7 @@ void sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int tex
|
|||
}
|
||||
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, tex[1]);
|
||||
if(serial != img.serial) {
|
||||
if (serial != img.serial) {
|
||||
serial = img.serial;
|
||||
|
||||
gl::ctx.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img.width, img.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, img.data);
|
||||
|
|
@ -815,7 +845,7 @@ void sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int tex
|
|||
|
||||
#ifndef NDEBUG
|
||||
auto status = gl::ctx.CheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if(status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
BOOST_LOG(error) << "Pass Cursor: CheckFramebufferStatus() --> [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return;
|
||||
}
|
||||
|
|
@ -829,9 +859,10 @@ void sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int tex
|
|||
gl::ctx.BindTexture(GL_TEXTURE_2D, 0);
|
||||
gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sws_t::convert(gl::frame_buf_t &fb) {
|
||||
int
|
||||
sws_t::convert(gl::frame_buf_t &fb) {
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, loaded_texture);
|
||||
|
||||
GLenum attachments[] {
|
||||
|
|
@ -839,13 +870,13 @@ int sws_t::convert(gl::frame_buf_t &fb) {
|
|||
GL_COLOR_ATTACHMENT1
|
||||
};
|
||||
|
||||
for(int x = 0; x < sizeof(attachments) / sizeof(decltype(attachments[0])); ++x) {
|
||||
for (int x = 0; x < sizeof(attachments) / sizeof(decltype(attachments[0])); ++x) {
|
||||
gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, fb[x]);
|
||||
gl::ctx.DrawBuffers(1, &attachments[x]);
|
||||
|
||||
#ifndef NDEBUG
|
||||
auto status = gl::ctx.CheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if(status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
BOOST_LOG(error) << "Pass "sv << x << ": CheckFramebufferStatus() --> [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -861,9 +892,10 @@ int sws_t::convert(gl::frame_buf_t &fb) {
|
|||
gl::ctx.Flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace egl
|
||||
|
||||
void free_frame(AVFrame *frame) {
|
||||
void
|
||||
free_frame(AVFrame *frame) {
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,46 +17,54 @@
|
|||
#define gl_drain_errors_helper(x) gl::drain_errors(x)
|
||||
#define gl_drain_errors gl_drain_errors_helper(__FILE__ ":" SUNSHINE_STRINGIFY(__LINE__))
|
||||
|
||||
extern "C" int close(int __fd);
|
||||
extern "C" int
|
||||
close(int __fd);
|
||||
|
||||
// X11 Display
|
||||
extern "C" struct _XDisplay;
|
||||
|
||||
struct AVFrame;
|
||||
void free_frame(AVFrame *frame);
|
||||
void
|
||||
free_frame(AVFrame *frame);
|
||||
|
||||
using frame_t = util::safe_ptr<AVFrame, free_frame>;
|
||||
|
||||
namespace gl {
|
||||
extern GladGLContext ctx;
|
||||
void drain_errors(const std::string_view &prefix);
|
||||
extern GladGLContext ctx;
|
||||
void
|
||||
drain_errors(const std::string_view &prefix);
|
||||
|
||||
class tex_t : public util::buffer_t<GLuint> {
|
||||
class tex_t: public util::buffer_t<GLuint> {
|
||||
using util::buffer_t<GLuint>::buffer_t;
|
||||
|
||||
public:
|
||||
public:
|
||||
tex_t(tex_t &&) = default;
|
||||
tex_t &operator=(tex_t &&) = default;
|
||||
tex_t &
|
||||
operator=(tex_t &&) = default;
|
||||
|
||||
~tex_t();
|
||||
|
||||
static tex_t make(std::size_t count);
|
||||
};
|
||||
static tex_t
|
||||
make(std::size_t count);
|
||||
};
|
||||
|
||||
class frame_buf_t : public util::buffer_t<GLuint> {
|
||||
class frame_buf_t: public util::buffer_t<GLuint> {
|
||||
using util::buffer_t<GLuint>::buffer_t;
|
||||
|
||||
public:
|
||||
public:
|
||||
frame_buf_t(frame_buf_t &&) = default;
|
||||
frame_buf_t &operator=(frame_buf_t &&) = default;
|
||||
frame_buf_t &
|
||||
operator=(frame_buf_t &&) = default;
|
||||
|
||||
~frame_buf_t();
|
||||
|
||||
static frame_buf_t make(std::size_t count);
|
||||
static frame_buf_t
|
||||
make(std::size_t count);
|
||||
|
||||
inline void bind(std::nullptr_t, std::nullptr_t) {
|
||||
inline void
|
||||
bind(std::nullptr_t, std::nullptr_t) {
|
||||
int x = 0;
|
||||
for(auto fb : (*this)) {
|
||||
for (auto fb : (*this)) {
|
||||
ctx.BindFramebuffer(GL_FRAMEBUFFER, fb);
|
||||
ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, 0, 0);
|
||||
|
||||
|
|
@ -65,10 +73,11 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
void bind(It it_begin, It it_end) {
|
||||
template <class It>
|
||||
void
|
||||
bind(It it_begin, It it_end) {
|
||||
using namespace std::literals;
|
||||
if(std::distance(it_begin, it_end) > size()) {
|
||||
if (std::distance(it_begin, it_end) > size()) {
|
||||
BOOST_LOG(warning) << "To many elements to bind"sv;
|
||||
return;
|
||||
}
|
||||
|
|
@ -87,45 +96,54 @@ public:
|
|||
/**
|
||||
* Copies a part of the framebuffer to texture
|
||||
*/
|
||||
void copy(int id, int texture, int offset_x, int offset_y, int width, int height);
|
||||
};
|
||||
void
|
||||
copy(int id, int texture, int offset_x, int offset_y, int width, int height);
|
||||
};
|
||||
|
||||
class shader_t {
|
||||
class shader_t {
|
||||
KITTY_USING_MOVE_T(shader_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
|
||||
if(el != std::numeric_limits<GLuint>::max()) {
|
||||
if (el != std::numeric_limits<GLuint>::max()) {
|
||||
ctx.DeleteShader(el);
|
||||
}
|
||||
});
|
||||
|
||||
public:
|
||||
std::string err_str();
|
||||
public:
|
||||
std::string
|
||||
err_str();
|
||||
|
||||
static util::Either<shader_t, std::string> compile(const std::string_view &source, GLenum type);
|
||||
static util::Either<shader_t, std::string>
|
||||
compile(const std::string_view &source, GLenum type);
|
||||
|
||||
GLuint handle() const;
|
||||
GLuint
|
||||
handle() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
shader_internal_t _shader;
|
||||
};
|
||||
};
|
||||
|
||||
class buffer_t {
|
||||
class buffer_t {
|
||||
KITTY_USING_MOVE_T(buffer_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
|
||||
if(el != std::numeric_limits<GLuint>::max()) {
|
||||
if (el != std::numeric_limits<GLuint>::max()) {
|
||||
ctx.DeleteBuffers(1, &el);
|
||||
}
|
||||
});
|
||||
|
||||
public:
|
||||
static buffer_t make(util::buffer_t<GLint> &&offsets, const char *block, const std::string_view &data);
|
||||
public:
|
||||
static buffer_t
|
||||
make(util::buffer_t<GLint> &&offsets, const char *block, const std::string_view &data);
|
||||
|
||||
GLuint handle() const;
|
||||
GLuint
|
||||
handle() const;
|
||||
|
||||
const char *block() const;
|
||||
const char *
|
||||
block() const;
|
||||
|
||||
void update(const std::string_view &view, std::size_t offset = 0);
|
||||
void update(std::string_view *members, std::size_t count, std::size_t offset = 0);
|
||||
void
|
||||
update(const std::string_view &view, std::size_t offset = 0);
|
||||
void
|
||||
update(std::string_view *members, std::size_t count, std::size_t offset = 0);
|
||||
|
||||
private:
|
||||
private:
|
||||
const char *_block;
|
||||
|
||||
std::size_t _size;
|
||||
|
|
@ -133,56 +151,62 @@ private:
|
|||
util::buffer_t<GLint> _offsets;
|
||||
|
||||
buffer_internal_t _buffer;
|
||||
};
|
||||
};
|
||||
|
||||
class program_t {
|
||||
class program_t {
|
||||
KITTY_USING_MOVE_T(program_internal_t, GLuint, std::numeric_limits<GLuint>::max(), {
|
||||
if(el != std::numeric_limits<GLuint>::max()) {
|
||||
if (el != std::numeric_limits<GLuint>::max()) {
|
||||
ctx.DeleteProgram(el);
|
||||
}
|
||||
});
|
||||
|
||||
public:
|
||||
std::string err_str();
|
||||
public:
|
||||
std::string
|
||||
err_str();
|
||||
|
||||
static util::Either<program_t, std::string> link(const shader_t &vert, const shader_t &frag);
|
||||
static util::Either<program_t, std::string>
|
||||
link(const shader_t &vert, const shader_t &frag);
|
||||
|
||||
void bind(const buffer_t &buffer);
|
||||
void
|
||||
bind(const buffer_t &buffer);
|
||||
|
||||
std::optional<buffer_t> uniform(const char *block, std::pair<const char *, std::string_view> *members, std::size_t count);
|
||||
std::optional<buffer_t>
|
||||
uniform(const char *block, std::pair<const char *, std::string_view> *members, std::size_t count);
|
||||
|
||||
GLuint handle() const;
|
||||
GLuint
|
||||
handle() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
program_internal_t _program;
|
||||
};
|
||||
};
|
||||
} // namespace gl
|
||||
|
||||
namespace gbm {
|
||||
struct device;
|
||||
typedef void (*device_destroy_fn)(device *gbm);
|
||||
typedef device *(*create_device_fn)(int fd);
|
||||
struct device;
|
||||
typedef void (*device_destroy_fn)(device *gbm);
|
||||
typedef device *(*create_device_fn)(int fd);
|
||||
|
||||
extern device_destroy_fn device_destroy;
|
||||
extern create_device_fn create_device;
|
||||
extern device_destroy_fn device_destroy;
|
||||
extern create_device_fn create_device;
|
||||
|
||||
using gbm_t = util::dyn_safe_ptr<device, &device_destroy>;
|
||||
using gbm_t = util::dyn_safe_ptr<device, &device_destroy>;
|
||||
|
||||
int init();
|
||||
int
|
||||
init();
|
||||
|
||||
} // namespace gbm
|
||||
|
||||
namespace egl {
|
||||
using display_t = util::dyn_safe_ptr_v2<void, EGLBoolean, &eglTerminate>;
|
||||
using display_t = util::dyn_safe_ptr_v2<void, EGLBoolean, &eglTerminate>;
|
||||
|
||||
struct rgb_img_t {
|
||||
struct rgb_img_t {
|
||||
display_t::pointer display;
|
||||
EGLImage xrgb8;
|
||||
|
||||
gl::tex_t tex;
|
||||
};
|
||||
};
|
||||
|
||||
struct nv12_img_t {
|
||||
struct nv12_img_t {
|
||||
display_t::pointer display;
|
||||
EGLImage r8;
|
||||
EGLImage bg88;
|
||||
|
|
@ -194,33 +218,33 @@ struct nv12_img_t {
|
|||
static constexpr std::size_t num_fds = 4;
|
||||
|
||||
std::array<file_t, num_fds> fds;
|
||||
};
|
||||
};
|
||||
|
||||
KITTY_USING_MOVE_T(rgb_t, rgb_img_t, , {
|
||||
if(el.xrgb8) {
|
||||
KITTY_USING_MOVE_T(rgb_t, rgb_img_t, , {
|
||||
if (el.xrgb8) {
|
||||
eglDestroyImage(el.display, el.xrgb8);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
KITTY_USING_MOVE_T(nv12_t, nv12_img_t, , {
|
||||
if(el.r8) {
|
||||
KITTY_USING_MOVE_T(nv12_t, nv12_img_t, , {
|
||||
if (el.r8) {
|
||||
eglDestroyImage(el.display, el.r8);
|
||||
}
|
||||
|
||||
if(el.bg88) {
|
||||
if (el.bg88) {
|
||||
eglDestroyImage(el.display, el.bg88);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
KITTY_USING_MOVE_T(ctx_t, (std::tuple<display_t::pointer, EGLContext>), , {
|
||||
KITTY_USING_MOVE_T(ctx_t, (std::tuple<display_t::pointer, EGLContext>), , {
|
||||
TUPLE_2D_REF(disp, ctx, el);
|
||||
if(ctx) {
|
||||
if (ctx) {
|
||||
eglMakeCurrent(disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroyContext(disp, ctx);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
struct surface_descriptor_t {
|
||||
struct surface_descriptor_t {
|
||||
int width;
|
||||
int height;
|
||||
int fds[4];
|
||||
|
|
@ -228,39 +252,44 @@ struct surface_descriptor_t {
|
|||
std::uint64_t modifier;
|
||||
std::uint32_t pitches[4];
|
||||
std::uint32_t offsets[4];
|
||||
};
|
||||
};
|
||||
|
||||
display_t make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay *> native_display);
|
||||
std::optional<ctx_t> make_ctx(display_t::pointer display);
|
||||
display_t
|
||||
make_display(std::variant<gbm::gbm_t::pointer, wl_display *, _XDisplay *> native_display);
|
||||
std::optional<ctx_t>
|
||||
make_ctx(display_t::pointer display);
|
||||
|
||||
std::optional<rgb_t> import_source(
|
||||
std::optional<rgb_t>
|
||||
import_source(
|
||||
display_t::pointer egl_display,
|
||||
const surface_descriptor_t &xrgb);
|
||||
|
||||
std::optional<nv12_t> import_target(
|
||||
std::optional<nv12_t>
|
||||
import_target(
|
||||
display_t::pointer egl_display,
|
||||
std::array<file_t, nv12_img_t::num_fds> &&fds,
|
||||
const surface_descriptor_t &r8, const surface_descriptor_t &gr88);
|
||||
|
||||
class cursor_t : public platf::img_t {
|
||||
public:
|
||||
class cursor_t: public platf::img_t {
|
||||
public:
|
||||
int x, y;
|
||||
|
||||
unsigned long serial;
|
||||
|
||||
std::vector<std::uint8_t> buffer;
|
||||
};
|
||||
};
|
||||
|
||||
// Allow cursor and the underlying image to be kept together
|
||||
class img_descriptor_t : public cursor_t {
|
||||
public:
|
||||
// Allow cursor and the underlying image to be kept together
|
||||
class img_descriptor_t: public cursor_t {
|
||||
public:
|
||||
~img_descriptor_t() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for(auto x = 0; x < 4; ++x) {
|
||||
if(sd.fds[x] >= 0) {
|
||||
void
|
||||
reset() {
|
||||
for (auto x = 0; x < 4; ++x) {
|
||||
if (sd.fds[x] >= 0) {
|
||||
close(sd.fds[x]);
|
||||
|
||||
sd.fds[x] = -1;
|
||||
|
|
@ -272,23 +301,30 @@ public:
|
|||
|
||||
// Increment sequence when new rgb_t needs to be created
|
||||
std::uint64_t sequence;
|
||||
};
|
||||
};
|
||||
|
||||
class sws_t {
|
||||
public:
|
||||
static std::optional<sws_t> make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex);
|
||||
static std::optional<sws_t> make(int in_width, int in_height, int out_width, int out_heigth);
|
||||
class sws_t {
|
||||
public:
|
||||
static std::optional<sws_t>
|
||||
make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex);
|
||||
static std::optional<sws_t>
|
||||
make(int in_width, int in_height, int out_width, int out_heigth);
|
||||
|
||||
// Convert the loaded image into the first two framebuffers
|
||||
int convert(gl::frame_buf_t &fb);
|
||||
int
|
||||
convert(gl::frame_buf_t &fb);
|
||||
|
||||
// Make an area of the image black
|
||||
int blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height);
|
||||
int
|
||||
blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height);
|
||||
|
||||
void load_ram(platf::img_t &img);
|
||||
void load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture);
|
||||
void
|
||||
load_ram(platf::img_t &img);
|
||||
void
|
||||
load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture);
|
||||
|
||||
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range);
|
||||
void
|
||||
set_colorspace(std::uint32_t colorspace, std::uint32_t color_range);
|
||||
|
||||
// The first texture is the monitor image.
|
||||
// The second texture is the cursor image
|
||||
|
|
@ -311,9 +347,10 @@ public:
|
|||
|
||||
// Store latest cursor for load_vram
|
||||
std::uint64_t serial;
|
||||
};
|
||||
};
|
||||
|
||||
bool fail();
|
||||
bool
|
||||
fail();
|
||||
} // namespace egl
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -25,9 +25,9 @@
|
|||
#include "vaapi.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define SUNSHINE_GNUC_EXTENSION __extension__
|
||||
#define SUNSHINE_GNUC_EXTENSION __extension__
|
||||
#else
|
||||
#define SUNSHINE_GNUC_EXTENSION
|
||||
#define SUNSHINE_GNUC_EXTENSION
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
|
|
@ -37,12 +37,13 @@ namespace bp = boost::process;
|
|||
window_system_e window_system;
|
||||
|
||||
namespace dyn {
|
||||
void *handle(const std::vector<const char *> &libs) {
|
||||
void *
|
||||
handle(const std::vector<const char *> &libs) {
|
||||
void *handle;
|
||||
|
||||
for(auto lib : libs) {
|
||||
for (auto lib : libs) {
|
||||
handle = dlopen(lib, RTLD_LAZY | RTLD_LOCAL);
|
||||
if(handle) {
|
||||
if (handle) {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
|
@ -58,16 +59,17 @@ void *handle(const std::vector<const char *> &libs) {
|
|||
BOOST_LOG(error) << ss.str();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict) {
|
||||
int
|
||||
load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict) {
|
||||
int err = 0;
|
||||
for(auto &func : funcs) {
|
||||
for (auto &func : funcs) {
|
||||
TUPLE_2D_REF(fn, name, func);
|
||||
|
||||
*fn = SUNSHINE_GNUC_EXTENSION(apiproc) dlsym(handle, name);
|
||||
|
||||
if(!*fn && strict) {
|
||||
if (!*fn && strict) {
|
||||
BOOST_LOG(error) << "Couldn't find function: "sv << name;
|
||||
|
||||
err = -1;
|
||||
|
|
@ -75,71 +77,76 @@ int load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &f
|
|||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} // namespace dyn
|
||||
namespace platf {
|
||||
using ifaddr_t = util::safe_ptr<ifaddrs, freeifaddrs>;
|
||||
using ifaddr_t = util::safe_ptr<ifaddrs, freeifaddrs>;
|
||||
|
||||
ifaddr_t get_ifaddrs() {
|
||||
ifaddr_t
|
||||
get_ifaddrs() {
|
||||
ifaddrs *p { nullptr };
|
||||
|
||||
getifaddrs(&p);
|
||||
|
||||
return ifaddr_t { p };
|
||||
}
|
||||
}
|
||||
|
||||
fs::path appdata() {
|
||||
fs::path
|
||||
appdata() {
|
||||
const char *homedir;
|
||||
if((homedir = getenv("HOME")) == nullptr) {
|
||||
if ((homedir = getenv("HOME")) == nullptr) {
|
||||
homedir = getpwuid(geteuid())->pw_dir;
|
||||
}
|
||||
|
||||
return fs::path { homedir } / ".config/sunshine"sv;
|
||||
}
|
||||
}
|
||||
|
||||
std::string from_sockaddr(const sockaddr *const ip_addr) {
|
||||
std::string
|
||||
from_sockaddr(const sockaddr *const ip_addr) {
|
||||
char data[INET6_ADDRSTRLEN];
|
||||
|
||||
auto family = ip_addr->sa_family;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data,
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data,
|
||||
if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
return std::string { data };
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
std::pair<std::uint16_t, std::string>
|
||||
from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
char data[INET6_ADDRSTRLEN];
|
||||
|
||||
auto family = ip_addr->sa_family;
|
||||
std::uint16_t port;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data,
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6 *)ip_addr)->sin6_port;
|
||||
port = ((sockaddr_in6 *) ip_addr)->sin6_port;
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data,
|
||||
if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in *)ip_addr)->sin_port;
|
||||
port = ((sockaddr_in *) ip_addr)->sin_port;
|
||||
}
|
||||
|
||||
return { port, std::string { data } };
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_mac_address(const std::string_view &address) {
|
||||
std::string
|
||||
get_mac_address(const std::string_view &address) {
|
||||
auto ifaddrs = get_ifaddrs();
|
||||
for(auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
|
||||
if(pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
|
||||
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
|
||||
if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
|
||||
std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address");
|
||||
if(mac_file.good()) {
|
||||
if (mac_file.good()) {
|
||||
std::string mac_address;
|
||||
std::getline(mac_file, mac_address);
|
||||
return mac_address;
|
||||
|
|
@ -149,12 +156,13 @@ std::string get_mac_address(const std::string_view &address) {
|
|||
|
||||
BOOST_LOG(warning) << "Unable to find MAC address for "sv << address;
|
||||
return "00:00:00:00:00:00"s;
|
||||
}
|
||||
}
|
||||
|
||||
bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
bp::child
|
||||
run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
BOOST_LOG(warning) << "run_unprivileged() is not yet implemented for this platform. The new process will run with Sunshine's permissions."sv;
|
||||
if(!group) {
|
||||
if(!file) {
|
||||
if (!group) {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec);
|
||||
}
|
||||
else {
|
||||
|
|
@ -162,46 +170,52 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
}
|
||||
}
|
||||
else {
|
||||
if(!file) {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec, *group);
|
||||
}
|
||||
else {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec, *group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adjust_thread_priority(thread_priority_e priority) {
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority) {
|
||||
// Unimplemented
|
||||
}
|
||||
}
|
||||
|
||||
void streaming_will_start() {
|
||||
void
|
||||
streaming_will_start() {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
void streaming_will_stop() {
|
||||
void
|
||||
streaming_will_stop() {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
bool restart_supported() {
|
||||
bool
|
||||
restart_supported() {
|
||||
// Restart not supported yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool restart() {
|
||||
bool
|
||||
restart() {
|
||||
// Restart not supported yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool send_batch(batched_send_info_t &send_info) {
|
||||
auto sockfd = (int)send_info.native_socket;
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info) {
|
||||
auto sockfd = (int) send_info.native_socket;
|
||||
|
||||
// Convert the target address into a sockaddr
|
||||
struct sockaddr_in saddr_v4 = {};
|
||||
struct sockaddr_in6 saddr_v6 = {};
|
||||
struct sockaddr *addr;
|
||||
socklen_t addr_len;
|
||||
if(send_info.target_address.is_v6()) {
|
||||
if (send_info.target_address.is_v6()) {
|
||||
auto address_v6 = send_info.target_address.to_v6();
|
||||
|
||||
saddr_v6.sin6_family = AF_INET6;
|
||||
|
|
@ -211,7 +225,7 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
auto addr_bytes = address_v6.to_bytes();
|
||||
memcpy(&saddr_v6.sin6_addr, addr_bytes.data(), sizeof(saddr_v6.sin6_addr));
|
||||
|
||||
addr = (struct sockaddr *)&saddr_v6;
|
||||
addr = (struct sockaddr *) &saddr_v6;
|
||||
addr_len = sizeof(saddr_v6);
|
||||
}
|
||||
else {
|
||||
|
|
@ -223,7 +237,7 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
auto addr_bytes = address_v4.to_bytes();
|
||||
memcpy(&saddr_v4.sin_addr, addr_bytes.data(), sizeof(saddr_v4.sin_addr));
|
||||
|
||||
addr = (struct sockaddr *)&saddr_v4;
|
||||
addr = (struct sockaddr *) &saddr_v4;
|
||||
addr_len = sizeof(saddr_v4);
|
||||
}
|
||||
|
||||
|
|
@ -239,8 +253,8 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
// UDP GSO on Linux currently only supports sending 64K or 64 segments at a time
|
||||
size_t seg_index = 0;
|
||||
const size_t seg_max = 65536 / 1500;
|
||||
while(seg_index < send_info.block_count) {
|
||||
iov.iov_base = (void *)&send_info.buffer[seg_index * send_info.block_size];
|
||||
while (seg_index < send_info.block_count) {
|
||||
iov.iov_base = (void *) &send_info.buffer[seg_index * send_info.block_size];
|
||||
iov.iov_len = send_info.block_size * std::min(send_info.block_count - seg_index, seg_max);
|
||||
|
||||
msg.msg_name = addr;
|
||||
|
|
@ -249,7 +263,7 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
msg.msg_iovlen = 1;
|
||||
|
||||
// We should not use GSO if the data is <= one full block size
|
||||
if(iov.iov_len > send_info.block_size) {
|
||||
if (iov.iov_len > send_info.block_size) {
|
||||
msg.msg_control = cmbuf.buf;
|
||||
msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
|
||||
|
||||
|
|
@ -258,7 +272,7 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
cm->cmsg_level = SOL_UDP;
|
||||
cm->cmsg_type = UDP_SEGMENT;
|
||||
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
|
||||
*((uint16_t *)CMSG_DATA(cm)) = send_info.block_size;
|
||||
*((uint16_t *) CMSG_DATA(cm)) = send_info.block_size;
|
||||
}
|
||||
else {
|
||||
msg.msg_control = nullptr;
|
||||
|
|
@ -269,15 +283,15 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
// it's the first sendmsg() call. On subsequent calls, we will treat errors as
|
||||
// actual failures and return to the caller.
|
||||
auto bytes_sent = sendmsg(sockfd, &msg, 0);
|
||||
if(bytes_sent < 0) {
|
||||
if (bytes_sent < 0) {
|
||||
// If there's no send buffer space, wait for some to be available
|
||||
if(errno == EAGAIN) {
|
||||
if (errno == EAGAIN) {
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = sockfd;
|
||||
pfd.events = POLLOUT;
|
||||
|
||||
if(poll(&pfd, 1, -1) != 1) {
|
||||
if (poll(&pfd, 1, -1) != 1) {
|
||||
BOOST_LOG(warning) << "poll() failed: "sv << errno;
|
||||
break;
|
||||
}
|
||||
|
|
@ -293,7 +307,7 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
}
|
||||
|
||||
// If we sent something, return the status and don't fall back to the non-GSO path.
|
||||
if(seg_index != 0) {
|
||||
if (seg_index != 0) {
|
||||
return seg_index >= send_info.block_count;
|
||||
}
|
||||
}
|
||||
|
|
@ -303,9 +317,9 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
// If GSO is not supported, use sendmmsg() instead.
|
||||
struct mmsghdr msgs[send_info.block_count];
|
||||
struct iovec iovs[send_info.block_count];
|
||||
for(size_t i = 0; i < send_info.block_count; i++) {
|
||||
for (size_t i = 0; i < send_info.block_count; i++) {
|
||||
iovs[i] = {};
|
||||
iovs[i].iov_base = (void *)&send_info.buffer[i * send_info.block_size];
|
||||
iovs[i].iov_base = (void *) &send_info.buffer[i * send_info.block_size];
|
||||
iovs[i].iov_len = send_info.block_size;
|
||||
|
||||
msgs[i] = {};
|
||||
|
|
@ -317,17 +331,17 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
|
||||
// Call sendmmsg() until all messages are sent
|
||||
size_t blocks_sent = 0;
|
||||
while(blocks_sent < send_info.block_count) {
|
||||
while (blocks_sent < send_info.block_count) {
|
||||
int msgs_sent = sendmmsg(sockfd, &msgs[blocks_sent], send_info.block_count - blocks_sent, 0);
|
||||
if(msgs_sent < 0) {
|
||||
if (msgs_sent < 0) {
|
||||
// If there's no send buffer space, wait for some to be available
|
||||
if(errno == EAGAIN) {
|
||||
if (errno == EAGAIN) {
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = sockfd;
|
||||
pfd.events = POLLOUT;
|
||||
|
||||
if(poll(&pfd, 1, -1) != 1) {
|
||||
if (poll(&pfd, 1, -1) != 1) {
|
||||
BOOST_LOG(warning) << "poll() failed: "sv << errno;
|
||||
break;
|
||||
}
|
||||
|
|
@ -345,31 +359,33 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class qos_t : public deinit_t {
|
||||
public:
|
||||
qos_t(int sockfd, int level, int option) : sockfd(sockfd), level(level), option(option) {}
|
||||
class qos_t: public deinit_t {
|
||||
public:
|
||||
qos_t(int sockfd, int level, int option):
|
||||
sockfd(sockfd), level(level), option(option) {}
|
||||
|
||||
virtual ~qos_t() {
|
||||
int reset_val = -1;
|
||||
if(setsockopt(sockfd, level, option, &reset_val, sizeof(reset_val)) < 0) {
|
||||
if (setsockopt(sockfd, level, option, &reset_val, sizeof(reset_val)) < 0) {
|
||||
BOOST_LOG(warning) << "Failed to reset IP TOS: "sv << errno;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
int sockfd;
|
||||
int level;
|
||||
int option;
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type) {
|
||||
int sockfd = (int)native_socket;
|
||||
std::unique_ptr<deinit_t>
|
||||
enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type) {
|
||||
int sockfd = (int) native_socket;
|
||||
|
||||
int level;
|
||||
int option;
|
||||
if(address.is_v6()) {
|
||||
if (address.is_v6()) {
|
||||
level = SOL_IPV6;
|
||||
option = IPV6_TCLASS;
|
||||
}
|
||||
|
|
@ -380,7 +396,7 @@ std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio
|
|||
|
||||
// The specific DSCP values here are chosen to be consistent with Windows
|
||||
int dscp;
|
||||
switch(data_type) {
|
||||
switch (data_type) {
|
||||
case qos_data_type_e::video:
|
||||
dscp = 40;
|
||||
break;
|
||||
|
|
@ -388,22 +404,22 @@ std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio
|
|||
dscp = 56;
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unknown traffic type: "sv << (int)data_type;
|
||||
BOOST_LOG(error) << "Unknown traffic type: "sv << (int) data_type;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Shift to put the DSCP value in the correct position in the TOS field
|
||||
dscp <<= 2;
|
||||
|
||||
if(setsockopt(sockfd, level, option, &dscp, sizeof(dscp)) < 0) {
|
||||
if (setsockopt(sockfd, level, option, &dscp, sizeof(dscp)) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<qos_t>(sockfd, level, option);
|
||||
}
|
||||
}
|
||||
|
||||
namespace source {
|
||||
enum source_e : std::size_t {
|
||||
namespace source {
|
||||
enum source_e : std::size_t {
|
||||
#ifdef SUNSHINE_BUILD_CUDA
|
||||
NVFBC,
|
||||
#endif
|
||||
|
|
@ -417,107 +433,122 @@ enum source_e : std::size_t {
|
|||
X11,
|
||||
#endif
|
||||
MAX_FLAGS
|
||||
};
|
||||
} // namespace source
|
||||
};
|
||||
} // namespace source
|
||||
|
||||
static std::bitset<source::MAX_FLAGS> sources;
|
||||
static std::bitset<source::MAX_FLAGS> sources;
|
||||
|
||||
#ifdef SUNSHINE_BUILD_CUDA
|
||||
std::vector<std::string> nvfbc_display_names();
|
||||
std::shared_ptr<display_t> nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
std::vector<std::string>
|
||||
nvfbc_display_names();
|
||||
std::shared_ptr<display_t>
|
||||
nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
|
||||
bool verify_nvfbc() {
|
||||
bool
|
||||
verify_nvfbc() {
|
||||
return !nvfbc_display_names().empty();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||
std::vector<std::string> wl_display_names();
|
||||
std::shared_ptr<display_t> wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
std::vector<std::string>
|
||||
wl_display_names();
|
||||
std::shared_ptr<display_t>
|
||||
wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
|
||||
bool verify_wl() {
|
||||
bool
|
||||
verify_wl() {
|
||||
return window_system == window_system_e::WAYLAND && !wl_display_names().empty();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SUNSHINE_BUILD_DRM
|
||||
std::vector<std::string> kms_display_names();
|
||||
std::shared_ptr<display_t> kms_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
std::vector<std::string>
|
||||
kms_display_names();
|
||||
std::shared_ptr<display_t>
|
||||
kms_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
|
||||
bool verify_kms() {
|
||||
bool
|
||||
verify_kms() {
|
||||
return !kms_display_names().empty();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SUNSHINE_BUILD_X11
|
||||
std::vector<std::string> x11_display_names();
|
||||
std::shared_ptr<display_t> x11_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
std::vector<std::string>
|
||||
x11_display_names();
|
||||
std::shared_ptr<display_t>
|
||||
x11_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config);
|
||||
|
||||
bool verify_x11() {
|
||||
bool
|
||||
verify_x11() {
|
||||
return window_system == window_system_e::X11 && !x11_display_names().empty();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<std::string> display_names(mem_type_e hwdevice_type) {
|
||||
std::vector<std::string>
|
||||
display_names(mem_type_e hwdevice_type) {
|
||||
#ifdef SUNSHINE_BUILD_CUDA
|
||||
// display using NvFBC only supports mem_type_e::cuda
|
||||
if(sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) return nvfbc_display_names();
|
||||
if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) return nvfbc_display_names();
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||
if(sources[source::WAYLAND]) return wl_display_names();
|
||||
if (sources[source::WAYLAND]) return wl_display_names();
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_DRM
|
||||
if(sources[source::KMS]) return kms_display_names();
|
||||
if (sources[source::KMS]) return kms_display_names();
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_X11
|
||||
if(sources[source::X11]) return x11_display_names();
|
||||
if (sources[source::X11]) return x11_display_names();
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<display_t> display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
std::shared_ptr<display_t>
|
||||
display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
#ifdef SUNSHINE_BUILD_CUDA
|
||||
if(sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) {
|
||||
if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) {
|
||||
BOOST_LOG(info) << "Screencasting with NvFBC"sv;
|
||||
return nvfbc_display(hwdevice_type, display_name, config);
|
||||
}
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||
if(sources[source::WAYLAND]) {
|
||||
if (sources[source::WAYLAND]) {
|
||||
BOOST_LOG(info) << "Screencasting with Wayland's protocol"sv;
|
||||
return wl_display(hwdevice_type, display_name, config);
|
||||
}
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_DRM
|
||||
if(sources[source::KMS]) {
|
||||
if (sources[source::KMS]) {
|
||||
BOOST_LOG(info) << "Screencasting with KMS"sv;
|
||||
return kms_display(hwdevice_type, display_name, config);
|
||||
}
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_X11
|
||||
if(sources[source::X11]) {
|
||||
if (sources[source::X11]) {
|
||||
BOOST_LOG(info) << "Screencasting with X11"sv;
|
||||
return x11_display(hwdevice_type, display_name, config);
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<deinit_t> init() {
|
||||
std::unique_ptr<deinit_t>
|
||||
init() {
|
||||
// These are allowed to fail.
|
||||
gbm::init();
|
||||
va::init();
|
||||
|
||||
window_system = window_system_e::NONE;
|
||||
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||
if(std::getenv("WAYLAND_DISPLAY")) {
|
||||
if (std::getenv("WAYLAND_DISPLAY")) {
|
||||
window_system = window_system_e::WAYLAND;
|
||||
}
|
||||
#endif
|
||||
#if defined(SUNSHINE_BUILD_X11) || defined(SUNSHINE_BUILD_CUDA)
|
||||
if(std::getenv("DISPLAY") && window_system != window_system_e::WAYLAND) {
|
||||
if(std::getenv("WAYLAND_DISPLAY")) {
|
||||
if (std::getenv("DISPLAY") && window_system != window_system_e::WAYLAND) {
|
||||
if (std::getenv("WAYLAND_DISPLAY")) {
|
||||
BOOST_LOG(warning) << "Wayland detected, yet sunshine will use X11 for screencasting, screencasting will only work on XWayland applications"sv;
|
||||
}
|
||||
|
||||
|
|
@ -526,23 +557,23 @@ std::unique_ptr<deinit_t> init() {
|
|||
#endif
|
||||
|
||||
#ifdef SUNSHINE_BUILD_CUDA
|
||||
if(config::video.capture.empty() || config::video.capture == "nvfbc") {
|
||||
if(verify_nvfbc()) {
|
||||
if (config::video.capture.empty() || config::video.capture == "nvfbc") {
|
||||
if (verify_nvfbc()) {
|
||||
sources[source::NVFBC] = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||
if(config::video.capture.empty() || config::video.capture == "wlr") {
|
||||
if(verify_wl()) {
|
||||
if (config::video.capture.empty() || config::video.capture == "wlr") {
|
||||
if (verify_wl()) {
|
||||
sources[source::WAYLAND] = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_DRM
|
||||
if(config::video.capture.empty() || config::video.capture == "kms") {
|
||||
if(verify_kms()) {
|
||||
if(window_system == window_system_e::WAYLAND) {
|
||||
if (config::video.capture.empty() || config::video.capture == "kms") {
|
||||
if (verify_kms()) {
|
||||
if (window_system == window_system_e::WAYLAND) {
|
||||
// On Wayland, using KMS, the cursor is unreliable.
|
||||
// Hide it by default
|
||||
display_cursor = false;
|
||||
|
|
@ -553,22 +584,22 @@ std::unique_ptr<deinit_t> init() {
|
|||
}
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_X11
|
||||
if(config::video.capture.empty() || config::video.capture == "x11") {
|
||||
if(verify_x11()) {
|
||||
if (config::video.capture.empty() || config::video.capture == "x11") {
|
||||
if (verify_x11()) {
|
||||
sources[source::X11] = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(sources.none()) {
|
||||
if (sources.none()) {
|
||||
BOOST_LOG(error) << "Unable to initialize capture method"sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!gladLoaderLoadEGL(EGL_NO_DISPLAY) || !eglGetPlatformDisplay) {
|
||||
if (!gladLoaderLoadEGL(EGL_NO_DISPLAY) || !eglGetPlatformDisplay) {
|
||||
BOOST_LOG(warning) << "Couldn't load EGL library"sv;
|
||||
}
|
||||
|
||||
return std::make_unique<deinit_t>();
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include "src/utility.h"
|
||||
|
||||
KITTY_USING_MOVE_T(file_t, int, -1, {
|
||||
if(el >= 0) {
|
||||
if (el >= 0) {
|
||||
close(el);
|
||||
}
|
||||
});
|
||||
|
|
@ -21,10 +21,12 @@ enum class window_system_e {
|
|||
extern window_system_e window_system;
|
||||
|
||||
namespace dyn {
|
||||
typedef void (*apiproc)(void);
|
||||
typedef void (*apiproc)(void);
|
||||
|
||||
int load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict = true);
|
||||
void *handle(const std::vector<const char *> &libs);
|
||||
int
|
||||
load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict = true);
|
||||
void *
|
||||
handle(const std::vector<const char *> &libs);
|
||||
|
||||
} // namespace dyn
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ using namespace std::literals;
|
|||
|
||||
namespace avahi {
|
||||
|
||||
/** Error codes used by avahi */
|
||||
enum err_e {
|
||||
/** Error codes used by avahi */
|
||||
enum err_e {
|
||||
OK = 0, /**< OK */
|
||||
ERR_FAILURE = -1, /**< Generic error code */
|
||||
ERR_BAD_STATE = -2, /**< Object was in a bad state */
|
||||
|
|
@ -75,46 +75,46 @@ enum err_e {
|
|||
ERR_NO_CHANGE = -53, /**< The requested operation is invalid because it is redundant */
|
||||
|
||||
ERR_MAX = -54
|
||||
};
|
||||
};
|
||||
|
||||
constexpr auto IF_UNSPEC = -1;
|
||||
enum proto {
|
||||
constexpr auto IF_UNSPEC = -1;
|
||||
enum proto {
|
||||
PROTO_INET = 0, /**< IPv4 */
|
||||
PROTO_INET6 = 1, /**< IPv6 */
|
||||
PROTO_UNSPEC = -1 /**< Unspecified/all protocol(s) */
|
||||
};
|
||||
};
|
||||
|
||||
enum ServerState {
|
||||
enum ServerState {
|
||||
SERVER_INVALID, /**< Invalid state (initial) */
|
||||
SERVER_REGISTERING, /**< Host RRs are being registered */
|
||||
SERVER_RUNNING, /**< All host RRs have been established */
|
||||
SERVER_COLLISION, /**< There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */
|
||||
SERVER_FAILURE /**< Some fatal failure happened, the server is unable to proceed */
|
||||
};
|
||||
};
|
||||
|
||||
enum ClientState {
|
||||
enum ClientState {
|
||||
CLIENT_S_REGISTERING = SERVER_REGISTERING, /**< Server state: REGISTERING */
|
||||
CLIENT_S_RUNNING = SERVER_RUNNING, /**< Server state: RUNNING */
|
||||
CLIENT_S_COLLISION = SERVER_COLLISION, /**< Server state: COLLISION */
|
||||
CLIENT_FAILURE = 100, /**< Some kind of error happened on the client side */
|
||||
CLIENT_CONNECTING = 101 /**< We're still connecting. This state is only entered when AVAHI_CLIENT_NO_FAIL has been passed to avahi_client_new() and the daemon is not yet available. */
|
||||
};
|
||||
};
|
||||
|
||||
enum EntryGroupState {
|
||||
enum EntryGroupState {
|
||||
ENTRY_GROUP_UNCOMMITED, /**< The group has not yet been commited, the user must still call avahi_entry_group_commit() */
|
||||
ENTRY_GROUP_REGISTERING, /**< The entries of the group are currently being registered */
|
||||
ENTRY_GROUP_ESTABLISHED, /**< The entries have successfully been established */
|
||||
ENTRY_GROUP_COLLISION, /**< A name collision for one of the entries in the group has been detected, the entries have been withdrawn */
|
||||
ENTRY_GROUP_FAILURE /**< Some kind of failure happened, the entries have been withdrawn */
|
||||
};
|
||||
};
|
||||
|
||||
enum ClientFlags {
|
||||
enum ClientFlags {
|
||||
CLIENT_IGNORE_USER_CONFIG = 1, /**< Don't read user configuration */
|
||||
CLIENT_NO_FAIL = 2 /**< Don't fail if the daemon is not available when avahi_client_new() is called, instead enter CLIENT_CONNECTING state and wait for the daemon to appear */
|
||||
};
|
||||
};
|
||||
|
||||
/** Some flags for publishing functions */
|
||||
enum PublishFlags {
|
||||
/** Some flags for publishing functions */
|
||||
enum PublishFlags {
|
||||
PUBLISH_UNIQUE = 1, /**< For raw records: The RRset is intended to be unique */
|
||||
PUBLISH_NO_PROBE = 2, /**< For raw records: Though the RRset is intended to be unique no probes shall be sent */
|
||||
PUBLISH_NO_ANNOUNCE = 4, /**< For raw records: Do not announce this RR to other hosts */
|
||||
|
|
@ -128,29 +128,29 @@ enum PublishFlags {
|
|||
PUBLISH_USE_WIDE_AREA = 128, /**< Register the record using wide area DNS (i.e. unicast DNS update) */
|
||||
PUBLISH_USE_MULTICAST = 256 /**< Register the record using multicast DNS */
|
||||
/** \endcond */
|
||||
};
|
||||
};
|
||||
|
||||
using IfIndex = int;
|
||||
using Protocol = int;
|
||||
using IfIndex = int;
|
||||
using Protocol = int;
|
||||
|
||||
struct EntryGroup;
|
||||
struct Poll;
|
||||
struct SimplePoll;
|
||||
struct Client;
|
||||
struct EntryGroup;
|
||||
struct Poll;
|
||||
struct SimplePoll;
|
||||
struct Client;
|
||||
|
||||
typedef void (*ClientCallback)(Client *, ClientState, void *userdata);
|
||||
typedef void (*EntryGroupCallback)(EntryGroup *g, EntryGroupState state, void *userdata);
|
||||
typedef void (*ClientCallback)(Client *, ClientState, void *userdata);
|
||||
typedef void (*EntryGroupCallback)(EntryGroup *g, EntryGroupState state, void *userdata);
|
||||
|
||||
typedef void (*free_fn)(void *);
|
||||
typedef void (*free_fn)(void *);
|
||||
|
||||
typedef Client *(*client_new_fn)(const Poll *poll_api, ClientFlags flags, ClientCallback callback, void *userdata, int *error);
|
||||
typedef void (*client_free_fn)(Client *);
|
||||
typedef char *(*alternative_service_name_fn)(char *);
|
||||
typedef Client *(*client_new_fn)(const Poll *poll_api, ClientFlags flags, ClientCallback callback, void *userdata, int *error);
|
||||
typedef void (*client_free_fn)(Client *);
|
||||
typedef char *(*alternative_service_name_fn)(char *);
|
||||
|
||||
typedef Client *(*entry_group_get_client_fn)(EntryGroup *);
|
||||
typedef Client *(*entry_group_get_client_fn)(EntryGroup *);
|
||||
|
||||
typedef EntryGroup *(*entry_group_new_fn)(Client *, EntryGroupCallback, void *userdata);
|
||||
typedef int (*entry_group_add_service_fn)(
|
||||
typedef EntryGroup *(*entry_group_new_fn)(Client *, EntryGroupCallback, void *userdata);
|
||||
typedef int (*entry_group_add_service_fn)(
|
||||
EntryGroup *group,
|
||||
IfIndex interface,
|
||||
Protocol protocol,
|
||||
|
|
@ -162,136 +162,140 @@ typedef int (*entry_group_add_service_fn)(
|
|||
uint16_t port,
|
||||
...);
|
||||
|
||||
typedef int (*entry_group_is_empty_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_reset_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_commit_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_is_empty_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_reset_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_commit_fn)(EntryGroup *);
|
||||
|
||||
typedef char *(*strdup_fn)(const char *);
|
||||
typedef char *(*strerror_fn)(int);
|
||||
typedef int (*client_errno_fn)(Client *);
|
||||
typedef char *(*strdup_fn)(const char *);
|
||||
typedef char *(*strerror_fn)(int);
|
||||
typedef int (*client_errno_fn)(Client *);
|
||||
|
||||
typedef Poll *(*simple_poll_get_fn)(SimplePoll *);
|
||||
typedef int (*simple_poll_loop_fn)(SimplePoll *);
|
||||
typedef void (*simple_poll_quit_fn)(SimplePoll *);
|
||||
typedef SimplePoll *(*simple_poll_new_fn)();
|
||||
typedef void (*simple_poll_free_fn)(SimplePoll *);
|
||||
typedef Poll *(*simple_poll_get_fn)(SimplePoll *);
|
||||
typedef int (*simple_poll_loop_fn)(SimplePoll *);
|
||||
typedef void (*simple_poll_quit_fn)(SimplePoll *);
|
||||
typedef SimplePoll *(*simple_poll_new_fn)();
|
||||
typedef void (*simple_poll_free_fn)(SimplePoll *);
|
||||
|
||||
free_fn free;
|
||||
client_new_fn client_new;
|
||||
client_free_fn client_free;
|
||||
alternative_service_name_fn alternative_service_name;
|
||||
entry_group_get_client_fn entry_group_get_client;
|
||||
entry_group_new_fn entry_group_new;
|
||||
entry_group_add_service_fn entry_group_add_service;
|
||||
entry_group_is_empty_fn entry_group_is_empty;
|
||||
entry_group_reset_fn entry_group_reset;
|
||||
entry_group_commit_fn entry_group_commit;
|
||||
strdup_fn strdup;
|
||||
strerror_fn strerror;
|
||||
client_errno_fn client_errno;
|
||||
simple_poll_get_fn simple_poll_get;
|
||||
simple_poll_loop_fn simple_poll_loop;
|
||||
simple_poll_quit_fn simple_poll_quit;
|
||||
simple_poll_new_fn simple_poll_new;
|
||||
simple_poll_free_fn simple_poll_free;
|
||||
free_fn free;
|
||||
client_new_fn client_new;
|
||||
client_free_fn client_free;
|
||||
alternative_service_name_fn alternative_service_name;
|
||||
entry_group_get_client_fn entry_group_get_client;
|
||||
entry_group_new_fn entry_group_new;
|
||||
entry_group_add_service_fn entry_group_add_service;
|
||||
entry_group_is_empty_fn entry_group_is_empty;
|
||||
entry_group_reset_fn entry_group_reset;
|
||||
entry_group_commit_fn entry_group_commit;
|
||||
strdup_fn strdup;
|
||||
strerror_fn strerror;
|
||||
client_errno_fn client_errno;
|
||||
simple_poll_get_fn simple_poll_get;
|
||||
simple_poll_loop_fn simple_poll_loop;
|
||||
simple_poll_quit_fn simple_poll_quit;
|
||||
simple_poll_new_fn simple_poll_new;
|
||||
simple_poll_free_fn simple_poll_free;
|
||||
|
||||
|
||||
int init_common() {
|
||||
int
|
||||
init_common() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libavahi-common.so.3", "libavahi-common.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&alternative_service_name, "avahi_alternative_service_name" },
|
||||
{ (dyn::apiproc *)&free, "avahi_free" },
|
||||
{ (dyn::apiproc *)&strdup, "avahi_strdup" },
|
||||
{ (dyn::apiproc *)&strerror, "avahi_strerror" },
|
||||
{ (dyn::apiproc *)&simple_poll_get, "avahi_simple_poll_get" },
|
||||
{ (dyn::apiproc *)&simple_poll_loop, "avahi_simple_poll_loop" },
|
||||
{ (dyn::apiproc *)&simple_poll_quit, "avahi_simple_poll_quit" },
|
||||
{ (dyn::apiproc *)&simple_poll_new, "avahi_simple_poll_new" },
|
||||
{ (dyn::apiproc *)&simple_poll_free, "avahi_simple_poll_free" },
|
||||
{ (dyn::apiproc *) &alternative_service_name, "avahi_alternative_service_name" },
|
||||
{ (dyn::apiproc *) &free, "avahi_free" },
|
||||
{ (dyn::apiproc *) &strdup, "avahi_strdup" },
|
||||
{ (dyn::apiproc *) &strerror, "avahi_strerror" },
|
||||
{ (dyn::apiproc *) &simple_poll_get, "avahi_simple_poll_get" },
|
||||
{ (dyn::apiproc *) &simple_poll_loop, "avahi_simple_poll_loop" },
|
||||
{ (dyn::apiproc *) &simple_poll_quit, "avahi_simple_poll_quit" },
|
||||
{ (dyn::apiproc *) &simple_poll_new, "avahi_simple_poll_new" },
|
||||
{ (dyn::apiproc *) &simple_poll_free, "avahi_simple_poll_free" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int init_client() {
|
||||
if(init_common()) {
|
||||
int
|
||||
init_client() {
|
||||
if (init_common()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libavahi-client.so.3", "libavahi-client.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&client_new, "avahi_client_new" },
|
||||
{ (dyn::apiproc *)&client_free, "avahi_client_free" },
|
||||
{ (dyn::apiproc *)&entry_group_get_client, "avahi_entry_group_get_client" },
|
||||
{ (dyn::apiproc *)&entry_group_new, "avahi_entry_group_new" },
|
||||
{ (dyn::apiproc *)&entry_group_add_service, "avahi_entry_group_add_service" },
|
||||
{ (dyn::apiproc *)&entry_group_is_empty, "avahi_entry_group_is_empty" },
|
||||
{ (dyn::apiproc *)&entry_group_reset, "avahi_entry_group_reset" },
|
||||
{ (dyn::apiproc *)&entry_group_commit, "avahi_entry_group_commit" },
|
||||
{ (dyn::apiproc *)&client_errno, "avahi_client_errno" },
|
||||
{ (dyn::apiproc *) &client_new, "avahi_client_new" },
|
||||
{ (dyn::apiproc *) &client_free, "avahi_client_free" },
|
||||
{ (dyn::apiproc *) &entry_group_get_client, "avahi_entry_group_get_client" },
|
||||
{ (dyn::apiproc *) &entry_group_new, "avahi_entry_group_new" },
|
||||
{ (dyn::apiproc *) &entry_group_add_service, "avahi_entry_group_add_service" },
|
||||
{ (dyn::apiproc *) &entry_group_is_empty, "avahi_entry_group_is_empty" },
|
||||
{ (dyn::apiproc *) &entry_group_reset, "avahi_entry_group_reset" },
|
||||
{ (dyn::apiproc *) &entry_group_commit, "avahi_entry_group_commit" },
|
||||
{ (dyn::apiproc *) &client_errno, "avahi_client_errno" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace avahi
|
||||
|
||||
namespace platf::publish {
|
||||
|
||||
template<class T>
|
||||
void free(T *p) {
|
||||
template <class T>
|
||||
void
|
||||
free(T *p) {
|
||||
avahi::free(p);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
using ptr_t = util::safe_ptr<T, free<T>>;
|
||||
using client_t = util::dyn_safe_ptr<avahi::Client, &avahi::client_free>;
|
||||
using poll_t = util::dyn_safe_ptr<avahi::SimplePoll, &avahi::simple_poll_free>;
|
||||
template <class T>
|
||||
using ptr_t = util::safe_ptr<T, free<T>>;
|
||||
using client_t = util::dyn_safe_ptr<avahi::Client, &avahi::client_free>;
|
||||
using poll_t = util::dyn_safe_ptr<avahi::SimplePoll, &avahi::simple_poll_free>;
|
||||
|
||||
avahi::EntryGroup *group = nullptr;
|
||||
avahi::EntryGroup *group = nullptr;
|
||||
|
||||
poll_t poll;
|
||||
client_t client;
|
||||
poll_t poll;
|
||||
client_t client;
|
||||
|
||||
ptr_t<char> name;
|
||||
ptr_t<char> name;
|
||||
|
||||
void create_services(avahi::Client *c);
|
||||
void
|
||||
create_services(avahi::Client *c);
|
||||
|
||||
void entry_group_callback(avahi::EntryGroup *g, avahi::EntryGroupState state, void *) {
|
||||
void
|
||||
entry_group_callback(avahi::EntryGroup *g, avahi::EntryGroupState state, void *) {
|
||||
group = g;
|
||||
|
||||
switch(state) {
|
||||
switch (state) {
|
||||
case avahi::ENTRY_GROUP_ESTABLISHED:
|
||||
BOOST_LOG(info) << "Avahi service " << name.get() << " successfully established.";
|
||||
break;
|
||||
|
|
@ -309,23 +313,24 @@ void entry_group_callback(avahi::EntryGroup *g, avahi::EntryGroupState state, vo
|
|||
case avahi::ENTRY_GROUP_UNCOMMITED:
|
||||
case avahi::ENTRY_GROUP_REGISTERING:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void create_services(avahi::Client *c) {
|
||||
void
|
||||
create_services(avahi::Client *c) {
|
||||
int ret;
|
||||
|
||||
auto fg = util::fail_guard([]() {
|
||||
avahi::simple_poll_quit(poll.get());
|
||||
});
|
||||
|
||||
if(!group) {
|
||||
if(!(group = avahi::entry_group_new(c, entry_group_callback, nullptr))) {
|
||||
if (!group) {
|
||||
if (!(group = avahi::entry_group_new(c, entry_group_callback, nullptr))) {
|
||||
BOOST_LOG(error) << "avahi::entry_group_new() failed: "sv << avahi::strerror(avahi::client_errno(c));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(avahi::entry_group_is_empty(group)) {
|
||||
if (avahi::entry_group_is_empty(group)) {
|
||||
BOOST_LOG(info) << "Adding avahi service "sv << name.get();
|
||||
|
||||
ret = avahi::entry_group_add_service(
|
||||
|
|
@ -338,8 +343,8 @@ void create_services(avahi::Client *c) {
|
|||
map_port(nvhttp::PORT_HTTP),
|
||||
nullptr);
|
||||
|
||||
if(ret < 0) {
|
||||
if(ret == avahi::ERR_COLLISION) {
|
||||
if (ret < 0) {
|
||||
if (ret == avahi::ERR_COLLISION) {
|
||||
// A service name collision with a local service happened. Let's pick a new name
|
||||
name.reset(avahi::alternative_service_name(name.get()));
|
||||
BOOST_LOG(info) << "Service name collision, renaming service to "sv << name.get();
|
||||
|
|
@ -357,17 +362,18 @@ void create_services(avahi::Client *c) {
|
|||
}
|
||||
|
||||
ret = avahi::entry_group_commit(group);
|
||||
if(ret < 0) {
|
||||
if (ret < 0) {
|
||||
BOOST_LOG(error) << "Failed to commit entry group: "sv << avahi::strerror(ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fg.disable();
|
||||
}
|
||||
}
|
||||
|
||||
void client_callback(avahi::Client *c, avahi::ClientState state, void *) {
|
||||
switch(state) {
|
||||
void
|
||||
client_callback(avahi::Client *c, avahi::ClientState state, void *) {
|
||||
switch (state) {
|
||||
case avahi::CLIENT_S_RUNNING:
|
||||
create_services(c);
|
||||
break;
|
||||
|
|
@ -377,39 +383,41 @@ void client_callback(avahi::Client *c, avahi::ClientState state, void *) {
|
|||
break;
|
||||
case avahi::CLIENT_S_COLLISION:
|
||||
case avahi::CLIENT_S_REGISTERING:
|
||||
if(group)
|
||||
if (group)
|
||||
avahi::entry_group_reset(group);
|
||||
break;
|
||||
case avahi::CLIENT_CONNECTING:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class deinit_t : public ::platf::deinit_t {
|
||||
public:
|
||||
class deinit_t: public ::platf::deinit_t {
|
||||
public:
|
||||
std::thread poll_thread;
|
||||
|
||||
deinit_t(std::thread poll_thread) : poll_thread { std::move(poll_thread) } {}
|
||||
deinit_t(std::thread poll_thread):
|
||||
poll_thread { std::move(poll_thread) } {}
|
||||
|
||||
~deinit_t() override {
|
||||
if(avahi::simple_poll_quit && poll) {
|
||||
if (avahi::simple_poll_quit && poll) {
|
||||
avahi::simple_poll_quit(poll.get());
|
||||
}
|
||||
|
||||
if(poll_thread.joinable()) {
|
||||
if (poll_thread.joinable()) {
|
||||
poll_thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<::platf::deinit_t> start() {
|
||||
if(avahi::init_client()) {
|
||||
[[nodiscard]] std::unique_ptr<::platf::deinit_t>
|
||||
start() {
|
||||
if (avahi::init_client()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int avhi_error;
|
||||
|
||||
poll.reset(avahi::simple_poll_new());
|
||||
if(!poll) {
|
||||
if (!poll) {
|
||||
BOOST_LOG(error) << "Failed to create simple poll object."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -419,11 +427,11 @@ public:
|
|||
client.reset(
|
||||
avahi::client_new(avahi::simple_poll_get(poll.get()), avahi::ClientFlags(0), client_callback, nullptr, &avhi_error));
|
||||
|
||||
if(!client) {
|
||||
if (!client) {
|
||||
BOOST_LOG(error) << "Failed to create client: "sv << avahi::strerror(avhi_error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<deinit_t>(std::thread { avahi::simple_poll_loop, poll.get() });
|
||||
}
|
||||
}
|
||||
} // namespace platf::publish
|
||||
|
|
@ -9,7 +9,8 @@ extern "C" {
|
|||
#if !VA_CHECK_VERSION(1, 9, 0)
|
||||
/* vaSyncBuffer stub allows Sunshine built against libva <2.9.0
|
||||
to link against ffmpeg on libva 2.9.0 or later */
|
||||
VAStatus vaSyncBuffer(
|
||||
VAStatus
|
||||
vaSyncBuffer(
|
||||
VADisplay dpy,
|
||||
VABufferID buf_id,
|
||||
uint64_t timeout_ns) {
|
||||
|
|
@ -30,16 +31,16 @@ using namespace std::literals;
|
|||
extern "C" struct AVBufferRef;
|
||||
|
||||
namespace va {
|
||||
constexpr auto SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 = 0x40000000;
|
||||
constexpr auto EXPORT_SURFACE_WRITE_ONLY = 0x0002;
|
||||
constexpr auto EXPORT_SURFACE_COMPOSED_LAYERS = 0x0008;
|
||||
constexpr auto SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 = 0x40000000;
|
||||
constexpr auto EXPORT_SURFACE_WRITE_ONLY = 0x0002;
|
||||
constexpr auto EXPORT_SURFACE_COMPOSED_LAYERS = 0x0008;
|
||||
|
||||
using VADisplay = void *;
|
||||
using VAStatus = int;
|
||||
using VAGenericID = unsigned int;
|
||||
using VASurfaceID = VAGenericID;
|
||||
using VADisplay = void *;
|
||||
using VAStatus = int;
|
||||
using VAGenericID = unsigned int;
|
||||
using VASurfaceID = VAGenericID;
|
||||
|
||||
struct DRMPRIMESurfaceDescriptor {
|
||||
struct DRMPRIMESurfaceDescriptor {
|
||||
// VA Pixel format fourcc of the whole surface (VA_FOURCC_*).
|
||||
uint32_t fourcc;
|
||||
|
||||
|
|
@ -81,10 +82,10 @@ struct DRMPRIMESurfaceDescriptor {
|
|||
// Pitch of each plane.
|
||||
uint32_t pitch[4];
|
||||
} layers[4];
|
||||
};
|
||||
};
|
||||
|
||||
/** Currently defined profiles */
|
||||
enum class profile_e {
|
||||
/** Currently defined profiles */
|
||||
enum class profile_e {
|
||||
// Profile ID used for video processing.
|
||||
ProfileNone = -1,
|
||||
MPEG2Simple = 0,
|
||||
|
|
@ -125,9 +126,9 @@ enum class profile_e {
|
|||
|
||||
// Profile ID used for protected video playback.
|
||||
Protected = 35
|
||||
};
|
||||
};
|
||||
|
||||
enum class entry_e {
|
||||
enum class entry_e {
|
||||
VLD = 1,
|
||||
IZZ = 2,
|
||||
IDCT = 3,
|
||||
|
|
@ -191,128 +192,131 @@ enum class entry_e {
|
|||
* A function for protected content to decrypt encrypted content.
|
||||
**/
|
||||
ProtectedContent = 14,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
typedef VAStatus (*queryConfigEntrypoints_fn)(VADisplay dpy, profile_e profile, entry_e *entrypoint_list, int *num_entrypoints);
|
||||
typedef int (*maxNumEntrypoints_fn)(VADisplay dpy);
|
||||
typedef VADisplay (*getDisplayDRM_fn)(int fd);
|
||||
typedef VAStatus (*terminate_fn)(VADisplay dpy);
|
||||
typedef VAStatus (*initialize_fn)(VADisplay dpy, int *major_version, int *minor_version);
|
||||
typedef const char *(*errorStr_fn)(VAStatus error_status);
|
||||
typedef void (*VAMessageCallback)(void *user_context, const char *message);
|
||||
typedef VAMessageCallback (*setErrorCallback_fn)(VADisplay dpy, VAMessageCallback callback, void *user_context);
|
||||
typedef VAMessageCallback (*setInfoCallback_fn)(VADisplay dpy, VAMessageCallback callback, void *user_context);
|
||||
typedef const char *(*queryVendorString_fn)(VADisplay dpy);
|
||||
typedef VAStatus (*exportSurfaceHandle_fn)(
|
||||
typedef VAStatus (*queryConfigEntrypoints_fn)(VADisplay dpy, profile_e profile, entry_e *entrypoint_list, int *num_entrypoints);
|
||||
typedef int (*maxNumEntrypoints_fn)(VADisplay dpy);
|
||||
typedef VADisplay (*getDisplayDRM_fn)(int fd);
|
||||
typedef VAStatus (*terminate_fn)(VADisplay dpy);
|
||||
typedef VAStatus (*initialize_fn)(VADisplay dpy, int *major_version, int *minor_version);
|
||||
typedef const char *(*errorStr_fn)(VAStatus error_status);
|
||||
typedef void (*VAMessageCallback)(void *user_context, const char *message);
|
||||
typedef VAMessageCallback (*setErrorCallback_fn)(VADisplay dpy, VAMessageCallback callback, void *user_context);
|
||||
typedef VAMessageCallback (*setInfoCallback_fn)(VADisplay dpy, VAMessageCallback callback, void *user_context);
|
||||
typedef const char *(*queryVendorString_fn)(VADisplay dpy);
|
||||
typedef VAStatus (*exportSurfaceHandle_fn)(
|
||||
VADisplay dpy, VASurfaceID surface_id,
|
||||
uint32_t mem_type, uint32_t flags,
|
||||
void *descriptor);
|
||||
|
||||
static maxNumEntrypoints_fn maxNumEntrypoints;
|
||||
static queryConfigEntrypoints_fn queryConfigEntrypoints;
|
||||
static getDisplayDRM_fn getDisplayDRM;
|
||||
static terminate_fn terminate;
|
||||
static initialize_fn initialize;
|
||||
static errorStr_fn errorStr;
|
||||
static setErrorCallback_fn setErrorCallback;
|
||||
static setInfoCallback_fn setInfoCallback;
|
||||
static queryVendorString_fn queryVendorString;
|
||||
static exportSurfaceHandle_fn exportSurfaceHandle;
|
||||
static maxNumEntrypoints_fn maxNumEntrypoints;
|
||||
static queryConfigEntrypoints_fn queryConfigEntrypoints;
|
||||
static getDisplayDRM_fn getDisplayDRM;
|
||||
static terminate_fn terminate;
|
||||
static initialize_fn initialize;
|
||||
static errorStr_fn errorStr;
|
||||
static setErrorCallback_fn setErrorCallback;
|
||||
static setInfoCallback_fn setInfoCallback;
|
||||
static queryVendorString_fn queryVendorString;
|
||||
static exportSurfaceHandle_fn exportSurfaceHandle;
|
||||
|
||||
using display_t = util::dyn_safe_ptr_v2<void, VAStatus, &terminate>;
|
||||
using display_t = util::dyn_safe_ptr_v2<void, VAStatus, &terminate>;
|
||||
|
||||
int init_main_va() {
|
||||
int
|
||||
init_main_va() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libva.so.2", "libva.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&maxNumEntrypoints, "vaMaxNumEntrypoints" },
|
||||
{ (dyn::apiproc *)&queryConfigEntrypoints, "vaQueryConfigEntrypoints" },
|
||||
{ (dyn::apiproc *)&terminate, "vaTerminate" },
|
||||
{ (dyn::apiproc *)&initialize, "vaInitialize" },
|
||||
{ (dyn::apiproc *)&errorStr, "vaErrorStr" },
|
||||
{ (dyn::apiproc *)&setErrorCallback, "vaSetErrorCallback" },
|
||||
{ (dyn::apiproc *)&setInfoCallback, "vaSetInfoCallback" },
|
||||
{ (dyn::apiproc *)&queryVendorString, "vaQueryVendorString" },
|
||||
{ (dyn::apiproc *)&exportSurfaceHandle, "vaExportSurfaceHandle" },
|
||||
{ (dyn::apiproc *) &maxNumEntrypoints, "vaMaxNumEntrypoints" },
|
||||
{ (dyn::apiproc *) &queryConfigEntrypoints, "vaQueryConfigEntrypoints" },
|
||||
{ (dyn::apiproc *) &terminate, "vaTerminate" },
|
||||
{ (dyn::apiproc *) &initialize, "vaInitialize" },
|
||||
{ (dyn::apiproc *) &errorStr, "vaErrorStr" },
|
||||
{ (dyn::apiproc *) &setErrorCallback, "vaSetErrorCallback" },
|
||||
{ (dyn::apiproc *) &setInfoCallback, "vaSetInfoCallback" },
|
||||
{ (dyn::apiproc *) &queryVendorString, "vaQueryVendorString" },
|
||||
{ (dyn::apiproc *) &exportSurfaceHandle, "vaExportSurfaceHandle" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int init() {
|
||||
if(init_main_va()) {
|
||||
int
|
||||
init() {
|
||||
if (init_main_va()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libva-drm.so.2", "libva-drm.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&getDisplayDRM, "vaGetDisplayDRM" },
|
||||
{ (dyn::apiproc *) &getDisplayDRM, "vaGetDisplayDRM" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf);
|
||||
int
|
||||
vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf);
|
||||
|
||||
class va_t : public platf::hwdevice_t {
|
||||
public:
|
||||
int init(int in_width, int in_height, file_t &&render_device) {
|
||||
class va_t: public platf::hwdevice_t {
|
||||
public:
|
||||
int
|
||||
init(int in_width, int in_height, file_t &&render_device) {
|
||||
file = std::move(render_device);
|
||||
|
||||
if(!va::initialize || !gbm::create_device) {
|
||||
if(!va::initialize) BOOST_LOG(warning) << "libva not initialized"sv;
|
||||
if(!gbm::create_device) BOOST_LOG(warning) << "libgbm not initialized"sv;
|
||||
if (!va::initialize || !gbm::create_device) {
|
||||
if (!va::initialize) BOOST_LOG(warning) << "libva not initialized"sv;
|
||||
if (!gbm::create_device) BOOST_LOG(warning) << "libgbm not initialized"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
this->data = (void *)vaapi_make_hwdevice_ctx;
|
||||
this->data = (void *) vaapi_make_hwdevice_ctx;
|
||||
|
||||
gbm.reset(gbm::create_device(file.el));
|
||||
if(!gbm) {
|
||||
if (!gbm) {
|
||||
char string[1024];
|
||||
BOOST_LOG(error) << "Couldn't create GBM device: ["sv << strerror_r(errno, string, sizeof(string)) << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
display = egl::make_display(gbm.get());
|
||||
if(!display) {
|
||||
if (!display) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto ctx_opt = egl::make_ctx(display.get());
|
||||
if(!ctx_opt) {
|
||||
if (!ctx_opt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -324,19 +328,20 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
|
||||
int
|
||||
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override {
|
||||
this->hwframe.reset(frame);
|
||||
this->frame = frame;
|
||||
|
||||
if(!frame->buf[0]) {
|
||||
if(av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) {
|
||||
if (!frame->buf[0]) {
|
||||
if (av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) {
|
||||
BOOST_LOG(error) << "Couldn't get hwframe for VAAPI"sv;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
va::DRMPRIMESurfaceDescriptor prime;
|
||||
va::VASurfaceID surface = (std::uintptr_t)frame->data[3];
|
||||
va::VASurfaceID surface = (std::uintptr_t) frame->data[3];
|
||||
|
||||
auto status = va::exportSurfaceHandle(
|
||||
this->va_display,
|
||||
|
|
@ -344,43 +349,42 @@ public:
|
|||
va::SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
|
||||
va::EXPORT_SURFACE_WRITE_ONLY | va::EXPORT_SURFACE_COMPOSED_LAYERS,
|
||||
&prime);
|
||||
if(status) {
|
||||
|
||||
BOOST_LOG(error) << "Couldn't export va surface handle: ["sv << (int)surface << "]: "sv << va::errorStr(status);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't export va surface handle: ["sv << (int) surface << "]: "sv << va::errorStr(status);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Keep track of file descriptors
|
||||
std::array<file_t, egl::nv12_img_t::num_fds> fds;
|
||||
for(int x = 0; x < prime.num_objects; ++x) {
|
||||
for (int x = 0; x < prime.num_objects; ++x) {
|
||||
fds[x] = prime.objects[x].fd;
|
||||
}
|
||||
|
||||
auto nv12_opt = egl::import_target(
|
||||
display.get(),
|
||||
std::move(fds),
|
||||
{ (int)prime.width,
|
||||
(int)prime.height,
|
||||
{ (int) prime.width,
|
||||
(int) prime.height,
|
||||
{ prime.objects[prime.layers[0].object_index[0]].fd, -1, -1, -1 },
|
||||
0,
|
||||
0,
|
||||
{ prime.layers[0].pitch[0] },
|
||||
{ prime.layers[0].offset[0] } },
|
||||
{ (int)prime.width / 2,
|
||||
(int)prime.height / 2,
|
||||
{ (int) prime.width / 2,
|
||||
(int) prime.height / 2,
|
||||
{ prime.objects[prime.layers[0].object_index[1]].fd, -1, -1, -1 },
|
||||
0,
|
||||
0,
|
||||
{ prime.layers[0].pitch[1] },
|
||||
{ prime.layers[0].offset[1] } });
|
||||
|
||||
if(!nv12_opt) {
|
||||
if (!nv12_opt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto sws_opt = egl::sws_t::make(width, height, frame->width, frame->height);
|
||||
if(!sws_opt) {
|
||||
if (!sws_opt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -390,7 +394,8 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override {
|
||||
void
|
||||
set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) override {
|
||||
sws.set_colorspace(colorspace, color_range);
|
||||
}
|
||||
|
||||
|
|
@ -409,31 +414,33 @@ public:
|
|||
egl::nv12_t nv12;
|
||||
|
||||
int width, height;
|
||||
};
|
||||
};
|
||||
|
||||
class va_ram_t : public va_t {
|
||||
public:
|
||||
int convert(platf::img_t &img) override {
|
||||
class va_ram_t: public va_t {
|
||||
public:
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
sws.load_ram(img);
|
||||
|
||||
sws.convert(nv12->buf);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class va_vram_t : public va_t {
|
||||
public:
|
||||
int convert(platf::img_t &img) override {
|
||||
auto &descriptor = (egl::img_descriptor_t &)img;
|
||||
class va_vram_t: public va_t {
|
||||
public:
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
auto &descriptor = (egl::img_descriptor_t &) img;
|
||||
|
||||
if(descriptor.sequence > sequence) {
|
||||
if (descriptor.sequence > sequence) {
|
||||
sequence = descriptor.sequence;
|
||||
|
||||
rgb = egl::rgb_t {};
|
||||
|
||||
auto rgb_opt = egl::import_source(display.get(), descriptor.sd);
|
||||
|
||||
if(!rgb_opt) {
|
||||
if (!rgb_opt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -446,8 +453,9 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int init(int in_width, int in_height, file_t &&render_device, int offset_x, int offset_y) {
|
||||
if(va_t::init(in_width, in_height, std::move(render_device))) {
|
||||
int
|
||||
init(int in_width, int in_height, file_t &&render_device, int offset_x, int offset_y) {
|
||||
if (va_t::init(in_width, in_height, std::move(render_device))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -463,28 +471,28 @@ public:
|
|||
egl::rgb_t rgb;
|
||||
|
||||
int offset_x, offset_y;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* This is a private structure of FFmpeg, I need this to manually create
|
||||
* a VAAPI hardware context
|
||||
*
|
||||
* xdisplay will not be used internally by FFmpeg
|
||||
*/
|
||||
typedef struct VAAPIDevicePriv {
|
||||
typedef struct VAAPIDevicePriv {
|
||||
union {
|
||||
void *xdisplay;
|
||||
int fd;
|
||||
} drm;
|
||||
int drm_fd;
|
||||
} VAAPIDevicePriv;
|
||||
} VAAPIDevicePriv;
|
||||
|
||||
/**
|
||||
/**
|
||||
* VAAPI connection details.
|
||||
*
|
||||
* Allocated as AVHWDeviceContext.hwctx
|
||||
*/
|
||||
typedef struct AVVAAPIDeviceContext {
|
||||
typedef struct AVVAAPIDeviceContext {
|
||||
/**
|
||||
* The VADisplay handle, to be filled by the user.
|
||||
*/
|
||||
|
|
@ -497,27 +505,29 @@ typedef struct AVVAAPIDeviceContext {
|
|||
* operations using VAAPI with the same VADisplay.
|
||||
*/
|
||||
unsigned int driver_quirks;
|
||||
} AVVAAPIDeviceContext;
|
||||
} AVVAAPIDeviceContext;
|
||||
|
||||
static void __log(void *level, const char *msg) {
|
||||
BOOST_LOG(*(boost::log::sources::severity_logger<int> *)level) << msg;
|
||||
}
|
||||
static void
|
||||
__log(void *level, const char *msg) {
|
||||
BOOST_LOG(*(boost::log::sources::severity_logger<int> *) level) << msg;
|
||||
}
|
||||
|
||||
int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf) {
|
||||
if(!va::initialize) {
|
||||
int
|
||||
vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf) {
|
||||
if (!va::initialize) {
|
||||
BOOST_LOG(warning) << "libva not loaded"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!va::getDisplayDRM) {
|
||||
if (!va::getDisplayDRM) {
|
||||
BOOST_LOG(warning) << "libva-drm not loaded"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto va = (va::va_t *)base;
|
||||
auto va = (va::va_t *) base;
|
||||
auto fd = dup(va->file.el);
|
||||
|
||||
auto *priv = (VAAPIDevicePriv *)av_mallocz(sizeof(VAAPIDevicePriv));
|
||||
auto *priv = (VAAPIDevicePriv *) av_mallocz(sizeof(VAAPIDevicePriv));
|
||||
priv->drm_fd = fd;
|
||||
priv->drm.fd = fd;
|
||||
|
||||
|
|
@ -527,7 +537,7 @@ int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf
|
|||
});
|
||||
|
||||
va::display_t display { va::getDisplayDRM(fd) };
|
||||
if(!display) {
|
||||
if (!display) {
|
||||
auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str();
|
||||
|
||||
BOOST_LOG(error) << "Couldn't open a va display from DRM with device: "sv << render_device;
|
||||
|
|
@ -541,7 +551,7 @@ int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf
|
|||
|
||||
int major, minor;
|
||||
auto status = va::initialize(display.get(), &major, &minor);
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't initialize va display: "sv << va::errorStr(status);
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -549,13 +559,13 @@ int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf
|
|||
BOOST_LOG(debug) << "vaapi vendor: "sv << va::queryVendorString(display.get());
|
||||
|
||||
*hw_device_buf = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
|
||||
auto ctx = (AVVAAPIDeviceContext *)((AVHWDeviceContext *)(*hw_device_buf)->data)->hwctx;
|
||||
auto ctx = (AVVAAPIDeviceContext *) ((AVHWDeviceContext *) (*hw_device_buf)->data)->hwctx;
|
||||
ctx->display = display.release();
|
||||
|
||||
fg.disable();
|
||||
|
||||
auto err = av_hwdevice_ctx_init(*hw_device_buf);
|
||||
if(err) {
|
||||
if (err) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Failed to create FFMpeg hardware device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
|
|
@ -563,41 +573,43 @@ int vaapi_make_hwdevice_ctx(platf::hwdevice_t *base, AVBufferRef **hw_device_buf
|
|||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool query(display_t::pointer display, profile_e profile) {
|
||||
static bool
|
||||
query(display_t::pointer display, profile_e profile) {
|
||||
std::vector<entry_e> entrypoints;
|
||||
entrypoints.resize(maxNumEntrypoints(display));
|
||||
|
||||
int count;
|
||||
auto status = queryConfigEntrypoints(display, profile, entrypoints.data(), &count);
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't query entrypoints: "sv << va::errorStr(status);
|
||||
return false;
|
||||
}
|
||||
entrypoints.resize(count);
|
||||
|
||||
for(auto entrypoint : entrypoints) {
|
||||
if(entrypoint == entry_e::EncSlice || entrypoint == entry_e::EncSliceLP) {
|
||||
for (auto entrypoint : entrypoints) {
|
||||
if (entrypoint == entry_e::EncSlice || entrypoint == entry_e::EncSliceLP) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool validate(int fd) {
|
||||
if(init()) {
|
||||
bool
|
||||
validate(int fd) {
|
||||
if (init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
va::display_t display { va::getDisplayDRM(fd) };
|
||||
if(!display) {
|
||||
if (!display) {
|
||||
char string[1024];
|
||||
|
||||
auto bytes = readlink(("/proc/self/fd/" + std::to_string(fd)).c_str(), string, sizeof(string));
|
||||
|
||||
std::string_view render_device { string, (std::size_t)bytes };
|
||||
std::string_view render_device { string, (std::size_t) bytes };
|
||||
|
||||
BOOST_LOG(error) << "Couldn't open a va display from DRM with device: "sv << render_device;
|
||||
return false;
|
||||
|
|
@ -605,30 +617,31 @@ bool validate(int fd) {
|
|||
|
||||
int major, minor;
|
||||
auto status = initialize(display.get(), &major, &minor);
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't initialize va display: "sv << va::errorStr(status);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!query(display.get(), profile_e::H264Main)) {
|
||||
if (!query(display.get(), profile_e::H264Main)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(config::video.hevc_mode > 1 && !query(display.get(), profile_e::HEVCMain)) {
|
||||
if (config::video.hevc_mode > 1 && !query(display.get(), profile_e::HEVCMain)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(config::video.hevc_mode > 2 && !query(display.get(), profile_e::HEVCMain10)) {
|
||||
if (config::video.hevc_mode > 2 && !query(display.get(), profile_e::HEVCMain10)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram) {
|
||||
if(vram) {
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram) {
|
||||
if (vram) {
|
||||
auto egl = std::make_shared<va::va_vram_t>();
|
||||
if(egl->init(width, height, std::move(card), offset_x, offset_y)) {
|
||||
if (egl->init(width, height, std::move(card), offset_x, offset_y)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -637,19 +650,20 @@ std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, file_t &
|
|||
|
||||
else {
|
||||
auto egl = std::make_shared<va::va_ram_t>();
|
||||
if(egl->init(width, height, std::move(card))) {
|
||||
if (egl->init(width, height, std::move(card))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return egl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, int offset_x, int offset_y, bool vram) {
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(int width, int height, int offset_x, int offset_y, bool vram) {
|
||||
auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str();
|
||||
|
||||
file_t file = open(render_device, O_RDWR);
|
||||
if(file.el < 0) {
|
||||
if (file.el < 0) {
|
||||
char string[1024];
|
||||
BOOST_LOG(error) << "Couldn't open "sv << render_device << ": " << strerror_r(errno, string, sizeof(string));
|
||||
|
||||
|
|
@ -657,9 +671,10 @@ std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, int offs
|
|||
}
|
||||
|
||||
return make_hwdevice(width, height, std::move(file), offset_x, offset_y, vram);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, bool vram) {
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(int width, int height, bool vram) {
|
||||
return make_hwdevice(width, height, 0, 0, vram);
|
||||
}
|
||||
}
|
||||
} // namespace va
|
||||
|
|
|
|||
|
|
@ -5,23 +5,28 @@
|
|||
#include "src/platform/common.h"
|
||||
|
||||
namespace egl {
|
||||
struct surface_descriptor_t;
|
||||
struct surface_descriptor_t;
|
||||
}
|
||||
namespace va {
|
||||
/**
|
||||
/**
|
||||
* Width --> Width of the image
|
||||
* Height --> Height of the image
|
||||
* offset_x --> Horizontal offset of the image in the texture
|
||||
* offset_y --> Vertical offset of the image in the texture
|
||||
* file_t card --> The file descriptor of the render device used for encoding
|
||||
*/
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, bool vram);
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, int offset_x, int offset_y, bool vram);
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram);
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(int width, int height, bool vram);
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(int width, int height, int offset_x, int offset_y, bool vram);
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram);
|
||||
|
||||
// Ensure the render device pointed to by fd is capable of encoding h264 with the hevc_mode configured
|
||||
bool validate(int fd);
|
||||
// Ensure the render device pointed to by fd is capable of encoding h264 with the hevc_mode configured
|
||||
bool
|
||||
validate(int fd);
|
||||
|
||||
int init();
|
||||
int
|
||||
init();
|
||||
} // namespace va
|
||||
#endif
|
||||
|
|
@ -21,26 +21,28 @@ using namespace std::literals;
|
|||
|
||||
namespace wl {
|
||||
|
||||
// Helper to call C++ method from wayland C callback
|
||||
template<class T, class Method, Method m, class... Params>
|
||||
static auto classCall(void *data, Params... params) -> decltype(((*reinterpret_cast<T *>(data)).*m)(params...)) {
|
||||
// Helper to call C++ method from wayland C callback
|
||||
template <class T, class Method, Method m, class... Params>
|
||||
static auto
|
||||
classCall(void *data, Params... params) -> decltype(((*reinterpret_cast<T *>(data)).*m)(params...)) {
|
||||
return ((*reinterpret_cast<T *>(data)).*m)(params...);
|
||||
}
|
||||
}
|
||||
|
||||
#define CLASS_CALL(c, m) classCall<c, decltype(&c::m), &c::m>
|
||||
|
||||
int display_t::init(const char *display_name) {
|
||||
if(!display_name) {
|
||||
int
|
||||
display_t::init(const char *display_name) {
|
||||
if (!display_name) {
|
||||
display_name = std::getenv("WAYLAND_DISPLAY");
|
||||
}
|
||||
|
||||
if(!display_name) {
|
||||
if (!display_name) {
|
||||
BOOST_LOG(error) << "Environment variable WAYLAND_DISPLAY has not been defined"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
display_internal.reset(wl_display_connect(display_name));
|
||||
if(!display_internal) {
|
||||
if (!display_internal) {
|
||||
BOOST_LOG(error) << "Couldn't connect to Wayland display: "sv << display_name;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -48,18 +50,20 @@ int display_t::init(const char *display_name) {
|
|||
BOOST_LOG(info) << "Found display ["sv << display_name << ']';
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void display_t::roundtrip() {
|
||||
void
|
||||
display_t::roundtrip() {
|
||||
wl_display_roundtrip(display_internal.get());
|
||||
}
|
||||
}
|
||||
|
||||
wl_registry *display_t::registry() {
|
||||
wl_registry *
|
||||
display_t::registry() {
|
||||
return wl_display_get_registry(display_internal.get());
|
||||
}
|
||||
}
|
||||
|
||||
inline monitor_t::monitor_t(wl_output *output)
|
||||
: output { output }, listener {
|
||||
inline monitor_t::monitor_t(wl_output *output):
|
||||
output { output }, listener {
|
||||
&CLASS_CALL(monitor_t, xdg_position),
|
||||
&CLASS_CALL(monitor_t, xdg_size),
|
||||
&CLASS_CALL(monitor_t, xdg_done),
|
||||
|
|
@ -67,101 +71,114 @@ inline monitor_t::monitor_t(wl_output *output)
|
|||
&CLASS_CALL(monitor_t, xdg_description)
|
||||
} {}
|
||||
|
||||
inline void monitor_t::xdg_name(zxdg_output_v1 *, const char *name) {
|
||||
inline void
|
||||
monitor_t::xdg_name(zxdg_output_v1 *, const char *name) {
|
||||
this->name = name;
|
||||
|
||||
BOOST_LOG(info) << "Name: "sv << this->name;
|
||||
}
|
||||
}
|
||||
|
||||
void monitor_t::xdg_description(zxdg_output_v1 *, const char *description) {
|
||||
void
|
||||
monitor_t::xdg_description(zxdg_output_v1 *, const char *description) {
|
||||
this->description = description;
|
||||
|
||||
BOOST_LOG(info) << "Found monitor: "sv << this->description;
|
||||
}
|
||||
}
|
||||
|
||||
void monitor_t::xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y) {
|
||||
void
|
||||
monitor_t::xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y) {
|
||||
viewport.offset_x = x;
|
||||
viewport.offset_y = y;
|
||||
|
||||
BOOST_LOG(info) << "Offset: "sv << x << 'x' << y;
|
||||
}
|
||||
}
|
||||
|
||||
void monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) {
|
||||
void
|
||||
monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) {
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
|
||||
BOOST_LOG(info) << "Resolution: "sv << width << 'x' << height;
|
||||
}
|
||||
}
|
||||
|
||||
void monitor_t::xdg_done(zxdg_output_v1 *) {
|
||||
void
|
||||
monitor_t::xdg_done(zxdg_output_v1 *) {
|
||||
BOOST_LOG(info) << "All info about monitor ["sv << name << "] has been send"sv;
|
||||
}
|
||||
}
|
||||
|
||||
void monitor_t::listen(zxdg_output_manager_v1 *output_manager) {
|
||||
void
|
||||
monitor_t::listen(zxdg_output_manager_v1 *output_manager) {
|
||||
auto xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, output);
|
||||
zxdg_output_v1_add_listener(xdg_output, &listener, this);
|
||||
}
|
||||
}
|
||||
|
||||
interface_t::interface_t() noexcept
|
||||
: output_manager { nullptr }, listener {
|
||||
interface_t::interface_t() noexcept
|
||||
:
|
||||
output_manager { nullptr },
|
||||
listener {
|
||||
&CLASS_CALL(interface_t, add_interface),
|
||||
&CLASS_CALL(interface_t, del_interface)
|
||||
} {}
|
||||
|
||||
void interface_t::listen(wl_registry *registry) {
|
||||
void
|
||||
interface_t::listen(wl_registry *registry) {
|
||||
wl_registry_add_listener(registry, &listener, this);
|
||||
}
|
||||
}
|
||||
|
||||
void interface_t::add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version) {
|
||||
void
|
||||
interface_t::add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version) {
|
||||
BOOST_LOG(debug) << "Available interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
|
||||
if(!std::strcmp(interface, wl_output_interface.name)) {
|
||||
if (!std::strcmp(interface, wl_output_interface.name)) {
|
||||
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
monitors.emplace_back(
|
||||
std::make_unique<monitor_t>(
|
||||
(wl_output *)wl_registry_bind(registry, id, &wl_output_interface, version)));
|
||||
(wl_output *) wl_registry_bind(registry, id, &wl_output_interface, version)));
|
||||
}
|
||||
else if(!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) {
|
||||
else if (!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) {
|
||||
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
output_manager = (zxdg_output_manager_v1 *)wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version);
|
||||
output_manager = (zxdg_output_manager_v1 *) wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version);
|
||||
|
||||
this->interface[XDG_OUTPUT] = true;
|
||||
}
|
||||
else if(!std::strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) {
|
||||
else if (!std::strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) {
|
||||
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||
dmabuf_manager = (zwlr_export_dmabuf_manager_v1 *)wl_registry_bind(registry, id, &zwlr_export_dmabuf_manager_v1_interface, version);
|
||||
dmabuf_manager = (zwlr_export_dmabuf_manager_v1 *) wl_registry_bind(registry, id, &zwlr_export_dmabuf_manager_v1_interface, version);
|
||||
|
||||
this->interface[WLR_EXPORT_DMABUF] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void interface_t::del_interface(wl_registry *registry, uint32_t id) {
|
||||
void
|
||||
interface_t::del_interface(wl_registry *registry, uint32_t id) {
|
||||
BOOST_LOG(info) << "Delete: "sv << id;
|
||||
}
|
||||
}
|
||||
|
||||
dmabuf_t::dmabuf_t()
|
||||
: status { READY }, frames {}, current_frame { &frames[0] }, listener {
|
||||
dmabuf_t::dmabuf_t():
|
||||
status { READY }, frames {}, current_frame { &frames[0] }, listener {
|
||||
&CLASS_CALL(dmabuf_t, frame),
|
||||
&CLASS_CALL(dmabuf_t, object),
|
||||
&CLASS_CALL(dmabuf_t, ready),
|
||||
&CLASS_CALL(dmabuf_t, cancel)
|
||||
} {
|
||||
}
|
||||
}
|
||||
|
||||
void dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) {
|
||||
void
|
||||
dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) {
|
||||
auto frame = zwlr_export_dmabuf_manager_v1_capture_output(dmabuf_manager, blend_cursor, output);
|
||||
zwlr_export_dmabuf_frame_v1_add_listener(frame, &listener, this);
|
||||
|
||||
status = WAITING;
|
||||
}
|
||||
}
|
||||
|
||||
dmabuf_t::~dmabuf_t() {
|
||||
for(auto &frame : frames) {
|
||||
dmabuf_t::~dmabuf_t() {
|
||||
for (auto &frame : frames) {
|
||||
frame.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dmabuf_t::frame(
|
||||
void
|
||||
dmabuf_t::frame(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t width, std::uint32_t height,
|
||||
std::uint32_t x, std::uint32_t y,
|
||||
|
|
@ -174,10 +191,11 @@ void dmabuf_t::frame(
|
|||
next_frame->sd.fourcc = format;
|
||||
next_frame->sd.width = width;
|
||||
next_frame->sd.height = height;
|
||||
next_frame->sd.modifier = (((std::uint64_t)high) << 32) | low;
|
||||
}
|
||||
next_frame->sd.modifier = (((std::uint64_t) high) << 32) | low;
|
||||
}
|
||||
|
||||
void dmabuf_t::object(
|
||||
void
|
||||
dmabuf_t::object(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t index,
|
||||
std::int32_t fd,
|
||||
|
|
@ -190,51 +208,53 @@ void dmabuf_t::object(
|
|||
next_frame->sd.fds[plane_index] = fd;
|
||||
next_frame->sd.pitches[plane_index] = stride;
|
||||
next_frame->sd.offsets[plane_index] = offset;
|
||||
}
|
||||
}
|
||||
|
||||
void dmabuf_t::ready(
|
||||
void
|
||||
dmabuf_t::ready(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec) {
|
||||
|
||||
zwlr_export_dmabuf_frame_v1_destroy(frame);
|
||||
|
||||
current_frame->destroy();
|
||||
current_frame = get_next_frame();
|
||||
|
||||
status = READY;
|
||||
}
|
||||
}
|
||||
|
||||
void dmabuf_t::cancel(
|
||||
void
|
||||
dmabuf_t::cancel(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t reason) {
|
||||
|
||||
zwlr_export_dmabuf_frame_v1_destroy(frame);
|
||||
|
||||
auto next_frame = get_next_frame();
|
||||
next_frame->destroy();
|
||||
|
||||
status = REINIT;
|
||||
}
|
||||
}
|
||||
|
||||
void frame_t::destroy() {
|
||||
for(auto x = 0; x < 4; ++x) {
|
||||
if(sd.fds[x] >= 0) {
|
||||
void
|
||||
frame_t::destroy() {
|
||||
for (auto x = 0; x < 4; ++x) {
|
||||
if (sd.fds[x] >= 0) {
|
||||
close(sd.fds[x]);
|
||||
|
||||
sd.fds[x] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frame_t::frame_t() {
|
||||
frame_t::frame_t() {
|
||||
// File descriptors aren't open
|
||||
std::fill_n(sd.fds, 4, -1);
|
||||
};
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<monitor_t>> monitors(const char *display_name) {
|
||||
std::vector<std::unique_ptr<monitor_t>>
|
||||
monitors(const char *display_name) {
|
||||
display_t display;
|
||||
|
||||
if(display.init(display_name)) {
|
||||
if (display.init(display_name)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -243,31 +263,33 @@ std::vector<std::unique_ptr<monitor_t>> monitors(const char *display_name) {
|
|||
|
||||
display.roundtrip();
|
||||
|
||||
if(!interface[interface_t::XDG_OUTPUT]) {
|
||||
if (!interface[interface_t::XDG_OUTPUT]) {
|
||||
BOOST_LOG(error) << "Missing Wayland wire XDG_OUTPUT"sv;
|
||||
return {};
|
||||
}
|
||||
|
||||
for(auto &monitor : interface.monitors) {
|
||||
for (auto &monitor : interface.monitors) {
|
||||
monitor->listen(interface.output_manager);
|
||||
}
|
||||
|
||||
display.roundtrip();
|
||||
|
||||
return std::move(interface.monitors);
|
||||
}
|
||||
}
|
||||
|
||||
static bool validate() {
|
||||
static bool
|
||||
validate() {
|
||||
display_t display;
|
||||
|
||||
return display.init() == 0;
|
||||
}
|
||||
}
|
||||
|
||||
int init() {
|
||||
int
|
||||
init() {
|
||||
static bool validated = validate();
|
||||
|
||||
return !validated;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wl
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
#include <bitset>
|
||||
|
||||
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||
#include <wlr-export-dmabuf-unstable-v1.h>
|
||||
#include <xdg-output-unstable-v1.h>
|
||||
#include <wlr-export-dmabuf-unstable-v1.h>
|
||||
#include <xdg-output-unstable-v1.h>
|
||||
#endif
|
||||
|
||||
#include "graphics.h"
|
||||
|
|
@ -17,18 +17,19 @@
|
|||
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||
|
||||
namespace wl {
|
||||
using display_internal_t = util::safe_ptr<wl_display, wl_display_disconnect>;
|
||||
using display_internal_t = util::safe_ptr<wl_display, wl_display_disconnect>;
|
||||
|
||||
class frame_t {
|
||||
public:
|
||||
class frame_t {
|
||||
public:
|
||||
frame_t();
|
||||
egl::surface_descriptor_t sd;
|
||||
|
||||
void destroy();
|
||||
};
|
||||
void
|
||||
destroy();
|
||||
};
|
||||
|
||||
class dmabuf_t {
|
||||
public:
|
||||
class dmabuf_t {
|
||||
public:
|
||||
enum status_e {
|
||||
WAITING,
|
||||
READY,
|
||||
|
|
@ -38,16 +39,20 @@ public:
|
|||
dmabuf_t(dmabuf_t &&) = delete;
|
||||
dmabuf_t(const dmabuf_t &) = delete;
|
||||
|
||||
dmabuf_t &operator=(const dmabuf_t &) = delete;
|
||||
dmabuf_t &operator=(dmabuf_t &&) = delete;
|
||||
dmabuf_t &
|
||||
operator=(const dmabuf_t &) = delete;
|
||||
dmabuf_t &
|
||||
operator=(dmabuf_t &&) = delete;
|
||||
|
||||
dmabuf_t();
|
||||
|
||||
void listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false);
|
||||
void
|
||||
listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false);
|
||||
|
||||
~dmabuf_t();
|
||||
|
||||
void frame(
|
||||
void
|
||||
frame(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t width, std::uint32_t height,
|
||||
std::uint32_t x, std::uint32_t y,
|
||||
|
|
@ -56,7 +61,8 @@ public:
|
|||
std::uint32_t high, std::uint32_t low,
|
||||
std::uint32_t obj_count);
|
||||
|
||||
void object(
|
||||
void
|
||||
object(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t index,
|
||||
std::int32_t fd,
|
||||
|
|
@ -65,15 +71,18 @@ public:
|
|||
std::uint32_t stride,
|
||||
std::uint32_t plane_index);
|
||||
|
||||
void ready(
|
||||
void
|
||||
ready(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec);
|
||||
|
||||
void cancel(
|
||||
void
|
||||
cancel(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t reason);
|
||||
|
||||
inline frame_t *get_next_frame() {
|
||||
inline frame_t *
|
||||
get_next_frame() {
|
||||
return current_frame == &frames[0] ? &frames[1] : &frames[0];
|
||||
}
|
||||
|
||||
|
|
@ -83,25 +92,33 @@ public:
|
|||
frame_t *current_frame;
|
||||
|
||||
zwlr_export_dmabuf_frame_v1_listener listener;
|
||||
};
|
||||
};
|
||||
|
||||
class monitor_t {
|
||||
public:
|
||||
class monitor_t {
|
||||
public:
|
||||
monitor_t(monitor_t &&) = delete;
|
||||
monitor_t(const monitor_t &) = delete;
|
||||
|
||||
monitor_t &operator=(const monitor_t &) = delete;
|
||||
monitor_t &operator=(monitor_t &&) = delete;
|
||||
monitor_t &
|
||||
operator=(const monitor_t &) = delete;
|
||||
monitor_t &
|
||||
operator=(monitor_t &&) = delete;
|
||||
|
||||
monitor_t(wl_output *output);
|
||||
|
||||
void xdg_name(zxdg_output_v1 *, const char *name);
|
||||
void xdg_description(zxdg_output_v1 *, const char *description);
|
||||
void xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y);
|
||||
void xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height);
|
||||
void xdg_done(zxdg_output_v1 *);
|
||||
void
|
||||
xdg_name(zxdg_output_v1 *, const char *name);
|
||||
void
|
||||
xdg_description(zxdg_output_v1 *, const char *description);
|
||||
void
|
||||
xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y);
|
||||
void
|
||||
xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height);
|
||||
void
|
||||
xdg_done(zxdg_output_v1 *);
|
||||
|
||||
void listen(zxdg_output_manager_v1 *output_manager);
|
||||
void
|
||||
listen(zxdg_output_manager_v1 *output_manager);
|
||||
|
||||
wl_output *output;
|
||||
|
||||
|
|
@ -111,15 +128,15 @@ public:
|
|||
platf::touch_port_t viewport;
|
||||
|
||||
zxdg_output_v1_listener listener;
|
||||
};
|
||||
};
|
||||
|
||||
class interface_t {
|
||||
class interface_t {
|
||||
struct bind_t {
|
||||
std::uint32_t id;
|
||||
std::uint32_t version;
|
||||
};
|
||||
|
||||
public:
|
||||
public:
|
||||
enum interface_e {
|
||||
XDG_OUTPUT,
|
||||
WLR_EXPORT_DMABUF,
|
||||
|
|
@ -129,57 +146,69 @@ public:
|
|||
interface_t(interface_t &&) = delete;
|
||||
interface_t(const interface_t &) = delete;
|
||||
|
||||
interface_t &operator=(const interface_t &) = delete;
|
||||
interface_t &operator=(interface_t &&) = delete;
|
||||
interface_t &
|
||||
operator=(const interface_t &) = delete;
|
||||
interface_t &
|
||||
operator=(interface_t &&) = delete;
|
||||
|
||||
interface_t() noexcept;
|
||||
|
||||
void listen(wl_registry *registry);
|
||||
void
|
||||
listen(wl_registry *registry);
|
||||
|
||||
std::vector<std::unique_ptr<monitor_t>> monitors;
|
||||
|
||||
zwlr_export_dmabuf_manager_v1 *dmabuf_manager;
|
||||
zxdg_output_manager_v1 *output_manager;
|
||||
|
||||
bool operator[](interface_e bit) const {
|
||||
bool
|
||||
operator[](interface_e bit) const {
|
||||
return interface[bit];
|
||||
}
|
||||
|
||||
private:
|
||||
void add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version);
|
||||
void del_interface(wl_registry *registry, uint32_t id);
|
||||
private:
|
||||
void
|
||||
add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version);
|
||||
void
|
||||
del_interface(wl_registry *registry, uint32_t id);
|
||||
|
||||
std::bitset<MAX_INTERFACES> interface;
|
||||
|
||||
wl_registry_listener listener;
|
||||
};
|
||||
};
|
||||
|
||||
class display_t {
|
||||
public:
|
||||
class display_t {
|
||||
public:
|
||||
/**
|
||||
* Initialize display with display_name
|
||||
* If display_name == nullptr -> display_name = std::getenv("WAYLAND_DISPLAY")
|
||||
*/
|
||||
int init(const char *display_name = nullptr);
|
||||
int
|
||||
init(const char *display_name = nullptr);
|
||||
|
||||
// Roundtrip with Wayland connection
|
||||
void roundtrip();
|
||||
void
|
||||
roundtrip();
|
||||
|
||||
// Get the registry associated with the display
|
||||
// No need to manually free the registry
|
||||
wl_registry *registry();
|
||||
wl_registry *
|
||||
registry();
|
||||
|
||||
inline display_internal_t::pointer get() {
|
||||
inline display_internal_t::pointer
|
||||
get() {
|
||||
return display_internal.get();
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
display_internal_t display_internal;
|
||||
};
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<monitor_t>> monitors(const char *display_name = nullptr);
|
||||
std::vector<std::unique_ptr<monitor_t>>
|
||||
monitors(const char *display_name = nullptr);
|
||||
|
||||
int init();
|
||||
int
|
||||
init();
|
||||
} // namespace wl
|
||||
#else
|
||||
|
||||
|
|
@ -187,17 +216,20 @@ struct wl_output;
|
|||
struct zxdg_output_manager_v1;
|
||||
|
||||
namespace wl {
|
||||
class monitor_t {
|
||||
public:
|
||||
class monitor_t {
|
||||
public:
|
||||
monitor_t(monitor_t &&) = delete;
|
||||
monitor_t(const monitor_t &) = delete;
|
||||
|
||||
monitor_t &operator=(const monitor_t &) = delete;
|
||||
monitor_t &operator=(monitor_t &&) = delete;
|
||||
monitor_t &
|
||||
operator=(const monitor_t &) = delete;
|
||||
monitor_t &
|
||||
operator=(monitor_t &&) = delete;
|
||||
|
||||
monitor_t(wl_output *output);
|
||||
|
||||
void listen(zxdg_output_manager_v1 *output_manager);
|
||||
void
|
||||
listen(zxdg_output_manager_v1 *output_manager);
|
||||
|
||||
wl_output *output;
|
||||
|
||||
|
|
@ -205,11 +237,13 @@ public:
|
|||
std::string description;
|
||||
|
||||
platf::touch_port_t viewport;
|
||||
};
|
||||
};
|
||||
|
||||
inline std::vector<std::unique_ptr<monitor_t>> monitors(const char *display_name = nullptr) { return {}; }
|
||||
inline std::vector<std::unique_ptr<monitor_t>>
|
||||
monitors(const char *display_name = nullptr) { return {}; }
|
||||
|
||||
inline int init() { return -1; }
|
||||
inline int
|
||||
init() { return -1; }
|
||||
} // namespace wl
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -7,23 +7,24 @@
|
|||
|
||||
using namespace std::literals;
|
||||
namespace wl {
|
||||
static int env_width;
|
||||
static int env_height;
|
||||
static int env_width;
|
||||
static int env_height;
|
||||
|
||||
struct img_t : public platf::img_t {
|
||||
struct img_t: public platf::img_t {
|
||||
~img_t() override {
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class wlr_t : public platf::display_t {
|
||||
public:
|
||||
int init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
class wlr_t: public platf::display_t {
|
||||
public:
|
||||
int
|
||||
init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
delay = std::chrono::nanoseconds { 1s } / config.framerate;
|
||||
mem_type = hwdevice_type;
|
||||
|
||||
if(display.init()) {
|
||||
if (display.init()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -31,22 +32,22 @@ public:
|
|||
|
||||
display.roundtrip();
|
||||
|
||||
if(!interface[wl::interface_t::XDG_OUTPUT]) {
|
||||
if (!interface[wl::interface_t::XDG_OUTPUT]) {
|
||||
BOOST_LOG(error) << "Missing Wayland wire for xdg_output"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
|
||||
if (!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
|
||||
BOOST_LOG(error) << "Missing Wayland wire for wlr-export-dmabuf"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto monitor = interface.monitors[0].get();
|
||||
|
||||
if(!display_name.empty()) {
|
||||
if (!display_name.empty()) {
|
||||
auto streamedMonitor = util::from_view(display_name);
|
||||
|
||||
if(streamedMonitor >= 0 && streamedMonitor < interface.monitors.size()) {
|
||||
if (streamedMonitor >= 0 && streamedMonitor < interface.monitors.size()) {
|
||||
monitor = interface.monitors[streamedMonitor].get();
|
||||
}
|
||||
}
|
||||
|
|
@ -73,29 +74,30 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dummy_img(platf::img_t *img) override {
|
||||
int
|
||||
dummy_img(platf::img_t *img) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline platf::capture_e snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
|
||||
inline platf::capture_e
|
||||
snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
|
||||
auto to = std::chrono::steady_clock::now() + timeout;
|
||||
|
||||
dmabuf.listen(interface.dmabuf_manager, output, cursor);
|
||||
do {
|
||||
display.roundtrip();
|
||||
|
||||
if(to < std::chrono::steady_clock::now()) {
|
||||
if (to < std::chrono::steady_clock::now()) {
|
||||
return platf::capture_e::timeout;
|
||||
}
|
||||
} while(dmabuf.status == dmabuf_t::WAITING);
|
||||
} while (dmabuf.status == dmabuf_t::WAITING);
|
||||
|
||||
auto current_frame = dmabuf.current_frame;
|
||||
|
||||
if(
|
||||
if (
|
||||
dmabuf.status == dmabuf_t::REINIT ||
|
||||
current_frame->sd.width != width ||
|
||||
current_frame->sd.height != height) {
|
||||
|
||||
return platf::capture_e::reinit;
|
||||
}
|
||||
|
||||
|
|
@ -111,26 +113,27 @@ public:
|
|||
dmabuf_t dmabuf;
|
||||
|
||||
wl_output *output;
|
||||
};
|
||||
};
|
||||
|
||||
class wlr_ram_t : public wlr_t {
|
||||
public:
|
||||
platf::capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<platf::img_t> img, bool *cursor) override {
|
||||
class wlr_ram_t: public wlr_t {
|
||||
public:
|
||||
platf::capture_e
|
||||
capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<platf::img_t> img, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
while(img) {
|
||||
while (img) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if(next_frame > now) {
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
}
|
||||
while(next_frame > now) {
|
||||
while (next_frame > now) {
|
||||
now = std::chrono::steady_clock::now();
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
auto status = snapshot(img.get(), 1000ms, *cursor);
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case platf::capture_e::reinit:
|
||||
case platf::capture_e::error:
|
||||
return status;
|
||||
|
|
@ -141,7 +144,7 @@ public:
|
|||
img = snapshot_cb(img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
@ -149,9 +152,10 @@ public:
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
platf::capture_e snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
|
||||
platf::capture_e
|
||||
snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
|
||||
auto status = wlr_t::snapshot(img_out_base, timeout, cursor);
|
||||
if(status != platf::capture_e::ok) {
|
||||
if (status != platf::capture_e::ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +163,7 @@ public:
|
|||
|
||||
auto rgb_opt = egl::import_source(egl_display.get(), current_frame->sd);
|
||||
|
||||
if(!rgb_opt) {
|
||||
if (!rgb_opt) {
|
||||
return platf::capture_e::reinit;
|
||||
}
|
||||
|
||||
|
|
@ -176,18 +180,19 @@ public:
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
int init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
if(wlr_t::init(hwdevice_type, display_name, config)) {
|
||||
int
|
||||
init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
if (wlr_t::init(hwdevice_type, display_name, config)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
egl_display = egl::make_display(display.get());
|
||||
if(!egl_display) {
|
||||
if (!egl_display) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto ctx_opt = egl::make_ctx(egl_display.get());
|
||||
if(!ctx_opt) {
|
||||
if (!ctx_opt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -196,15 +201,17 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(platf::pix_fmt_e pix_fmt) override {
|
||||
if(mem_type == platf::mem_type_e::vaapi) {
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(platf::pix_fmt_e pix_fmt) override {
|
||||
if (mem_type == platf::mem_type_e::vaapi) {
|
||||
return va::make_hwdevice(width, height, false);
|
||||
}
|
||||
|
||||
return std::make_shared<platf::hwdevice_t>();
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::img_t> alloc_img() override {
|
||||
std::shared_ptr<platf::img_t>
|
||||
alloc_img() override {
|
||||
auto img = std::make_shared<img_t>();
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
|
|
@ -217,26 +224,27 @@ public:
|
|||
|
||||
egl::display_t egl_display;
|
||||
egl::ctx_t ctx;
|
||||
};
|
||||
};
|
||||
|
||||
class wlr_vram_t : public wlr_t {
|
||||
public:
|
||||
platf::capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<platf::img_t> img, bool *cursor) override {
|
||||
class wlr_vram_t: public wlr_t {
|
||||
public:
|
||||
platf::capture_e
|
||||
capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<platf::img_t> img, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
while(img) {
|
||||
while (img) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if(next_frame > now) {
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
}
|
||||
while(next_frame > now) {
|
||||
while (next_frame > now) {
|
||||
now = std::chrono::steady_clock::now();
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
auto status = snapshot(img.get(), 1000ms, *cursor);
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case platf::capture_e::reinit:
|
||||
case platf::capture_e::error:
|
||||
return status;
|
||||
|
|
@ -247,7 +255,7 @@ public:
|
|||
img = snapshot_cb(img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
@ -255,13 +263,14 @@ public:
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
platf::capture_e snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
|
||||
platf::capture_e
|
||||
snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
|
||||
auto status = wlr_t::snapshot(img_out_base, timeout, cursor);
|
||||
if(status != platf::capture_e::ok) {
|
||||
if (status != platf::capture_e::ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
auto img = (egl::img_descriptor_t *)img_out_base;
|
||||
auto img = (egl::img_descriptor_t *) img_out_base;
|
||||
img->reset();
|
||||
|
||||
auto current_frame = dmabuf.current_frame;
|
||||
|
|
@ -277,7 +286,8 @@ public:
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::img_t> alloc_img() override {
|
||||
std::shared_ptr<platf::img_t>
|
||||
alloc_img() override {
|
||||
auto img = std::make_shared<egl::img_descriptor_t>();
|
||||
|
||||
img->sequence = 0;
|
||||
|
|
@ -290,33 +300,36 @@ public:
|
|||
return img;
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(platf::pix_fmt_e pix_fmt) override {
|
||||
if(mem_type == platf::mem_type_e::vaapi) {
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(platf::pix_fmt_e pix_fmt) override {
|
||||
if (mem_type == platf::mem_type_e::vaapi) {
|
||||
return va::make_hwdevice(width, height, 0, 0, true);
|
||||
}
|
||||
|
||||
return std::make_shared<platf::hwdevice_t>();
|
||||
}
|
||||
|
||||
int dummy_img(platf::img_t *img) override {
|
||||
int
|
||||
dummy_img(platf::img_t *img) override {
|
||||
return snapshot(img, 1000ms, false) != platf::capture_e::ok;
|
||||
}
|
||||
|
||||
std::uint64_t sequence {};
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace wl
|
||||
|
||||
namespace platf {
|
||||
std::shared_ptr<display_t> wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if(hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) {
|
||||
std::shared_ptr<display_t>
|
||||
wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) {
|
||||
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(hwdevice_type == platf::mem_type_e::vaapi) {
|
||||
if (hwdevice_type == platf::mem_type_e::vaapi) {
|
||||
auto wlr = std::make_shared<wl::wlr_vram_t>();
|
||||
if(wlr->init(hwdevice_type, display_name, config)) {
|
||||
if (wlr->init(hwdevice_type, display_name, config)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -324,18 +337,19 @@ std::shared_ptr<display_t> wl_display(mem_type_e hwdevice_type, const std::strin
|
|||
}
|
||||
|
||||
auto wlr = std::make_shared<wl::wlr_ram_t>();
|
||||
if(wlr->init(hwdevice_type, display_name, config)) {
|
||||
if (wlr->init(hwdevice_type, display_name, config)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return wlr;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> wl_display_names() {
|
||||
std::vector<std::string>
|
||||
wl_display_names() {
|
||||
std::vector<std::string> display_names;
|
||||
|
||||
wl::display_t display;
|
||||
if(display.init()) {
|
||||
if (display.init()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -344,12 +358,12 @@ std::vector<std::string> wl_display_names() {
|
|||
|
||||
display.roundtrip();
|
||||
|
||||
if(!interface[wl::interface_t::XDG_OUTPUT]) {
|
||||
if (!interface[wl::interface_t::XDG_OUTPUT]) {
|
||||
BOOST_LOG(warning) << "Missing Wayland wire for xdg_output"sv;
|
||||
return {};
|
||||
}
|
||||
|
||||
if(!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
|
||||
if (!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
|
||||
BOOST_LOG(warning) << "Missing Wayland wire for wlr-export-dmabuf"sv;
|
||||
return {};
|
||||
}
|
||||
|
|
@ -357,22 +371,22 @@ std::vector<std::string> wl_display_names() {
|
|||
wl::env_width = 0;
|
||||
wl::env_height = 0;
|
||||
|
||||
for(auto &monitor : interface.monitors) {
|
||||
for (auto &monitor : interface.monitors) {
|
||||
monitor->listen(interface.output_manager);
|
||||
}
|
||||
|
||||
display.roundtrip();
|
||||
|
||||
for(int x = 0; x < interface.monitors.size(); ++x) {
|
||||
for (int x = 0; x < interface.monitors.size(); ++x) {
|
||||
auto monitor = interface.monitors[x].get();
|
||||
|
||||
wl::env_width = std::max(wl::env_width, (int)(monitor->viewport.offset_x + monitor->viewport.width));
|
||||
wl::env_height = std::max(wl::env_height, (int)(monitor->viewport.offset_y + monitor->viewport.height));
|
||||
wl::env_width = std::max(wl::env_width, (int) (monitor->viewport.offset_x + monitor->viewport.width));
|
||||
wl::env_height = std::max(wl::env_height, (int) (monitor->viewport.offset_y + monitor->viewport.height));
|
||||
|
||||
display_names.emplace_back(std::to_string(x));
|
||||
}
|
||||
|
||||
return display_names;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -30,15 +30,17 @@
|
|||
using namespace std::literals;
|
||||
|
||||
namespace platf {
|
||||
int load_xcb();
|
||||
int load_x11();
|
||||
int
|
||||
load_xcb();
|
||||
int
|
||||
load_x11();
|
||||
|
||||
namespace x11 {
|
||||
namespace x11 {
|
||||
#define _FN(x, ret, args) \
|
||||
typedef ret(*x##_fn) args; \
|
||||
static x##_fn x
|
||||
|
||||
_FN(GetImage, XImage *,
|
||||
_FN(GetImage, XImage *,
|
||||
(
|
||||
Display * display,
|
||||
Drawable d,
|
||||
|
|
@ -47,126 +49,129 @@ _FN(GetImage, XImage *,
|
|||
unsigned long plane_mask,
|
||||
int format));
|
||||
|
||||
_FN(OpenDisplay, Display *, (_Xconst char *display_name));
|
||||
_FN(GetWindowAttributes, Status,
|
||||
_FN(OpenDisplay, Display *, (_Xconst char *display_name));
|
||||
_FN(GetWindowAttributes, Status,
|
||||
(
|
||||
Display * display,
|
||||
Window w,
|
||||
XWindowAttributes *window_attributes_return));
|
||||
|
||||
_FN(CloseDisplay, int, (Display * display));
|
||||
_FN(Free, int, (void *data));
|
||||
_FN(InitThreads, Status, (void));
|
||||
_FN(CloseDisplay, int, (Display * display));
|
||||
_FN(Free, int, (void *data));
|
||||
_FN(InitThreads, Status, (void) );
|
||||
|
||||
namespace rr {
|
||||
_FN(GetScreenResources, XRRScreenResources *, (Display * dpy, Window window));
|
||||
_FN(GetOutputInfo, XRROutputInfo *, (Display * dpy, XRRScreenResources *resources, RROutput output));
|
||||
_FN(GetCrtcInfo, XRRCrtcInfo *, (Display * dpy, XRRScreenResources *resources, RRCrtc crtc));
|
||||
_FN(FreeScreenResources, void, (XRRScreenResources * resources));
|
||||
_FN(FreeOutputInfo, void, (XRROutputInfo * outputInfo));
|
||||
_FN(FreeCrtcInfo, void, (XRRCrtcInfo * crtcInfo));
|
||||
namespace rr {
|
||||
_FN(GetScreenResources, XRRScreenResources *, (Display * dpy, Window window));
|
||||
_FN(GetOutputInfo, XRROutputInfo *, (Display * dpy, XRRScreenResources *resources, RROutput output));
|
||||
_FN(GetCrtcInfo, XRRCrtcInfo *, (Display * dpy, XRRScreenResources *resources, RRCrtc crtc));
|
||||
_FN(FreeScreenResources, void, (XRRScreenResources * resources));
|
||||
_FN(FreeOutputInfo, void, (XRROutputInfo * outputInfo));
|
||||
_FN(FreeCrtcInfo, void, (XRRCrtcInfo * crtcInfo));
|
||||
|
||||
static int init() {
|
||||
static int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libXrandr.so.2", "libXrandr.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&GetScreenResources, "XRRGetScreenResources" },
|
||||
{ (dyn::apiproc *)&GetOutputInfo, "XRRGetOutputInfo" },
|
||||
{ (dyn::apiproc *)&GetCrtcInfo, "XRRGetCrtcInfo" },
|
||||
{ (dyn::apiproc *)&FreeScreenResources, "XRRFreeScreenResources" },
|
||||
{ (dyn::apiproc *)&FreeOutputInfo, "XRRFreeOutputInfo" },
|
||||
{ (dyn::apiproc *)&FreeCrtcInfo, "XRRFreeCrtcInfo" },
|
||||
{ (dyn::apiproc *) &GetScreenResources, "XRRGetScreenResources" },
|
||||
{ (dyn::apiproc *) &GetOutputInfo, "XRRGetOutputInfo" },
|
||||
{ (dyn::apiproc *) &GetCrtcInfo, "XRRGetCrtcInfo" },
|
||||
{ (dyn::apiproc *) &FreeScreenResources, "XRRFreeScreenResources" },
|
||||
{ (dyn::apiproc *) &FreeOutputInfo, "XRRFreeOutputInfo" },
|
||||
{ (dyn::apiproc *) &FreeCrtcInfo, "XRRFreeCrtcInfo" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rr
|
||||
namespace fix {
|
||||
_FN(GetCursorImage, XFixesCursorImage *, (Display * dpy));
|
||||
} // namespace rr
|
||||
namespace fix {
|
||||
_FN(GetCursorImage, XFixesCursorImage *, (Display * dpy));
|
||||
|
||||
static int init() {
|
||||
static int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libXfixes.so.3", "libXfixes.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&GetCursorImage, "XFixesGetCursorImage" },
|
||||
{ (dyn::apiproc *) &GetCursorImage, "XFixesGetCursorImage" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
} // namespace fix
|
||||
}
|
||||
} // namespace fix
|
||||
|
||||
static int init() {
|
||||
static int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libX11.so.6", "libX11.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&GetImage, "XGetImage" },
|
||||
{ (dyn::apiproc *)&OpenDisplay, "XOpenDisplay" },
|
||||
{ (dyn::apiproc *)&GetWindowAttributes, "XGetWindowAttributes" },
|
||||
{ (dyn::apiproc *)&Free, "XFree" },
|
||||
{ (dyn::apiproc *)&CloseDisplay, "XCloseDisplay" },
|
||||
{ (dyn::apiproc *)&InitThreads, "XInitThreads" },
|
||||
{ (dyn::apiproc *) &GetImage, "XGetImage" },
|
||||
{ (dyn::apiproc *) &OpenDisplay, "XOpenDisplay" },
|
||||
{ (dyn::apiproc *) &GetWindowAttributes, "XGetWindowAttributes" },
|
||||
{ (dyn::apiproc *) &Free, "XFree" },
|
||||
{ (dyn::apiproc *) &CloseDisplay, "XCloseDisplay" },
|
||||
{ (dyn::apiproc *) &InitThreads, "XInitThreads" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
} // namespace x11
|
||||
}
|
||||
} // namespace x11
|
||||
|
||||
namespace xcb {
|
||||
static xcb_extension_t *shm_id;
|
||||
namespace xcb {
|
||||
static xcb_extension_t *shm_id;
|
||||
|
||||
_FN(shm_get_image_reply, xcb_shm_get_image_reply_t *,
|
||||
_FN(shm_get_image_reply, xcb_shm_get_image_reply_t *,
|
||||
(
|
||||
xcb_connection_t * c,
|
||||
xcb_shm_get_image_cookie_t cookie,
|
||||
xcb_generic_error_t **e));
|
||||
|
||||
_FN(shm_get_image_unchecked, xcb_shm_get_image_cookie_t,
|
||||
_FN(shm_get_image_unchecked, xcb_shm_get_image_cookie_t,
|
||||
(
|
||||
xcb_connection_t * c,
|
||||
xcb_drawable_t drawable,
|
||||
|
|
@ -177,147 +182,158 @@ _FN(shm_get_image_unchecked, xcb_shm_get_image_cookie_t,
|
|||
xcb_shm_seg_t shmseg,
|
||||
uint32_t offset));
|
||||
|
||||
_FN(shm_attach, xcb_void_cookie_t,
|
||||
_FN(shm_attach, xcb_void_cookie_t,
|
||||
(xcb_connection_t * c,
|
||||
xcb_shm_seg_t shmseg,
|
||||
uint32_t shmid,
|
||||
uint8_t read_only));
|
||||
|
||||
_FN(get_extension_data, xcb_query_extension_reply_t *,
|
||||
_FN(get_extension_data, xcb_query_extension_reply_t *,
|
||||
(xcb_connection_t * c, xcb_extension_t *ext));
|
||||
|
||||
_FN(get_setup, xcb_setup_t *, (xcb_connection_t * c));
|
||||
_FN(disconnect, void, (xcb_connection_t * c));
|
||||
_FN(connection_has_error, int, (xcb_connection_t * c));
|
||||
_FN(connect, xcb_connection_t *, (const char *displayname, int *screenp));
|
||||
_FN(setup_roots_iterator, xcb_screen_iterator_t, (const xcb_setup_t *R));
|
||||
_FN(generate_id, std::uint32_t, (xcb_connection_t * c));
|
||||
_FN(get_setup, xcb_setup_t *, (xcb_connection_t * c));
|
||||
_FN(disconnect, void, (xcb_connection_t * c));
|
||||
_FN(connection_has_error, int, (xcb_connection_t * c));
|
||||
_FN(connect, xcb_connection_t *, (const char *displayname, int *screenp));
|
||||
_FN(setup_roots_iterator, xcb_screen_iterator_t, (const xcb_setup_t *R));
|
||||
_FN(generate_id, std::uint32_t, (xcb_connection_t * c));
|
||||
|
||||
int init_shm() {
|
||||
int
|
||||
init_shm() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libxcb-shm.so.0", "libxcb-shm.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&shm_id, "xcb_shm_id" },
|
||||
{ (dyn::apiproc *)&shm_get_image_reply, "xcb_shm_get_image_reply" },
|
||||
{ (dyn::apiproc *)&shm_get_image_unchecked, "xcb_shm_get_image_unchecked" },
|
||||
{ (dyn::apiproc *)&shm_attach, "xcb_shm_attach" },
|
||||
{ (dyn::apiproc *) &shm_id, "xcb_shm_id" },
|
||||
{ (dyn::apiproc *) &shm_get_image_reply, "xcb_shm_get_image_reply" },
|
||||
{ (dyn::apiproc *) &shm_get_image_unchecked, "xcb_shm_get_image_unchecked" },
|
||||
{ (dyn::apiproc *) &shm_attach, "xcb_shm_attach" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int init() {
|
||||
int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libxcb.so.1", "libxcb.so" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&get_extension_data, "xcb_get_extension_data" },
|
||||
{ (dyn::apiproc *)&get_setup, "xcb_get_setup" },
|
||||
{ (dyn::apiproc *)&disconnect, "xcb_disconnect" },
|
||||
{ (dyn::apiproc *)&connection_has_error, "xcb_connection_has_error" },
|
||||
{ (dyn::apiproc *)&connect, "xcb_connect" },
|
||||
{ (dyn::apiproc *)&setup_roots_iterator, "xcb_setup_roots_iterator" },
|
||||
{ (dyn::apiproc *)&generate_id, "xcb_generate_id" },
|
||||
{ (dyn::apiproc *) &get_extension_data, "xcb_get_extension_data" },
|
||||
{ (dyn::apiproc *) &get_setup, "xcb_get_setup" },
|
||||
{ (dyn::apiproc *) &disconnect, "xcb_disconnect" },
|
||||
{ (dyn::apiproc *) &connection_has_error, "xcb_connection_has_error" },
|
||||
{ (dyn::apiproc *) &connect, "xcb_connect" },
|
||||
{ (dyn::apiproc *) &setup_roots_iterator, "xcb_setup_roots_iterator" },
|
||||
{ (dyn::apiproc *) &generate_id, "xcb_generate_id" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#undef _FN
|
||||
} // namespace xcb
|
||||
} // namespace xcb
|
||||
|
||||
void freeImage(XImage *);
|
||||
void freeX(XFixesCursorImage *);
|
||||
void
|
||||
freeImage(XImage *);
|
||||
void
|
||||
freeX(XFixesCursorImage *);
|
||||
|
||||
using xcb_connect_t = util::dyn_safe_ptr<xcb_connection_t, &xcb::disconnect>;
|
||||
using xcb_img_t = util::c_ptr<xcb_shm_get_image_reply_t>;
|
||||
using xcb_connect_t = util::dyn_safe_ptr<xcb_connection_t, &xcb::disconnect>;
|
||||
using xcb_img_t = util::c_ptr<xcb_shm_get_image_reply_t>;
|
||||
|
||||
using ximg_t = util::safe_ptr<XImage, freeImage>;
|
||||
using xcursor_t = util::safe_ptr<XFixesCursorImage, freeX>;
|
||||
using ximg_t = util::safe_ptr<XImage, freeImage>;
|
||||
using xcursor_t = util::safe_ptr<XFixesCursorImage, freeX>;
|
||||
|
||||
using crtc_info_t = util::dyn_safe_ptr<_XRRCrtcInfo, &x11::rr::FreeCrtcInfo>;
|
||||
using output_info_t = util::dyn_safe_ptr<_XRROutputInfo, &x11::rr::FreeOutputInfo>;
|
||||
using screen_res_t = util::dyn_safe_ptr<_XRRScreenResources, &x11::rr::FreeScreenResources>;
|
||||
using crtc_info_t = util::dyn_safe_ptr<_XRRCrtcInfo, &x11::rr::FreeCrtcInfo>;
|
||||
using output_info_t = util::dyn_safe_ptr<_XRROutputInfo, &x11::rr::FreeOutputInfo>;
|
||||
using screen_res_t = util::dyn_safe_ptr<_XRRScreenResources, &x11::rr::FreeScreenResources>;
|
||||
|
||||
class shm_id_t {
|
||||
public:
|
||||
shm_id_t() : id { -1 } {}
|
||||
shm_id_t(int id) : id { id } {}
|
||||
shm_id_t(shm_id_t &&other) noexcept : id(other.id) {
|
||||
class shm_id_t {
|
||||
public:
|
||||
shm_id_t():
|
||||
id { -1 } {}
|
||||
shm_id_t(int id):
|
||||
id { id } {}
|
||||
shm_id_t(shm_id_t &&other) noexcept:
|
||||
id(other.id) {
|
||||
other.id = -1;
|
||||
}
|
||||
|
||||
~shm_id_t() {
|
||||
if(id != -1) {
|
||||
if (id != -1) {
|
||||
shmctl(id, IPC_RMID, nullptr);
|
||||
id = -1;
|
||||
}
|
||||
}
|
||||
int id;
|
||||
};
|
||||
};
|
||||
|
||||
class shm_data_t {
|
||||
public:
|
||||
shm_data_t() : data { (void *)-1 } {}
|
||||
shm_data_t(void *data) : data { data } {}
|
||||
class shm_data_t {
|
||||
public:
|
||||
shm_data_t():
|
||||
data { (void *) -1 } {}
|
||||
shm_data_t(void *data):
|
||||
data { data } {}
|
||||
|
||||
shm_data_t(shm_data_t &&other) noexcept : data(other.data) {
|
||||
other.data = (void *)-1;
|
||||
shm_data_t(shm_data_t &&other) noexcept:
|
||||
data(other.data) {
|
||||
other.data = (void *) -1;
|
||||
}
|
||||
|
||||
~shm_data_t() {
|
||||
if((std::uintptr_t)data != -1) {
|
||||
if ((std::uintptr_t) data != -1) {
|
||||
shmdt(data);
|
||||
}
|
||||
}
|
||||
|
||||
void *data;
|
||||
};
|
||||
};
|
||||
|
||||
struct x11_img_t : public img_t {
|
||||
struct x11_img_t: public img_t {
|
||||
ximg_t img;
|
||||
};
|
||||
};
|
||||
|
||||
struct shm_img_t : public img_t {
|
||||
struct shm_img_t: public img_t {
|
||||
~shm_img_t() override {
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) {
|
||||
static void
|
||||
blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) {
|
||||
xcursor_t overlay { x11::fix::GetCursorImage(display) };
|
||||
|
||||
if(!overlay) {
|
||||
if (!overlay) {
|
||||
BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv;
|
||||
return;
|
||||
}
|
||||
|
|
@ -328,33 +344,33 @@ static void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY)
|
|||
overlay->x -= offsetX;
|
||||
overlay->y -= offsetY;
|
||||
|
||||
overlay->x = std::max((short)0, overlay->x);
|
||||
overlay->y = std::max((short)0, overlay->y);
|
||||
overlay->x = std::max((short) 0, overlay->x);
|
||||
overlay->y = std::max((short) 0, overlay->y);
|
||||
|
||||
auto pixels = (int *)img.data;
|
||||
auto pixels = (int *) img.data;
|
||||
|
||||
auto screen_height = img.height;
|
||||
auto screen_width = img.width;
|
||||
|
||||
auto delta_height = std::min<uint16_t>(overlay->height, std::max(0, screen_height - overlay->y));
|
||||
auto delta_width = std::min<uint16_t>(overlay->width, std::max(0, screen_width - overlay->x));
|
||||
for(auto y = 0; y < delta_height; ++y) {
|
||||
for (auto y = 0; y < delta_height; ++y) {
|
||||
auto overlay_begin = &overlay->pixels[y * overlay->width];
|
||||
auto overlay_end = &overlay->pixels[y * overlay->width + delta_width];
|
||||
|
||||
auto pixels_begin = &pixels[(y + overlay->y) * (img.row_pitch / img.pixel_pitch) + overlay->x];
|
||||
|
||||
std::for_each(overlay_begin, overlay_end, [&](long pixel) {
|
||||
int *pixel_p = (int *)&pixel;
|
||||
int *pixel_p = (int *) &pixel;
|
||||
|
||||
auto colors_in = (uint8_t *)pixels_begin;
|
||||
auto colors_in = (uint8_t *) pixels_begin;
|
||||
|
||||
auto alpha = (*(uint *)pixel_p) >> 24u;
|
||||
if(alpha == 255) {
|
||||
auto alpha = (*(uint *) pixel_p) >> 24u;
|
||||
if (alpha == 255) {
|
||||
*pixels_begin = *pixel_p;
|
||||
}
|
||||
else {
|
||||
auto colors_out = (uint8_t *)pixel_p;
|
||||
auto colors_out = (uint8_t *) pixel_p;
|
||||
colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255;
|
||||
|
|
@ -362,9 +378,9 @@ static void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY)
|
|||
++pixels_begin;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct x11_attr_t : public display_t {
|
||||
struct x11_attr_t: public display_t {
|
||||
std::chrono::nanoseconds delay;
|
||||
|
||||
x11::xdisplay_t xdisplay;
|
||||
|
|
@ -379,12 +395,14 @@ struct x11_attr_t : public display_t {
|
|||
*/
|
||||
// int env_width, env_height;
|
||||
|
||||
x11_attr_t(mem_type_e mem_type) : xdisplay { x11::OpenDisplay(nullptr) }, xwindow {}, xattr {}, mem_type { mem_type } {
|
||||
x11_attr_t(mem_type_e mem_type):
|
||||
xdisplay { x11::OpenDisplay(nullptr) }, xwindow {}, xattr {}, mem_type { mem_type } {
|
||||
x11::InitThreads();
|
||||
}
|
||||
|
||||
int init(const std::string &display_name, const ::video::config_t &config) {
|
||||
if(!xdisplay) {
|
||||
int
|
||||
init(const std::string &display_name, const ::video::config_t &config) {
|
||||
if (!xdisplay) {
|
||||
BOOST_LOG(error) << "Could not open X11 display"sv;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -396,33 +414,33 @@ struct x11_attr_t : public display_t {
|
|||
refresh();
|
||||
|
||||
int streamedMonitor = -1;
|
||||
if(!display_name.empty()) {
|
||||
streamedMonitor = (int)util::from_view(display_name);
|
||||
if (!display_name.empty()) {
|
||||
streamedMonitor = (int) util::from_view(display_name);
|
||||
}
|
||||
|
||||
if(streamedMonitor != -1) {
|
||||
if (streamedMonitor != -1) {
|
||||
BOOST_LOG(info) << "Configuring selected monitor ("sv << streamedMonitor << ") to stream"sv;
|
||||
screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) };
|
||||
int output = screenr->noutput;
|
||||
|
||||
output_info_t result;
|
||||
int monitor = 0;
|
||||
for(int x = 0; x < output; ++x) {
|
||||
for (int x = 0; x < output; ++x) {
|
||||
output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) };
|
||||
if(out_info && out_info->connection == RR_Connected) {
|
||||
if(monitor++ == streamedMonitor) {
|
||||
if (out_info && out_info->connection == RR_Connected) {
|
||||
if (monitor++ == streamedMonitor) {
|
||||
result = std::move(out_info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
if (!result) {
|
||||
BOOST_LOG(error) << "Could not stream display number ["sv << streamedMonitor << "], there are only ["sv << monitor << "] displays."sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(result->crtc) {
|
||||
if (result->crtc) {
|
||||
crtc_info_t crt_info { x11::rr::GetCrtcInfo(xdisplay.get(), screenr.get(), result->crtc) };
|
||||
BOOST_LOG(info)
|
||||
<< "Streaming display: "sv << result->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y;
|
||||
|
|
@ -452,27 +470,29 @@ struct x11_attr_t : public display_t {
|
|||
/**
|
||||
* Called when the display attributes should change.
|
||||
*/
|
||||
void refresh() {
|
||||
void
|
||||
refresh() {
|
||||
x11::GetWindowAttributes(xdisplay.get(), xwindow, &xattr); //Update xattr's
|
||||
}
|
||||
|
||||
capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override {
|
||||
capture_e
|
||||
capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
while(img) {
|
||||
while (img) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if(next_frame > now) {
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
}
|
||||
while(next_frame > now) {
|
||||
while (next_frame > now) {
|
||||
std::this_thread::sleep_for(1ns);
|
||||
now = std::chrono::steady_clock::now();
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
auto status = snapshot(img.get(), 1000ms, *cursor);
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case platf::capture_e::reinit:
|
||||
case platf::capture_e::error:
|
||||
return status;
|
||||
|
|
@ -483,7 +503,7 @@ struct x11_attr_t : public display_t {
|
|||
img = snapshot_cb(img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
@ -491,42 +511,45 @@ struct x11_attr_t : public display_t {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
|
||||
capture_e
|
||||
snapshot(img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
|
||||
refresh();
|
||||
|
||||
//The whole X server changed, so we must reinit everything
|
||||
if(xattr.width != env_width || xattr.height != env_height) {
|
||||
if (xattr.width != env_width || xattr.height != env_height) {
|
||||
BOOST_LOG(warning) << "X dimensions changed in non-SHM mode, request reinit"sv;
|
||||
return capture_e::reinit;
|
||||
}
|
||||
XImage *img { x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) };
|
||||
|
||||
auto img_out = (x11_img_t *)img_out_base;
|
||||
auto img_out = (x11_img_t *) img_out_base;
|
||||
img_out->width = img->width;
|
||||
img_out->height = img->height;
|
||||
img_out->data = (uint8_t *)img->data;
|
||||
img_out->data = (uint8_t *) img->data;
|
||||
img_out->row_pitch = img->bytes_per_line;
|
||||
img_out->pixel_pitch = img->bits_per_pixel / 8;
|
||||
img_out->img.reset(img);
|
||||
|
||||
if(cursor) {
|
||||
if (cursor) {
|
||||
blend_cursor(xdisplay.get(), *img_out_base, offset_x, offset_y);
|
||||
}
|
||||
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
std::shared_ptr<img_t> alloc_img() override {
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override {
|
||||
return std::make_shared<x11_img_t>();
|
||||
}
|
||||
|
||||
std::shared_ptr<hwdevice_t> make_hwdevice(pix_fmt_e pix_fmt) override {
|
||||
if(mem_type == mem_type_e::vaapi) {
|
||||
std::shared_ptr<hwdevice_t>
|
||||
make_hwdevice(pix_fmt_e pix_fmt) override {
|
||||
if (mem_type == mem_type_e::vaapi) {
|
||||
return va::make_hwdevice(width, height, false);
|
||||
}
|
||||
|
||||
#ifdef SUNSHINE_BUILD_CUDA
|
||||
if(mem_type == mem_type_e::cuda) {
|
||||
if (mem_type == mem_type_e::cuda) {
|
||||
return cuda::make_hwdevice(width, height, false);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -534,13 +557,14 @@ struct x11_attr_t : public display_t {
|
|||
return std::make_shared<hwdevice_t>();
|
||||
}
|
||||
|
||||
int dummy_img(img_t *img) override {
|
||||
int
|
||||
dummy_img(img_t *img) override {
|
||||
snapshot(img, 0s, true);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct shm_attr_t : public x11_attr_t {
|
||||
struct shm_attr_t: public x11_attr_t {
|
||||
x11::xdisplay_t shm_xdisplay; // Prevent race condition with x11_attr_t::xdisplay
|
||||
xcb_connect_t xcb;
|
||||
xcb_screen_t *display;
|
||||
|
|
@ -552,38 +576,41 @@ struct shm_attr_t : public x11_attr_t {
|
|||
|
||||
task_pool_util::TaskPool::task_id_t refresh_task_id;
|
||||
|
||||
void delayed_refresh() {
|
||||
void
|
||||
delayed_refresh() {
|
||||
refresh();
|
||||
|
||||
refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id;
|
||||
}
|
||||
|
||||
shm_attr_t(mem_type_e mem_type) : x11_attr_t(mem_type), shm_xdisplay { x11::OpenDisplay(nullptr) } {
|
||||
shm_attr_t(mem_type_e mem_type):
|
||||
x11_attr_t(mem_type), shm_xdisplay { x11::OpenDisplay(nullptr) } {
|
||||
refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id;
|
||||
}
|
||||
|
||||
~shm_attr_t() override {
|
||||
while(!task_pool.cancel(refresh_task_id))
|
||||
while (!task_pool.cancel(refresh_task_id))
|
||||
;
|
||||
}
|
||||
|
||||
capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override {
|
||||
capture_e
|
||||
capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
while(img) {
|
||||
while (img) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if(next_frame > now) {
|
||||
if (next_frame > now) {
|
||||
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
|
||||
}
|
||||
while(next_frame > now) {
|
||||
while (next_frame > now) {
|
||||
std::this_thread::sleep_for(1ns);
|
||||
now = std::chrono::steady_clock::now();
|
||||
}
|
||||
next_frame = now + delay;
|
||||
|
||||
auto status = snapshot(img.get(), 1000ms, *cursor);
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case platf::capture_e::reinit:
|
||||
case platf::capture_e::error:
|
||||
return status;
|
||||
|
|
@ -594,7 +621,7 @@ struct shm_attr_t : public x11_attr_t {
|
|||
img = snapshot_cb(img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
@ -602,9 +629,10 @@ struct shm_attr_t : public x11_attr_t {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) {
|
||||
capture_e
|
||||
snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor) {
|
||||
//The whole X server changed, so we must reinit everything
|
||||
if(xattr.width != env_width || xattr.height != env_height) {
|
||||
if (xattr.width != env_width || xattr.height != env_height) {
|
||||
BOOST_LOG(warning) << "X dimensions changed in SHM mode, request reinit"sv;
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
|
@ -612,14 +640,14 @@ struct shm_attr_t : public x11_attr_t {
|
|||
auto img_cookie = xcb::shm_get_image_unchecked(xcb.get(), display->root, offset_x, offset_y, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0);
|
||||
|
||||
xcb_img_t img_reply { xcb::shm_get_image_reply(xcb.get(), img_cookie, nullptr) };
|
||||
if(!img_reply) {
|
||||
if (!img_reply) {
|
||||
BOOST_LOG(error) << "Could not get image reply"sv;
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
std::copy_n((std::uint8_t *)data.data, frame_size(), img->data);
|
||||
std::copy_n((std::uint8_t *) data.data, frame_size(), img->data);
|
||||
|
||||
if(cursor) {
|
||||
if (cursor) {
|
||||
blend_cursor(shm_xdisplay.get(), *img, offset_x, offset_y);
|
||||
}
|
||||
|
||||
|
|
@ -627,7 +655,8 @@ struct shm_attr_t : public x11_attr_t {
|
|||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<img_t> alloc_img() override {
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override {
|
||||
auto img = std::make_shared<shm_img_t>();
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
|
|
@ -638,22 +667,24 @@ struct shm_attr_t : public x11_attr_t {
|
|||
return img;
|
||||
}
|
||||
|
||||
int dummy_img(platf::img_t *img) override {
|
||||
int
|
||||
dummy_img(platf::img_t *img) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init(const std::string &display_name, const ::video::config_t &config) {
|
||||
if(x11_attr_t::init(display_name, config)) {
|
||||
int
|
||||
init(const std::string &display_name, const ::video::config_t &config) {
|
||||
if (x11_attr_t::init(display_name, config)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
shm_xdisplay.reset(x11::OpenDisplay(nullptr));
|
||||
xcb.reset(xcb::connect(nullptr, nullptr));
|
||||
if(xcb::connection_has_error(xcb.get())) {
|
||||
if (xcb::connection_has_error(xcb.get())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!xcb::get_extension_data(xcb.get(), xcb::shm_id)->present) {
|
||||
if (!xcb::get_extension_data(xcb.get(), xcb::shm_id)->present) {
|
||||
BOOST_LOG(error) << "Missing SHM extension"sv;
|
||||
|
||||
return -1;
|
||||
|
|
@ -664,7 +695,7 @@ struct shm_attr_t : public x11_attr_t {
|
|||
seg = xcb::generate_id(xcb.get());
|
||||
|
||||
shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777);
|
||||
if(shm_id.id == -1) {
|
||||
if (shm_id.id == -1) {
|
||||
BOOST_LOG(error) << "shmget failed"sv;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -672,7 +703,7 @@ struct shm_attr_t : public x11_attr_t {
|
|||
xcb::shm_attach(xcb.get(), seg, shm_id.id, false);
|
||||
data.data = shmat(shm_id.id, nullptr, 0);
|
||||
|
||||
if((uintptr_t)data.data == -1) {
|
||||
if ((uintptr_t) data.data == -1) {
|
||||
BOOST_LOG(error) << "shmat failed"sv;
|
||||
|
||||
return -1;
|
||||
|
|
@ -681,18 +712,20 @@ struct shm_attr_t : public x11_attr_t {
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t frame_size() {
|
||||
std::uint32_t
|
||||
frame_size() {
|
||||
return width * height * 4;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
std::shared_ptr<display_t> x11_display(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
if(hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) {
|
||||
std::shared_ptr<display_t>
|
||||
x11_display(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) {
|
||||
BOOST_LOG(error) << "Could not initialize x11 display with the given hw device type"sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(xcb::init_shm() || xcb::init() || x11::init() || x11::rr::init() || x11::fix::init()) {
|
||||
if (xcb::init_shm() || xcb::init() || x11::init() || x11::rr::init() || x11::fix::init()) {
|
||||
BOOST_LOG(error) << "Couldn't init x11 libraries"sv;
|
||||
|
||||
return nullptr;
|
||||
|
|
@ -702,26 +735,27 @@ std::shared_ptr<display_t> x11_display(platf::mem_type_e hwdevice_type, const st
|
|||
auto shm_disp = std::make_shared<shm_attr_t>(hwdevice_type);
|
||||
|
||||
auto status = shm_disp->init(display_name, config);
|
||||
if(status > 0) {
|
||||
if (status > 0) {
|
||||
// x11_attr_t::init() failed, don't bother trying again.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(status == 0) {
|
||||
if (status == 0) {
|
||||
return shm_disp;
|
||||
}
|
||||
|
||||
// Fallback
|
||||
auto x11_disp = std::make_shared<x11_attr_t>(hwdevice_type);
|
||||
if(x11_disp->init(display_name, config)) {
|
||||
if (x11_disp->init(display_name, config)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return x11_disp;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> x11_display_names() {
|
||||
if(load_x11() || load_xcb()) {
|
||||
std::vector<std::string>
|
||||
x11_display_names() {
|
||||
if (load_x11() || load_xcb()) {
|
||||
BOOST_LOG(error) << "Couldn't init x11 libraries"sv;
|
||||
|
||||
return {};
|
||||
|
|
@ -730,7 +764,7 @@ std::vector<std::string> x11_display_names() {
|
|||
BOOST_LOG(info) << "Detecting connected monitors"sv;
|
||||
|
||||
x11::xdisplay_t xdisplay { x11::OpenDisplay(nullptr) };
|
||||
if(!xdisplay) {
|
||||
if (!xdisplay) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -739,9 +773,9 @@ std::vector<std::string> x11_display_names() {
|
|||
int output = screenr->noutput;
|
||||
|
||||
int monitor = 0;
|
||||
for(int x = 0; x < output; ++x) {
|
||||
for (int x = 0; x < output; ++x) {
|
||||
output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) };
|
||||
if(out_info && out_info->connection == RR_Connected) {
|
||||
if (out_info && out_info->connection == RR_Connected) {
|
||||
++monitor;
|
||||
}
|
||||
}
|
||||
|
|
@ -749,62 +783,68 @@ std::vector<std::string> x11_display_names() {
|
|||
std::vector<std::string> names;
|
||||
names.reserve(monitor);
|
||||
|
||||
for(auto x = 0; x < monitor; ++x) {
|
||||
for (auto x = 0; x < monitor; ++x) {
|
||||
names.emplace_back(std::to_string(x));
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
void freeImage(XImage *p) {
|
||||
void
|
||||
freeImage(XImage *p) {
|
||||
XDestroyImage(p);
|
||||
}
|
||||
void freeX(XFixesCursorImage *p) {
|
||||
}
|
||||
void
|
||||
freeX(XFixesCursorImage *p) {
|
||||
x11::Free(p);
|
||||
}
|
||||
}
|
||||
|
||||
int load_xcb() {
|
||||
int
|
||||
load_xcb() {
|
||||
// This will be called once only
|
||||
static int xcb_status = xcb::init_shm() || xcb::init();
|
||||
|
||||
return xcb_status;
|
||||
}
|
||||
}
|
||||
|
||||
int load_x11() {
|
||||
int
|
||||
load_x11() {
|
||||
// This will be called once only
|
||||
static int x11_status =
|
||||
window_system == window_system_e::NONE ||
|
||||
x11::init() || x11::rr::init() || x11::fix::init();
|
||||
|
||||
return x11_status;
|
||||
}
|
||||
}
|
||||
|
||||
namespace x11 {
|
||||
std::optional<cursor_t> cursor_t::make() {
|
||||
if(load_x11()) {
|
||||
namespace x11 {
|
||||
std::optional<cursor_t>
|
||||
cursor_t::make() {
|
||||
if (load_x11()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
cursor_t cursor;
|
||||
|
||||
cursor.ctx.reset((cursor_ctx_t::pointer)x11::OpenDisplay(nullptr));
|
||||
cursor.ctx.reset((cursor_ctx_t::pointer) x11::OpenDisplay(nullptr));
|
||||
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
void cursor_t::capture(egl::cursor_t &img) {
|
||||
auto display = (xdisplay_t::pointer)ctx.get();
|
||||
void
|
||||
cursor_t::capture(egl::cursor_t &img) {
|
||||
auto display = (xdisplay_t::pointer) ctx.get();
|
||||
|
||||
xcursor_t xcursor = fix::GetCursorImage(display);
|
||||
|
||||
if(img.serial != xcursor->cursor_serial) {
|
||||
if (img.serial != xcursor->cursor_serial) {
|
||||
auto buf_size = xcursor->width * xcursor->height * sizeof(int);
|
||||
|
||||
if(img.buffer.size() < buf_size) {
|
||||
if (img.buffer.size() < buf_size) {
|
||||
img.buffer.resize(buf_size);
|
||||
}
|
||||
|
||||
std::transform(xcursor->pixels, xcursor->pixels + buf_size / 4, (int *)img.buffer.data(), [](long pixel) -> int {
|
||||
std::transform(xcursor->pixels, xcursor->pixels + buf_size / 4, (int *) img.buffer.data(), [](long pixel) -> int {
|
||||
return pixel;
|
||||
});
|
||||
}
|
||||
|
|
@ -817,22 +857,26 @@ void cursor_t::capture(egl::cursor_t &img) {
|
|||
img.pixel_pitch = 4;
|
||||
img.row_pitch = img.pixel_pitch * img.width;
|
||||
img.serial = xcursor->cursor_serial;
|
||||
}
|
||||
}
|
||||
|
||||
void cursor_t::blend(img_t &img, int offsetX, int offsetY) {
|
||||
blend_cursor((xdisplay_t::pointer)ctx.get(), img, offsetX, offsetY);
|
||||
}
|
||||
void
|
||||
cursor_t::blend(img_t &img, int offsetX, int offsetY) {
|
||||
blend_cursor((xdisplay_t::pointer) ctx.get(), img, offsetX, offsetY);
|
||||
}
|
||||
|
||||
xdisplay_t make_display() {
|
||||
xdisplay_t
|
||||
make_display() {
|
||||
return OpenDisplay(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void freeDisplay(_XDisplay *xdisplay) {
|
||||
void
|
||||
freeDisplay(_XDisplay *xdisplay) {
|
||||
CloseDisplay(xdisplay);
|
||||
}
|
||||
}
|
||||
|
||||
void freeCursorCtx(cursor_ctx_t::pointer ctx) {
|
||||
CloseDisplay((xdisplay_t::pointer)ctx);
|
||||
}
|
||||
} // namespace x11
|
||||
void
|
||||
freeCursorCtx(cursor_ctx_t::pointer ctx) {
|
||||
CloseDisplay((xdisplay_t::pointer) ctx);
|
||||
}
|
||||
} // namespace x11
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -10,24 +10,28 @@
|
|||
extern "C" struct _XDisplay;
|
||||
|
||||
namespace egl {
|
||||
class cursor_t;
|
||||
class cursor_t;
|
||||
}
|
||||
|
||||
namespace platf::x11 {
|
||||
|
||||
#ifdef SUNSHINE_BUILD_X11
|
||||
struct cursor_ctx_raw_t;
|
||||
void freeCursorCtx(cursor_ctx_raw_t *ctx);
|
||||
void freeDisplay(_XDisplay *xdisplay);
|
||||
struct cursor_ctx_raw_t;
|
||||
void
|
||||
freeCursorCtx(cursor_ctx_raw_t *ctx);
|
||||
void
|
||||
freeDisplay(_XDisplay *xdisplay);
|
||||
|
||||
using cursor_ctx_t = util::safe_ptr<cursor_ctx_raw_t, freeCursorCtx>;
|
||||
using xdisplay_t = util::safe_ptr<_XDisplay, freeDisplay>;
|
||||
using cursor_ctx_t = util::safe_ptr<cursor_ctx_raw_t, freeCursorCtx>;
|
||||
using xdisplay_t = util::safe_ptr<_XDisplay, freeDisplay>;
|
||||
|
||||
class cursor_t {
|
||||
public:
|
||||
static std::optional<cursor_t> make();
|
||||
class cursor_t {
|
||||
public:
|
||||
static std::optional<cursor_t>
|
||||
make();
|
||||
|
||||
void capture(egl::cursor_t &img);
|
||||
void
|
||||
capture(egl::cursor_t &img);
|
||||
|
||||
/**
|
||||
* Capture and blend the cursor into the image
|
||||
|
|
@ -35,25 +39,31 @@ public:
|
|||
* img <-- destination image
|
||||
* offsetX, offsetY <--- Top left corner of the virtual screen
|
||||
*/
|
||||
void blend(img_t &img, int offsetX, int offsetY);
|
||||
void
|
||||
blend(img_t &img, int offsetX, int offsetY);
|
||||
|
||||
cursor_ctx_t ctx;
|
||||
};
|
||||
};
|
||||
|
||||
xdisplay_t make_display();
|
||||
xdisplay_t
|
||||
make_display();
|
||||
#else
|
||||
// It's never something different from nullptr
|
||||
util::safe_ptr<_XDisplay, std::default_delete<_XDisplay>>;
|
||||
// It's never something different from nullptr
|
||||
util::safe_ptr<_XDisplay, std::default_delete<_XDisplay>>;
|
||||
|
||||
class cursor_t {
|
||||
public:
|
||||
static std::optional<cursor_t> make() { return std::nullopt; }
|
||||
class cursor_t {
|
||||
public:
|
||||
static std::optional<cursor_t>
|
||||
make() { return std::nullopt; }
|
||||
|
||||
void capture(egl::cursor_t &) {}
|
||||
void blend(img_t &, int, int) {}
|
||||
};
|
||||
void
|
||||
capture(egl::cursor_t &) {}
|
||||
void
|
||||
blend(img_t &, int, int) {}
|
||||
};
|
||||
|
||||
xdisplay_t make_display() { return nullptr; }
|
||||
xdisplay_t
|
||||
make_display() { return nullptr; }
|
||||
#endif
|
||||
} // namespace platf::x11
|
||||
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
#define kBufferLength 2048
|
||||
|
||||
@interface AVAudio : NSObject <AVCaptureAudioDataOutputSampleBufferDelegate> {
|
||||
@interface AVAudio: NSObject <AVCaptureAudioDataOutputSampleBufferDelegate> {
|
||||
@public
|
||||
TPCircularBuffer audioSampleBuffer;
|
||||
}
|
||||
|
||||
@property(nonatomic, assign) AVCaptureSession *audioCaptureSession;
|
||||
@property(nonatomic, assign) AVCaptureConnection *audioConnection;
|
||||
@property(nonatomic, assign) NSCondition *samplesArrivedSignal;
|
||||
@property (nonatomic, assign) AVCaptureSession *audioCaptureSession;
|
||||
@property (nonatomic, assign) AVCaptureConnection *audioConnection;
|
||||
@property (nonatomic, assign) NSCondition *samplesArrivedSignal;
|
||||
|
||||
+ (NSArray *)microphoneNames;
|
||||
+ (AVCaptureDevice *)findMicrophone:(NSString *)name;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
@implementation AVAudio
|
||||
|
||||
+ (NSArray<AVCaptureDevice *> *)microphones {
|
||||
|
||||
if([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) { 10, 15, 0 })]) {
|
||||
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) { 10, 15, 0 })]) {
|
||||
// This will generate a warning about AVCaptureDeviceDiscoverySession being
|
||||
// unavailable before macOS 10.15, but we have a guard to prevent it from
|
||||
// being called on those earlier systems.
|
||||
|
|
@ -34,7 +33,7 @@
|
|||
+ (NSArray<NSString *> *)microphoneNames {
|
||||
NSMutableArray *result = [[NSMutableArray alloc] init];
|
||||
|
||||
for(AVCaptureDevice *device in [AVAudio microphones]) {
|
||||
for (AVCaptureDevice *device in [AVAudio microphones]) {
|
||||
[result addObject:[device localizedName]];
|
||||
}
|
||||
|
||||
|
|
@ -42,8 +41,8 @@
|
|||
}
|
||||
|
||||
+ (AVCaptureDevice *)findMicrophone:(NSString *)name {
|
||||
for(AVCaptureDevice *device in [AVAudio microphones]) {
|
||||
if([[device localizedName] isEqualToString:name]) {
|
||||
for (AVCaptureDevice *device in [AVAudio microphones]) {
|
||||
if ([[device localizedName] isEqualToString:name]) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
|
@ -66,11 +65,11 @@
|
|||
|
||||
NSError *error;
|
||||
AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
|
||||
if(audioInput == nil) {
|
||||
if (audioInput == nil) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if([self.audioCaptureSession canAddInput:audioInput]) {
|
||||
if ([self.audioCaptureSession canAddInput:audioInput]) {
|
||||
[self.audioCaptureSession addInput:audioInput];
|
||||
}
|
||||
else {
|
||||
|
|
@ -81,12 +80,12 @@
|
|||
AVCaptureAudioDataOutput *audioOutput = [[AVCaptureAudioDataOutput alloc] init];
|
||||
|
||||
[audioOutput setAudioSettings:@{
|
||||
(NSString *)AVFormatIDKey: [NSNumber numberWithUnsignedInt:kAudioFormatLinearPCM],
|
||||
(NSString *)AVSampleRateKey: [NSNumber numberWithUnsignedInt:sampleRate],
|
||||
(NSString *)AVNumberOfChannelsKey: [NSNumber numberWithUnsignedInt:channels],
|
||||
(NSString *)AVLinearPCMBitDepthKey: [NSNumber numberWithUnsignedInt:16],
|
||||
(NSString *)AVLinearPCMIsFloatKey: @NO,
|
||||
(NSString *)AVLinearPCMIsNonInterleaved: @NO
|
||||
(NSString *) AVFormatIDKey: [NSNumber numberWithUnsignedInt:kAudioFormatLinearPCM],
|
||||
(NSString *) AVSampleRateKey: [NSNumber numberWithUnsignedInt:sampleRate],
|
||||
(NSString *) AVNumberOfChannelsKey: [NSNumber numberWithUnsignedInt:channels],
|
||||
(NSString *) AVLinearPCMBitDepthKey: [NSNumber numberWithUnsignedInt:16],
|
||||
(NSString *) AVLinearPCMIsFloatKey: @NO,
|
||||
(NSString *) AVLinearPCMIsNonInterleaved: @NO
|
||||
}];
|
||||
|
||||
dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT,
|
||||
|
|
@ -96,7 +95,7 @@
|
|||
|
||||
[audioOutput setSampleBufferDelegate:self queue:recordingQueue];
|
||||
|
||||
if([self.audioCaptureSession canAddOutput:audioOutput]) {
|
||||
if ([self.audioCaptureSession canAddOutput:audioOutput]) {
|
||||
[self.audioCaptureSession addOutput:audioOutput];
|
||||
}
|
||||
else {
|
||||
|
|
@ -121,7 +120,7 @@
|
|||
- (void)captureOutput:(AVCaptureOutput *)output
|
||||
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
||||
fromConnection:(AVCaptureConnection *)connection {
|
||||
if(connection == self.audioConnection) {
|
||||
if (connection == self.audioConnection) {
|
||||
AudioBufferList audioBufferList;
|
||||
CMBlockBufferRef blockBuffer;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@
|
|||
#include <CoreVideo/CoreVideo.h>
|
||||
|
||||
namespace platf {
|
||||
struct av_img_t : public img_t {
|
||||
struct av_img_t: public img_t {
|
||||
CVPixelBufferRef pixel_buffer = nullptr;
|
||||
CMSampleBufferRef sample_buffer = nullptr;
|
||||
|
||||
~av_img_t();
|
||||
};
|
||||
};
|
||||
} // namespace platf
|
||||
|
||||
#endif /* av_img_t_h */
|
||||
|
|
|
|||
|
|
@ -3,33 +3,32 @@
|
|||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
|
||||
struct CaptureSession {
|
||||
AVCaptureVideoDataOutput *output;
|
||||
NSCondition *captureStopped;
|
||||
};
|
||||
|
||||
@interface AVVideo : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
|
||||
@interface AVVideo: NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
|
||||
|
||||
#define kMaxDisplays 32
|
||||
|
||||
@property(nonatomic, assign) CGDirectDisplayID displayID;
|
||||
@property(nonatomic, assign) CMTime minFrameDuration;
|
||||
@property(nonatomic, assign) OSType pixelFormat;
|
||||
@property(nonatomic, assign) int frameWidth;
|
||||
@property(nonatomic, assign) int frameHeight;
|
||||
@property(nonatomic, assign) float scaling;
|
||||
@property(nonatomic, assign) int paddingLeft;
|
||||
@property(nonatomic, assign) int paddingRight;
|
||||
@property(nonatomic, assign) int paddingTop;
|
||||
@property(nonatomic, assign) int paddingBottom;
|
||||
@property (nonatomic, assign) CGDirectDisplayID displayID;
|
||||
@property (nonatomic, assign) CMTime minFrameDuration;
|
||||
@property (nonatomic, assign) OSType pixelFormat;
|
||||
@property (nonatomic, assign) int frameWidth;
|
||||
@property (nonatomic, assign) int frameHeight;
|
||||
@property (nonatomic, assign) float scaling;
|
||||
@property (nonatomic, assign) int paddingLeft;
|
||||
@property (nonatomic, assign) int paddingRight;
|
||||
@property (nonatomic, assign) int paddingTop;
|
||||
@property (nonatomic, assign) int paddingBottom;
|
||||
|
||||
typedef bool (^FrameCallbackBlock)(CMSampleBufferRef);
|
||||
|
||||
@property(nonatomic, assign) AVCaptureSession *session;
|
||||
@property(nonatomic, assign) NSMapTable<AVCaptureConnection *, AVCaptureVideoDataOutput *> *videoOutputs;
|
||||
@property(nonatomic, assign) NSMapTable<AVCaptureConnection *, FrameCallbackBlock> *captureCallbacks;
|
||||
@property(nonatomic, assign) NSMapTable<AVCaptureConnection *, dispatch_semaphore_t> *captureSignals;
|
||||
@property (nonatomic, assign) AVCaptureSession *session;
|
||||
@property (nonatomic, assign) NSMapTable<AVCaptureConnection *, AVCaptureVideoDataOutput *> *videoOutputs;
|
||||
@property (nonatomic, assign) NSMapTable<AVCaptureConnection *, FrameCallbackBlock> *captureCallbacks;
|
||||
@property (nonatomic, assign) NSMapTable<AVCaptureConnection *, dispatch_semaphore_t> *captureSignals;
|
||||
|
||||
+ (NSArray<NSDictionary *> *)displayNames;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
+ (NSArray<NSDictionary *> *)displayNames {
|
||||
CGDirectDisplayID displays[kMaxDisplays];
|
||||
uint32_t count;
|
||||
if(CGGetActiveDisplayList(kMaxDisplays, displays, &count) != kCGErrorSuccess) {
|
||||
if (CGGetActiveDisplayList(kMaxDisplays, displays, &count) != kCGErrorSuccess) {
|
||||
return [NSArray array];
|
||||
}
|
||||
|
||||
NSMutableArray *result = [NSMutableArray array];
|
||||
|
||||
for(uint32_t i = 0; i < count; i++) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
[result addObject:@{
|
||||
@"id": [NSNumber numberWithUnsignedInt:displays[i]],
|
||||
@"name": [NSString stringWithFormat:@"%d", displays[i]]
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
AVCaptureScreenInput *screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:self.displayID];
|
||||
[screenInput setMinFrameDuration:self.minFrameDuration];
|
||||
|
||||
if([self.session canAddInput:screenInput]) {
|
||||
if ([self.session canAddInput:screenInput]) {
|
||||
[self.session addInput:screenInput];
|
||||
}
|
||||
else {
|
||||
|
|
@ -78,10 +78,10 @@
|
|||
self.frameWidth = frameWidth;
|
||||
self.frameHeight = frameHeight;
|
||||
|
||||
double screenRatio = (double)CGImageGetWidth(screenshot) / (double)CGImageGetHeight(screenshot);
|
||||
double streamRatio = (double)frameWidth / (double)frameHeight;
|
||||
double screenRatio = (double) CGImageGetWidth(screenshot) / (double) CGImageGetHeight(screenshot);
|
||||
double streamRatio = (double) frameWidth / (double) frameHeight;
|
||||
|
||||
if(screenRatio < streamRatio) {
|
||||
if (screenRatio < streamRatio) {
|
||||
int padding = frameWidth - (frameHeight * screenRatio);
|
||||
self.paddingLeft = padding / 2;
|
||||
self.paddingRight = padding - self.paddingLeft;
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
// XXX: if the streamed image is larger than the native resolution, we add a black box around
|
||||
// the frame. Instead the frame should be resized entirely.
|
||||
int delta_width = frameWidth - (CGImageGetWidth(screenshot) + self.paddingLeft + self.paddingRight);
|
||||
if(delta_width > 0) {
|
||||
if (delta_width > 0) {
|
||||
int adjust_left = delta_width / 2;
|
||||
int adjust_right = delta_width - adjust_left;
|
||||
self.paddingLeft += adjust_left;
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
}
|
||||
|
||||
int delta_height = frameHeight - (CGImageGetHeight(screenshot) + self.paddingTop + self.paddingBottom);
|
||||
if(delta_height > 0) {
|
||||
if (delta_height > 0) {
|
||||
int adjust_top = delta_height / 2;
|
||||
int adjust_bottom = delta_height - adjust_top;
|
||||
self.paddingTop += adjust_top;
|
||||
|
|
@ -122,13 +122,13 @@
|
|||
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
|
||||
|
||||
[videoOutput setVideoSettings:@{
|
||||
(NSString *)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:self.pixelFormat],
|
||||
(NSString *)kCVPixelBufferWidthKey: [NSNumber numberWithInt:self.frameWidth],
|
||||
(NSString *)kCVPixelBufferExtendedPixelsRightKey: [NSNumber numberWithInt:self.paddingRight],
|
||||
(NSString *)kCVPixelBufferExtendedPixelsLeftKey: [NSNumber numberWithInt:self.paddingLeft],
|
||||
(NSString *)kCVPixelBufferExtendedPixelsTopKey: [NSNumber numberWithInt:self.paddingTop],
|
||||
(NSString *)kCVPixelBufferExtendedPixelsBottomKey: [NSNumber numberWithInt:self.paddingBottom],
|
||||
(NSString *)kCVPixelBufferHeightKey: [NSNumber numberWithInt:self.frameHeight]
|
||||
(NSString *) kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:self.pixelFormat],
|
||||
(NSString *) kCVPixelBufferWidthKey: [NSNumber numberWithInt:self.frameWidth],
|
||||
(NSString *) kCVPixelBufferExtendedPixelsRightKey: [NSNumber numberWithInt:self.paddingRight],
|
||||
(NSString *) kCVPixelBufferExtendedPixelsLeftKey: [NSNumber numberWithInt:self.paddingLeft],
|
||||
(NSString *) kCVPixelBufferExtendedPixelsTopKey: [NSNumber numberWithInt:self.paddingTop],
|
||||
(NSString *) kCVPixelBufferExtendedPixelsBottomKey: [NSNumber numberWithInt:self.paddingBottom],
|
||||
(NSString *) kCVPixelBufferHeightKey: [NSNumber numberWithInt:self.frameHeight]
|
||||
}];
|
||||
|
||||
dispatch_queue_attr_t qos = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
|
||||
|
|
@ -139,7 +139,7 @@
|
|||
|
||||
[self.session stopRunning];
|
||||
|
||||
if([self.session canAddOutput:videoOutput]) {
|
||||
if ([self.session canAddOutput:videoOutput]) {
|
||||
[self.session addOutput:videoOutput];
|
||||
}
|
||||
else {
|
||||
|
|
@ -163,11 +163,10 @@
|
|||
- (void)captureOutput:(AVCaptureOutput *)captureOutput
|
||||
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
||||
fromConnection:(AVCaptureConnection *)connection {
|
||||
|
||||
FrameCallbackBlock callback = [self.captureCallbacks objectForKey:connection];
|
||||
|
||||
if(callback != nil) {
|
||||
if(!callback(sampleBuffer)) {
|
||||
if (callback != nil) {
|
||||
if (!callback(sampleBuffer)) {
|
||||
@synchronized(self) {
|
||||
[self.session stopRunning];
|
||||
[self.captureCallbacks removeObjectForKey:connection];
|
||||
|
|
|
|||
|
|
@ -14,21 +14,21 @@
|
|||
namespace fs = std::filesystem;
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
using namespace std::literals;
|
||||
|
||||
av_img_t::~av_img_t() {
|
||||
if(pixel_buffer != NULL) {
|
||||
av_img_t::~av_img_t() {
|
||||
if (pixel_buffer != NULL) {
|
||||
CVPixelBufferUnlockBaseAddress(pixel_buffer, 0);
|
||||
}
|
||||
|
||||
if(sample_buffer != nullptr) {
|
||||
if (sample_buffer != nullptr) {
|
||||
CFRelease(sample_buffer);
|
||||
}
|
||||
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
struct av_display_t : public display_t {
|
||||
struct av_display_t: public display_t {
|
||||
AVVideo *av_capture;
|
||||
CGDirectDisplayID display_id;
|
||||
|
||||
|
|
@ -36,7 +36,8 @@ struct av_display_t : public display_t {
|
|||
[av_capture release];
|
||||
}
|
||||
|
||||
capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override {
|
||||
capture_e
|
||||
capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override {
|
||||
__block auto img_next = std::move(img);
|
||||
|
||||
auto signal = [av_capture capture:^(CMSampleBufferRef sampleBuffer) {
|
||||
|
|
@ -47,15 +48,15 @@ struct av_display_t : public display_t {
|
|||
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
||||
CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
|
||||
|
||||
if(av_img_next->pixel_buffer != nullptr)
|
||||
if (av_img_next->pixel_buffer != nullptr)
|
||||
CVPixelBufferUnlockBaseAddress(av_img_next->pixel_buffer, 0);
|
||||
|
||||
if(av_img_next->sample_buffer != nullptr)
|
||||
if (av_img_next->sample_buffer != nullptr)
|
||||
CFRelease(av_img_next->sample_buffer);
|
||||
|
||||
av_img_next->sample_buffer = sampleBuffer;
|
||||
av_img_next->pixel_buffer = pixelBuffer;
|
||||
img_next->data = (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer);
|
||||
img_next->data = (uint8_t *) CVPixelBufferGetBaseAddress(pixelBuffer);
|
||||
|
||||
size_t extraPixels[4];
|
||||
CVPixelBufferGetExtendedPixels(pixelBuffer, &extraPixels[0], &extraPixels[1], &extraPixels[2], &extraPixels[3]);
|
||||
|
|
@ -76,17 +77,19 @@ struct av_display_t : public display_t {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
std::shared_ptr<img_t> alloc_img() override {
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override {
|
||||
return std::make_shared<av_img_t>();
|
||||
}
|
||||
|
||||
std::shared_ptr<hwdevice_t> make_hwdevice(pix_fmt_e pix_fmt) override {
|
||||
if(pix_fmt == pix_fmt_e::yuv420p) {
|
||||
std::shared_ptr<hwdevice_t>
|
||||
make_hwdevice(pix_fmt_e pix_fmt) override {
|
||||
if (pix_fmt == pix_fmt_e::yuv420p) {
|
||||
av_capture.pixelFormat = kCVPixelFormatType_32BGRA;
|
||||
|
||||
return std::make_shared<hwdevice_t>();
|
||||
}
|
||||
else if(pix_fmt == pix_fmt_e::nv12) {
|
||||
else if (pix_fmt == pix_fmt_e::nv12) {
|
||||
auto device = std::make_shared<nv12_zero_device>();
|
||||
|
||||
device->init(static_cast<void *>(av_capture), setResolution, setPixelFormat);
|
||||
|
|
@ -99,9 +102,10 @@ struct av_display_t : public display_t {
|
|||
}
|
||||
}
|
||||
|
||||
int dummy_img(img_t *img) override {
|
||||
int
|
||||
dummy_img(img_t *img) override {
|
||||
auto signal = [av_capture capture:^(CMSampleBufferRef sampleBuffer) {
|
||||
auto av_img = (av_img_t *)img;
|
||||
auto av_img = (av_img_t *) img;
|
||||
|
||||
CFRetain(sampleBuffer);
|
||||
|
||||
|
|
@ -110,15 +114,15 @@ struct av_display_t : public display_t {
|
|||
|
||||
// XXX: next_img->img should be moved to a smart pointer with
|
||||
// the CFRelease as custom deallocator
|
||||
if(av_img->pixel_buffer != nullptr)
|
||||
CVPixelBufferUnlockBaseAddress(((av_img_t *)img)->pixel_buffer, 0);
|
||||
if (av_img->pixel_buffer != nullptr)
|
||||
CVPixelBufferUnlockBaseAddress(((av_img_t *) img)->pixel_buffer, 0);
|
||||
|
||||
if(av_img->sample_buffer != nullptr)
|
||||
if (av_img->sample_buffer != nullptr)
|
||||
CFRelease(av_img->sample_buffer);
|
||||
|
||||
av_img->sample_buffer = sampleBuffer;
|
||||
av_img->pixel_buffer = pixelBuffer;
|
||||
img->data = (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer);
|
||||
img->data = (uint8_t *) CVPixelBufferGetBaseAddress(pixelBuffer);
|
||||
|
||||
size_t extraPixels[4];
|
||||
CVPixelBufferGetExtendedPixels(pixelBuffer, &extraPixels[0], &extraPixels[1], &extraPixels[2], &extraPixels[3]);
|
||||
|
|
@ -143,17 +147,20 @@ struct av_display_t : public display_t {
|
|||
* width --> the intended capture width
|
||||
* height --> the intended capture height
|
||||
*/
|
||||
static void setResolution(void *display, int width, int height) {
|
||||
static void
|
||||
setResolution(void *display, int width, int height) {
|
||||
[static_cast<AVVideo *>(display) setFrameWidth:width frameHeight:height];
|
||||
}
|
||||
|
||||
static void setPixelFormat(void *display, OSType pixelFormat) {
|
||||
static void
|
||||
setPixelFormat(void *display, OSType pixelFormat) {
|
||||
static_cast<AVVideo *>(display).pixelFormat = pixelFormat;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if(hwdevice_type != platf::mem_type_e::system) {
|
||||
std::shared_ptr<display_t>
|
||||
display(platf::mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if (hwdevice_type != platf::mem_type_e::system) {
|
||||
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -161,12 +168,12 @@ std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, const std::s
|
|||
auto display = std::make_shared<av_display_t>();
|
||||
|
||||
display->display_id = CGMainDisplayID();
|
||||
if(!display_name.empty()) {
|
||||
if (!display_name.empty()) {
|
||||
auto display_array = [AVVideo displayNames];
|
||||
|
||||
for(NSDictionary *item in display_array) {
|
||||
for (NSDictionary *item in display_array) {
|
||||
NSString *name = item[@"name"];
|
||||
if(name.UTF8String == display_name) {
|
||||
if (name.UTF8String == display_name) {
|
||||
NSNumber *display_id = item[@"id"];
|
||||
display->display_id = [display_id unsignedIntValue];
|
||||
}
|
||||
|
|
@ -175,7 +182,7 @@ std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, const std::s
|
|||
|
||||
display->av_capture = [[AVVideo alloc] initWithDisplay:display->display_id frameRate:config.framerate];
|
||||
|
||||
if(!display->av_capture) {
|
||||
if (!display->av_capture) {
|
||||
BOOST_LOG(error) << "Video setup failed."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -184,9 +191,10 @@ std::shared_ptr<display_t> display(platf::mem_type_e hwdevice_type, const std::s
|
|||
display->height = display->av_capture.frameHeight;
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> display_names(mem_type_e hwdevice_type) {
|
||||
std::vector<std::string>
|
||||
display_names(mem_type_e hwdevice_type) {
|
||||
__block std::vector<std::string> display_names;
|
||||
|
||||
auto display_array = [AVVideo displayNames];
|
||||
|
|
@ -198,5 +206,5 @@ std::vector<std::string> display_names(mem_type_e hwdevice_type) {
|
|||
}];
|
||||
|
||||
return display_names;
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
#define MULTICLICK_DELAY_NS 500000000
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
using namespace std::literals;
|
||||
|
||||
struct macos_input_t {
|
||||
public:
|
||||
struct macos_input_t {
|
||||
public:
|
||||
CGDirectDisplayID display;
|
||||
CGFloat displayScaling;
|
||||
CGEventSourceRef source;
|
||||
|
|
@ -27,20 +27,21 @@ public:
|
|||
CGEventRef mouse_event; // mouse event source
|
||||
bool mouse_down[3]; // mouse button status
|
||||
uint64_t last_mouse_event[3][2]; // timestamp of last mouse events
|
||||
};
|
||||
};
|
||||
|
||||
// A struct to hold a Windows keycode to Mac virtual keycode mapping.
|
||||
struct KeyCodeMap {
|
||||
// A struct to hold a Windows keycode to Mac virtual keycode mapping.
|
||||
struct KeyCodeMap {
|
||||
int win_keycode;
|
||||
int mac_keycode;
|
||||
};
|
||||
};
|
||||
|
||||
// Customized less operator for using std::lower_bound() on a KeyCodeMap array.
|
||||
bool operator<(const KeyCodeMap &a, const KeyCodeMap &b) {
|
||||
// Customized less operator for using std::lower_bound() on a KeyCodeMap array.
|
||||
bool
|
||||
operator<(const KeyCodeMap &a, const KeyCodeMap &b) {
|
||||
return a.win_keycode < b.win_keycode;
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
const KeyCodeMap kKeyCodesMap[] = {
|
||||
{ 0x08 /* VKEY_BACK */, kVK_Delete },
|
||||
{ 0x09 /* VKEY_TAB */, kVK_Tab },
|
||||
|
|
@ -210,43 +211,44 @@ const KeyCodeMap kKeyCodesMap[] = {
|
|||
{ 0xFD /* VKEY_PA1 */, -1 },
|
||||
{ 0xFE /* VKEY_OEM_CLEAR */, kVK_ANSI_KeypadClear }
|
||||
};
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
int keysym(int keycode) {
|
||||
int
|
||||
keysym(int keycode) {
|
||||
KeyCodeMap key_map;
|
||||
|
||||
key_map.win_keycode = keycode;
|
||||
const KeyCodeMap *temp_map = std::lower_bound(
|
||||
kKeyCodesMap, kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]), key_map);
|
||||
|
||||
if(temp_map >= kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]) ||
|
||||
if (temp_map >= kKeyCodesMap + sizeof(kKeyCodesMap) / sizeof(kKeyCodesMap[0]) ||
|
||||
temp_map->win_keycode != keycode || temp_map->mac_keycode == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return temp_map->mac_keycode;
|
||||
}
|
||||
}
|
||||
|
||||
void keyboard(input_t &input, uint16_t modcode, bool release) {
|
||||
void
|
||||
keyboard(input_t &input, uint16_t modcode, bool release) {
|
||||
auto key = keysym(modcode);
|
||||
|
||||
BOOST_LOG(debug) << "got keycode: 0x"sv << std::hex << modcode << ", translated to: 0x" << std::hex << key << ", release:" << release;
|
||||
|
||||
if(key < 0) {
|
||||
if (key < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto macos_input = ((macos_input_t *)input.get());
|
||||
auto macos_input = ((macos_input_t *) input.get());
|
||||
auto event = macos_input->kb_event;
|
||||
|
||||
if(key == kVK_Shift || key == kVK_RightShift ||
|
||||
if (key == kVK_Shift || key == kVK_RightShift ||
|
||||
key == kVK_Command || key == kVK_RightCommand ||
|
||||
key == kVK_Option || key == kVK_RightOption ||
|
||||
key == kVK_Control || key == kVK_RightControl) {
|
||||
|
||||
CGEventFlags mask;
|
||||
|
||||
switch(key) {
|
||||
switch (key) {
|
||||
case kVK_Shift:
|
||||
case kVK_RightShift:
|
||||
mask = kCGEventFlagMaskShift;
|
||||
|
|
@ -275,45 +277,51 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
|
|||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
}
|
||||
}
|
||||
|
||||
void unicode(input_t &input, char *utf8, int size) {
|
||||
void
|
||||
unicode(input_t &input, char *utf8, int size) {
|
||||
BOOST_LOG(info) << "unicode: Unicode input not yet implemented for MacOS."sv;
|
||||
}
|
||||
}
|
||||
|
||||
int alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) {
|
||||
int
|
||||
alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) {
|
||||
BOOST_LOG(info) << "alloc_gamepad: Gamepad not yet implemented for MacOS."sv;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void free_gamepad(input_t &input, int nr) {
|
||||
void
|
||||
free_gamepad(input_t &input, int nr) {
|
||||
BOOST_LOG(info) << "free_gamepad: Gamepad not yet implemented for MacOS."sv;
|
||||
}
|
||||
}
|
||||
|
||||
void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
void
|
||||
gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
BOOST_LOG(info) << "gamepad: Gamepad not yet implemented for MacOS."sv;
|
||||
}
|
||||
}
|
||||
|
||||
// returns current mouse location:
|
||||
inline CGPoint get_mouse_loc(input_t &input) {
|
||||
return CGEventGetLocation(((macos_input_t *)input.get())->mouse_event);
|
||||
}
|
||||
// returns current mouse location:
|
||||
inline CGPoint
|
||||
get_mouse_loc(input_t &input) {
|
||||
return CGEventGetLocation(((macos_input_t *) input.get())->mouse_event);
|
||||
}
|
||||
|
||||
void post_mouse(input_t &input, CGMouseButton button, CGEventType type, CGPoint location, int click_count) {
|
||||
void
|
||||
post_mouse(input_t &input, CGMouseButton button, CGEventType type, CGPoint location, int click_count) {
|
||||
BOOST_LOG(debug) << "mouse_event: "sv << button << ", type: "sv << type << ", location:"sv << location.x << ":"sv << location.y << " click_count: "sv << click_count;
|
||||
|
||||
auto macos_input = (macos_input_t *)input.get();
|
||||
auto macos_input = (macos_input_t *) input.get();
|
||||
auto display = macos_input->display;
|
||||
auto event = macos_input->mouse_event;
|
||||
|
||||
if(location.x < 0)
|
||||
if (location.x < 0)
|
||||
location.x = 0;
|
||||
if(location.x >= CGDisplayPixelsWide(display))
|
||||
if (location.x >= CGDisplayPixelsWide(display))
|
||||
location.x = CGDisplayPixelsWide(display) - 1;
|
||||
|
||||
if(location.y < 0)
|
||||
if (location.y < 0)
|
||||
location.y = 0;
|
||||
if(location.y >= CGDisplayPixelsHigh(display))
|
||||
if (location.y >= CGDisplayPixelsHigh(display))
|
||||
location.y = CGDisplayPixelsHigh(display) - 1;
|
||||
|
||||
CGEventSetType(event, type);
|
||||
|
|
@ -326,58 +334,63 @@ void post_mouse(input_t &input, CGMouseButton button, CGEventType type, CGPoint
|
|||
// For why this is here, see:
|
||||
// https://stackoverflow.com/questions/15194409/simulated-mouseevent-not-working-properly-osx
|
||||
CGWarpMouseCursorPosition(location);
|
||||
}
|
||||
}
|
||||
|
||||
inline CGEventType event_type_mouse(input_t &input) {
|
||||
auto macos_input = ((macos_input_t *)input.get());
|
||||
inline CGEventType
|
||||
event_type_mouse(input_t &input) {
|
||||
auto macos_input = ((macos_input_t *) input.get());
|
||||
|
||||
if(macos_input->mouse_down[0]) {
|
||||
if (macos_input->mouse_down[0]) {
|
||||
return kCGEventLeftMouseDragged;
|
||||
}
|
||||
else if(macos_input->mouse_down[1]) {
|
||||
else if (macos_input->mouse_down[1]) {
|
||||
return kCGEventOtherMouseDragged;
|
||||
}
|
||||
else if(macos_input->mouse_down[2]) {
|
||||
else if (macos_input->mouse_down[2]) {
|
||||
return kCGEventRightMouseDragged;
|
||||
}
|
||||
else {
|
||||
return kCGEventMouseMoved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
void
|
||||
move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
auto current = get_mouse_loc(input);
|
||||
|
||||
CGPoint location = CGPointMake(current.x + deltaX, current.y + deltaY);
|
||||
|
||||
post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {
|
||||
auto scaling = ((macos_input_t *)input.get())->displayScaling;
|
||||
void
|
||||
abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {
|
||||
auto scaling = ((macos_input_t *) input.get())->displayScaling;
|
||||
|
||||
CGPoint location = CGPointMake(x * scaling, y * scaling);
|
||||
|
||||
post_mouse(input, kCGMouseButtonLeft, event_type_mouse(input), location, 0);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t time_diff(uint64_t start) {
|
||||
uint64_t
|
||||
time_diff(uint64_t start) {
|
||||
uint64_t elapsed;
|
||||
Nanoseconds elapsedNano;
|
||||
|
||||
elapsed = mach_absolute_time() - start;
|
||||
elapsedNano = AbsoluteToNanoseconds(*(AbsoluteTime *)&elapsed);
|
||||
elapsedNano = AbsoluteToNanoseconds(*(AbsoluteTime *) &elapsed);
|
||||
|
||||
return *(uint64_t *)&elapsedNano;
|
||||
}
|
||||
return *(uint64_t *) &elapsedNano;
|
||||
}
|
||||
|
||||
void button_mouse(input_t &input, int button, bool release) {
|
||||
void
|
||||
button_mouse(input_t &input, int button, bool release) {
|
||||
CGMouseButton mac_button;
|
||||
CGEventType event;
|
||||
|
||||
auto mouse = ((macos_input_t *)input.get());
|
||||
auto mouse = ((macos_input_t *) input.get());
|
||||
|
||||
switch(button) {
|
||||
switch (button) {
|
||||
case 1:
|
||||
mac_button = kCGMouseButtonLeft;
|
||||
event = release ? kCGEventLeftMouseUp : kCGEventLeftMouseDown;
|
||||
|
|
@ -398,7 +411,7 @@ void button_mouse(input_t &input, int button, bool release) {
|
|||
mouse->mouse_down[mac_button] = !release;
|
||||
|
||||
// if the last mouse down was less than MULTICLICK_DELAY_NS, we send a double click event
|
||||
if(time_diff(mouse->last_mouse_event[mac_button][release]) < MULTICLICK_DELAY_NS) {
|
||||
if (time_diff(mouse->last_mouse_event[mac_button][release]) < MULTICLICK_DELAY_NS) {
|
||||
post_mouse(input, mac_button, event, get_mouse_loc(input), 2);
|
||||
}
|
||||
else {
|
||||
|
|
@ -406,32 +419,35 @@ void button_mouse(input_t &input, int button, bool release) {
|
|||
}
|
||||
|
||||
mouse->last_mouse_event[mac_button][release] = mach_absolute_time();
|
||||
}
|
||||
}
|
||||
|
||||
void scroll(input_t &input, int high_res_distance) {
|
||||
void
|
||||
scroll(input_t &input, int high_res_distance) {
|
||||
CGEventRef upEvent = CGEventCreateScrollWheelEvent(
|
||||
NULL,
|
||||
kCGScrollEventUnitLine,
|
||||
2, high_res_distance > 0 ? 1 : -1, high_res_distance);
|
||||
CGEventPost(kCGHIDEventTap, upEvent);
|
||||
CFRelease(upEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void hscroll(input_t &input, int high_res_distance) {
|
||||
void
|
||||
hscroll(input_t &input, int high_res_distance) {
|
||||
// Unimplemented
|
||||
}
|
||||
}
|
||||
|
||||
input_t input() {
|
||||
input_t
|
||||
input() {
|
||||
input_t result { new macos_input_t() };
|
||||
|
||||
auto macos_input = (macos_input_t *)result.get();
|
||||
auto macos_input = (macos_input_t *) result.get();
|
||||
|
||||
// If we don't use the main display in the future, this has to be adapted
|
||||
macos_input->display = CGMainDisplayID();
|
||||
|
||||
// Input coordinates are based on the virtual resolution not the physical, so we need the scaling factor
|
||||
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(macos_input->display);
|
||||
macos_input->displayScaling = ((CGFloat)CGDisplayPixelsWide(macos_input->display)) / ((CGFloat)CGDisplayModeGetPixelWidth(mode));
|
||||
macos_input->displayScaling = ((CGFloat) CGDisplayPixelsWide(macos_input->display)) / ((CGFloat) CGDisplayModeGetPixelWidth(mode));
|
||||
CFRelease(mode);
|
||||
|
||||
macos_input->source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
|
|
@ -453,21 +469,23 @@ input_t input() {
|
|||
BOOST_LOG(debug) << "Display "sv << macos_input->display << ", pixel dimention: " << CGDisplayPixelsWide(macos_input->display) << "x"sv << CGDisplayPixelsHigh(macos_input->display);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void freeInput(void *p) {
|
||||
auto *input = (macos_input_t *)p;
|
||||
void
|
||||
freeInput(void *p) {
|
||||
auto *input = (macos_input_t *) p;
|
||||
|
||||
CFRelease(input->source);
|
||||
CFRelease(input->kb_event);
|
||||
CFRelease(input->mouse_event);
|
||||
|
||||
delete input;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string_view> &supported_gamepads() {
|
||||
std::vector<std::string_view> &
|
||||
supported_gamepads() {
|
||||
static std::vector<std::string_view> gamepads { ""sv };
|
||||
|
||||
return gamepads;
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -5,27 +5,28 @@
|
|||
#include "src/main.h"
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
using namespace std::literals;
|
||||
|
||||
struct av_mic_t : public mic_t {
|
||||
struct av_mic_t: public mic_t {
|
||||
AVAudio *av_audio_capture;
|
||||
|
||||
~av_mic_t() {
|
||||
[av_audio_capture release];
|
||||
}
|
||||
|
||||
capture_e sample(std::vector<std::int16_t> &sample_in) override {
|
||||
capture_e
|
||||
sample(std::vector<std::int16_t> &sample_in) override {
|
||||
auto sample_size = sample_in.size();
|
||||
|
||||
uint32_t length = 0;
|
||||
void *byteSampleBuffer = TPCircularBufferTail(&av_audio_capture->audioSampleBuffer, &length);
|
||||
|
||||
while(length < sample_size * sizeof(std::int16_t)) {
|
||||
while (length < sample_size * sizeof(std::int16_t)) {
|
||||
[av_audio_capture.samplesArrivedSignal wait];
|
||||
byteSampleBuffer = TPCircularBufferTail(&av_audio_capture->audioSampleBuffer, &length);
|
||||
}
|
||||
|
||||
const int16_t *sampleBuffer = (int16_t *)byteSampleBuffer;
|
||||
const int16_t *sampleBuffer = (int16_t *) byteSampleBuffer;
|
||||
std::vector<int16_t> vectorBuffer(sampleBuffer, sampleBuffer + sample_size);
|
||||
|
||||
std::copy_n(std::begin(vectorBuffer), sample_size, std::begin(sample_in));
|
||||
|
|
@ -34,30 +35,32 @@ struct av_mic_t : public mic_t {
|
|||
|
||||
return capture_e::ok;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct macos_audio_control_t : public audio_control_t {
|
||||
struct macos_audio_control_t: public audio_control_t {
|
||||
AVCaptureDevice *audio_capture_device;
|
||||
|
||||
public:
|
||||
int set_sink(const std::string &sink) override {
|
||||
public:
|
||||
int
|
||||
set_sink(const std::string &sink) override {
|
||||
BOOST_LOG(warning) << "audio_control_t::set_sink() unimplemented: "sv << sink;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<mic_t> microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
|
||||
std::unique_ptr<mic_t>
|
||||
microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
|
||||
auto mic = std::make_unique<av_mic_t>();
|
||||
const char *audio_sink = "";
|
||||
|
||||
if(!config::audio.sink.empty()) {
|
||||
if (!config::audio.sink.empty()) {
|
||||
audio_sink = config::audio.sink.c_str();
|
||||
}
|
||||
|
||||
if((audio_capture_device = [AVAudio findMicrophone:[NSString stringWithUTF8String:audio_sink]]) == nullptr) {
|
||||
if ((audio_capture_device = [AVAudio findMicrophone:[NSString stringWithUTF8String:audio_sink]]) == nullptr) {
|
||||
BOOST_LOG(error) << "opening microphone '"sv << audio_sink << "' failed. Please set a valid input source in the Sunshine config."sv;
|
||||
BOOST_LOG(error) << "Available inputs:"sv;
|
||||
|
||||
for(NSString *name in [AVAudio microphoneNames]) {
|
||||
for (NSString *name in [AVAudio microphoneNames]) {
|
||||
BOOST_LOG(error) << "\t"sv << [name UTF8String];
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +69,7 @@ public:
|
|||
|
||||
mic->av_audio_capture = [[AVAudio alloc] init];
|
||||
|
||||
if([mic->av_audio_capture setupMicrophone:audio_capture_device sampleRate:sample_rate frameSize:frame_size channels:channels]) {
|
||||
if ([mic->av_audio_capture setupMicrophone:audio_capture_device sampleRate:sample_rate frameSize:frame_size channels:channels]) {
|
||||
BOOST_LOG(error) << "Failed to setup microphone."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -74,14 +77,16 @@ public:
|
|||
return mic;
|
||||
}
|
||||
|
||||
std::optional<sink_t> sink_info() override {
|
||||
std::optional<sink_t>
|
||||
sink_info() override {
|
||||
sink_t sink;
|
||||
|
||||
return sink;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<audio_control_t> audio_control() {
|
||||
std::unique_ptr<audio_control_t>
|
||||
audio_control() {
|
||||
return std::make_unique<macos_audio_control_t>();
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@
|
|||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
namespace dyn {
|
||||
typedef void (*apiproc)(void);
|
||||
typedef void (*apiproc)(void);
|
||||
|
||||
int load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict = true);
|
||||
void *handle(const std::vector<const char *> &libs);
|
||||
int
|
||||
load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict = true);
|
||||
void *
|
||||
handle(const std::vector<const char *> &libs);
|
||||
|
||||
} // namespace dyn
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,16 @@ namespace platf {
|
|||
// Even though the following two functions are available starting in macOS 10.15, they weren't
|
||||
// actually in the Mac SDK until Xcode 12.2, the first to include the SDK for macOS 11
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 110000 // __MAC_11_0
|
||||
// If they're not in the SDK then we can use our own function definitions.
|
||||
// Need to use weak import so that this will link in macOS 10.14 and earlier
|
||||
extern "C" bool CGPreflightScreenCaptureAccess(void) __attribute__((weak_import));
|
||||
extern "C" bool CGRequestScreenCaptureAccess(void) __attribute__((weak_import));
|
||||
// If they're not in the SDK then we can use our own function definitions.
|
||||
// Need to use weak import so that this will link in macOS 10.14 and earlier
|
||||
extern "C" bool
|
||||
CGPreflightScreenCaptureAccess(void) __attribute__((weak_import));
|
||||
extern "C" bool
|
||||
CGRequestScreenCaptureAccess(void) __attribute__((weak_import));
|
||||
#endif
|
||||
|
||||
std::unique_ptr<deinit_t> init() {
|
||||
std::unique_ptr<deinit_t>
|
||||
init() {
|
||||
// This will generate a warning about CGPreflightScreenCaptureAccess and
|
||||
// CGRequestScreenCaptureAccess being unavailable before macOS 10.15, but
|
||||
// we have a guard to prevent it from being called on those earlier systems.
|
||||
|
|
@ -42,7 +45,7 @@ std::unique_ptr<deinit_t> init() {
|
|||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
||||
#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
|
||||
if([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) { 10, 15, 0 })] &&
|
||||
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:((NSOperatingSystemVersion) { 10, 15, 0 })] &&
|
||||
// Double check that these weakly-linked symbols have been loaded:
|
||||
CGPreflightScreenCaptureAccess != nullptr && CGRequestScreenCaptureAccess != nullptr &&
|
||||
!CGPreflightScreenCaptureAccess()) {
|
||||
|
|
@ -53,79 +56,84 @@ std::unique_ptr<deinit_t> init() {
|
|||
}
|
||||
#pragma clang diagnostic pop
|
||||
return std::make_unique<deinit_t>();
|
||||
}
|
||||
}
|
||||
|
||||
fs::path appdata() {
|
||||
fs::path
|
||||
appdata() {
|
||||
const char *homedir;
|
||||
if((homedir = getenv("HOME")) == nullptr) {
|
||||
if ((homedir = getenv("HOME")) == nullptr) {
|
||||
homedir = getpwuid(geteuid())->pw_dir;
|
||||
}
|
||||
|
||||
return fs::path { homedir } / ".config/sunshine"sv;
|
||||
}
|
||||
}
|
||||
|
||||
using ifaddr_t = util::safe_ptr<ifaddrs, freeifaddrs>;
|
||||
using ifaddr_t = util::safe_ptr<ifaddrs, freeifaddrs>;
|
||||
|
||||
ifaddr_t get_ifaddrs() {
|
||||
ifaddr_t
|
||||
get_ifaddrs() {
|
||||
ifaddrs *p { nullptr };
|
||||
|
||||
getifaddrs(&p);
|
||||
|
||||
return ifaddr_t { p };
|
||||
}
|
||||
}
|
||||
|
||||
std::string from_sockaddr(const sockaddr *const ip_addr) {
|
||||
std::string
|
||||
from_sockaddr(const sockaddr *const ip_addr) {
|
||||
char data[INET6_ADDRSTRLEN];
|
||||
|
||||
auto family = ip_addr->sa_family;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data,
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data,
|
||||
if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
return std::string { data };
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
std::pair<std::uint16_t, std::string>
|
||||
from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
char data[INET6_ADDRSTRLEN];
|
||||
|
||||
auto family = ip_addr->sa_family;
|
||||
std::uint16_t port;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data,
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6 *)ip_addr)->sin6_port;
|
||||
port = ((sockaddr_in6 *) ip_addr)->sin6_port;
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data,
|
||||
if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in *)ip_addr)->sin_port;
|
||||
port = ((sockaddr_in *) ip_addr)->sin_port;
|
||||
}
|
||||
|
||||
return { port, std::string { data } };
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_mac_address(const std::string_view &address) {
|
||||
std::string
|
||||
get_mac_address(const std::string_view &address) {
|
||||
auto ifaddrs = get_ifaddrs();
|
||||
|
||||
for(auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
|
||||
if(pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
|
||||
for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) {
|
||||
if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) {
|
||||
BOOST_LOG(verbose) << "Looking for MAC of "sv << pos->ifa_name;
|
||||
|
||||
struct ifaddrs *ifap, *ifaptr;
|
||||
unsigned char *ptr;
|
||||
std::string mac_address;
|
||||
|
||||
if(getifaddrs(&ifap) == 0) {
|
||||
for(ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
|
||||
if(!strcmp((ifaptr)->ifa_name, pos->ifa_name) && (((ifaptr)->ifa_addr)->sa_family == AF_LINK)) {
|
||||
ptr = (unsigned char *)LLADDR((struct sockaddr_dl *)(ifaptr)->ifa_addr);
|
||||
if (getifaddrs(&ifap) == 0) {
|
||||
for (ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
|
||||
if (!strcmp((ifaptr)->ifa_name, pos->ifa_name) && (((ifaptr)->ifa_addr)->sa_family == AF_LINK)) {
|
||||
ptr = (unsigned char *) LLADDR((struct sockaddr_dl *) (ifaptr)->ifa_addr);
|
||||
char buff[100];
|
||||
|
||||
snprintf(buff, sizeof(buff), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
|
|
@ -137,7 +145,7 @@ std::string get_mac_address(const std::string_view &address) {
|
|||
|
||||
freeifaddrs(ifap);
|
||||
|
||||
if(ifaptr != NULL) {
|
||||
if (ifaptr != NULL) {
|
||||
BOOST_LOG(verbose) << "Found MAC of "sv << pos->ifa_name << ": "sv << mac_address;
|
||||
return mac_address;
|
||||
}
|
||||
|
|
@ -147,12 +155,13 @@ std::string get_mac_address(const std::string_view &address) {
|
|||
|
||||
BOOST_LOG(warning) << "Unable to find MAC address for "sv << address;
|
||||
return "00:00:00:00:00:00"s;
|
||||
}
|
||||
}
|
||||
|
||||
bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
bp::child
|
||||
run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
BOOST_LOG(warning) << "run_unprivileged() is not yet implemented for this platform. The new process will run with Sunshine's permissions."sv;
|
||||
if(!group) {
|
||||
if(!file) {
|
||||
if (!group) {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec);
|
||||
}
|
||||
else {
|
||||
|
|
@ -160,58 +169,66 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
}
|
||||
}
|
||||
else {
|
||||
if(!file) {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec, *group);
|
||||
}
|
||||
else {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec, *group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adjust_thread_priority(thread_priority_e priority) {
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority) {
|
||||
// Unimplemented
|
||||
}
|
||||
}
|
||||
|
||||
void streaming_will_start() {
|
||||
void
|
||||
streaming_will_start() {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
void streaming_will_stop() {
|
||||
void
|
||||
streaming_will_stop() {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
bool restart_supported() {
|
||||
bool
|
||||
restart_supported() {
|
||||
// Restart not supported yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool restart() {
|
||||
bool
|
||||
restart() {
|
||||
// Restart not supported yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool send_batch(batched_send_info_t &send_info) {
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info) {
|
||||
// Fall back to unbatched send calls
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type) {
|
||||
std::unique_ptr<deinit_t>
|
||||
enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type) {
|
||||
// Unimplemented
|
||||
//
|
||||
// NB: When implementing, remember to consider that some routes can drop DSCP-tagged packets completely!
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace platf
|
||||
|
||||
namespace dyn {
|
||||
void *handle(const std::vector<const char *> &libs) {
|
||||
void *
|
||||
handle(const std::vector<const char *> &libs) {
|
||||
void *handle;
|
||||
|
||||
for(auto lib : libs) {
|
||||
for (auto lib : libs) {
|
||||
handle = dlopen(lib, RTLD_LAZY | RTLD_LOCAL);
|
||||
if(handle) {
|
||||
if (handle) {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
|
@ -227,16 +244,17 @@ void *handle(const std::vector<const char *> &libs) {
|
|||
BOOST_LOG(error) << ss.str();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict) {
|
||||
int
|
||||
load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &funcs, bool strict) {
|
||||
int err = 0;
|
||||
for(auto &func : funcs) {
|
||||
for (auto &func : funcs) {
|
||||
TUPLE_2D_REF(fn, name, func);
|
||||
|
||||
*fn = (void (*)())dlsym(handle, name);
|
||||
*fn = (void (*)()) dlsym(handle, name);
|
||||
|
||||
if(!*fn && strict) {
|
||||
if (!*fn && strict) {
|
||||
BOOST_LOG(error) << "Couldn't find function: "sv << name;
|
||||
|
||||
err = -1;
|
||||
|
|
@ -244,5 +262,5 @@ int load(void *handle, const std::vector<std::tuple<apiproc *, const char *>> &f
|
|||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} // namespace dyn
|
||||
|
|
|
|||
|
|
@ -9,23 +9,25 @@ extern "C" {
|
|||
|
||||
namespace platf {
|
||||
|
||||
void free_frame(AVFrame *frame) {
|
||||
void
|
||||
free_frame(AVFrame *frame) {
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
}
|
||||
|
||||
util::safe_ptr<AVFrame, free_frame> av_frame;
|
||||
util::safe_ptr<AVFrame, free_frame> av_frame;
|
||||
|
||||
int nv12_zero_device::convert(platf::img_t &img) {
|
||||
int
|
||||
nv12_zero_device::convert(platf::img_t &img) {
|
||||
av_frame_make_writable(av_frame.get());
|
||||
|
||||
av_img_t *av_img = (av_img_t *)&img;
|
||||
av_img_t *av_img = (av_img_t *) &img;
|
||||
|
||||
size_t left_pad, right_pad, top_pad, bottom_pad;
|
||||
CVPixelBufferGetExtendedPixels(av_img->pixel_buffer, &left_pad, &right_pad, &top_pad, &bottom_pad);
|
||||
|
||||
const uint8_t *data = (const uint8_t *)CVPixelBufferGetBaseAddressOfPlane(av_img->pixel_buffer, 0) - left_pad - (top_pad * img.width);
|
||||
const uint8_t *data = (const uint8_t *) CVPixelBufferGetBaseAddressOfPlane(av_img->pixel_buffer, 0) - left_pad - (top_pad * img.width);
|
||||
|
||||
int result = av_image_fill_arrays(av_frame->data, av_frame->linesize, data, (AVPixelFormat)av_frame->format, img.width, img.height, 32);
|
||||
int result = av_image_fill_arrays(av_frame->data, av_frame->linesize, data, (AVPixelFormat) av_frame->format, img.width, img.height, 32);
|
||||
|
||||
// We will create the black bars for the padding top/bottom or left/right here in very cheap way.
|
||||
// The luminance is 0, therefore, we simply need to set the chroma values to 128 for each pixel
|
||||
|
|
@ -37,23 +39,24 @@ int nv12_zero_device::convert(platf::img_t &img) {
|
|||
|
||||
size_t uv_plane_height = CVPixelBufferGetHeightOfPlane(av_img->pixel_buffer, 1);
|
||||
|
||||
if(left_pad || right_pad) {
|
||||
for(int l = 0; l < uv_plane_height + (top_pad / 2); l++) {
|
||||
if (left_pad || right_pad) {
|
||||
for (int l = 0; l < uv_plane_height + (top_pad / 2); l++) {
|
||||
int line = l * av_frame->linesize[1];
|
||||
memset((void *)&av_frame->data[1][line], 128, (size_t)left_pad);
|
||||
memset((void *)&av_frame->data[1][line + img.width - right_pad], 128, right_pad);
|
||||
memset((void *) &av_frame->data[1][line], 128, (size_t) left_pad);
|
||||
memset((void *) &av_frame->data[1][line + img.width - right_pad], 128, right_pad);
|
||||
}
|
||||
}
|
||||
|
||||
if(top_pad || bottom_pad) {
|
||||
memset((void *)&av_frame->data[1][0], 128, (top_pad / 2) * av_frame->linesize[1]);
|
||||
memset((void *)&av_frame->data[1][((top_pad / 2) + uv_plane_height) * av_frame->linesize[1]], 128, bottom_pad / 2 * av_frame->linesize[1]);
|
||||
if (top_pad || bottom_pad) {
|
||||
memset((void *) &av_frame->data[1][0], 128, (top_pad / 2) * av_frame->linesize[1]);
|
||||
memset((void *) &av_frame->data[1][((top_pad / 2) + uv_plane_height) * av_frame->linesize[1]], 128, bottom_pad / 2 * av_frame->linesize[1]);
|
||||
}
|
||||
|
||||
return result > 0 ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
int nv12_zero_device::set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
||||
int
|
||||
nv12_zero_device::set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
||||
this->frame = frame;
|
||||
|
||||
av_frame.reset(frame);
|
||||
|
|
@ -61,12 +64,14 @@ int nv12_zero_device::set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
|||
resolution_fn(this->display, frame->width, frame->height);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void nv12_zero_device::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {
|
||||
}
|
||||
void
|
||||
nv12_zero_device::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) {
|
||||
}
|
||||
|
||||
int nv12_zero_device::init(void *display, resolution_fn_t resolution_fn, pixel_format_fn_t pixel_format_fn) {
|
||||
int
|
||||
nv12_zero_device::init(void *display, resolution_fn_t resolution_fn, pixel_format_fn_t pixel_format_fn) {
|
||||
pixel_format_fn(display, '420v');
|
||||
|
||||
this->display = display;
|
||||
|
|
@ -77,6 +82,6 @@ int nv12_zero_device::init(void *display, resolution_fn_t resolution_fn, pixel_f
|
|||
data = this;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -5,24 +5,28 @@
|
|||
|
||||
namespace platf {
|
||||
|
||||
class nv12_zero_device : public hwdevice_t {
|
||||
class nv12_zero_device: public hwdevice_t {
|
||||
// display holds a pointer to an av_video object. Since the namespaces of AVFoundation
|
||||
// and FFMPEG collide, we need this opaque pointer and cannot use the definition
|
||||
void *display;
|
||||
|
||||
public:
|
||||
public:
|
||||
// this function is used to set the resolution on an av_video object that we cannot
|
||||
// call directly because of namespace collisions between AVFoundation and FFMPEG
|
||||
using resolution_fn_t = std::function<void(void *display, int width, int height)>;
|
||||
resolution_fn_t resolution_fn;
|
||||
using pixel_format_fn_t = std::function<void(void *display, int pixelFormat)>;
|
||||
|
||||
int init(void *display, resolution_fn_t resolution_fn, pixel_format_fn_t pixel_format_fn);
|
||||
int
|
||||
init(void *display, resolution_fn_t resolution_fn, pixel_format_fn_t pixel_format_fn);
|
||||
|
||||
int convert(img_t &img);
|
||||
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx);
|
||||
void set_colorspace(std::uint32_t colorspace, std::uint32_t color_range);
|
||||
};
|
||||
int
|
||||
convert(img_t &img);
|
||||
int
|
||||
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx);
|
||||
void
|
||||
set_colorspace(std::uint32_t colorspace, std::uint32_t color_range);
|
||||
};
|
||||
|
||||
} // namespace platf
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ using namespace std::literals;
|
|||
|
||||
namespace avahi {
|
||||
|
||||
/** Error codes used by avahi */
|
||||
enum err_e {
|
||||
/** Error codes used by avahi */
|
||||
enum err_e {
|
||||
OK = 0, /**< OK */
|
||||
ERR_FAILURE = -1, /**< Generic error code */
|
||||
ERR_BAD_STATE = -2, /**< Object was in a bad state */
|
||||
|
|
@ -75,46 +75,46 @@ enum err_e {
|
|||
ERR_NO_CHANGE = -53, /**< The requested operation is invalid because it is redundant */
|
||||
|
||||
ERR_MAX = -54
|
||||
};
|
||||
};
|
||||
|
||||
constexpr auto IF_UNSPEC = -1;
|
||||
enum proto {
|
||||
constexpr auto IF_UNSPEC = -1;
|
||||
enum proto {
|
||||
PROTO_INET = 0, /**< IPv4 */
|
||||
PROTO_INET6 = 1, /**< IPv6 */
|
||||
PROTO_UNSPEC = -1 /**< Unspecified/all protocol(s) */
|
||||
};
|
||||
};
|
||||
|
||||
enum ServerState {
|
||||
enum ServerState {
|
||||
SERVER_INVALID, /**< Invalid state (initial) */
|
||||
SERVER_REGISTERING, /**< Host RRs are being registered */
|
||||
SERVER_RUNNING, /**< All host RRs have been established */
|
||||
SERVER_COLLISION, /**< There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */
|
||||
SERVER_FAILURE /**< Some fatal failure happened, the server is unable to proceed */
|
||||
};
|
||||
};
|
||||
|
||||
enum ClientState {
|
||||
enum ClientState {
|
||||
CLIENT_S_REGISTERING = SERVER_REGISTERING, /**< Server state: REGISTERING */
|
||||
CLIENT_S_RUNNING = SERVER_RUNNING, /**< Server state: RUNNING */
|
||||
CLIENT_S_COLLISION = SERVER_COLLISION, /**< Server state: COLLISION */
|
||||
CLIENT_FAILURE = 100, /**< Some kind of error happened on the client side */
|
||||
CLIENT_CONNECTING = 101 /**< We're still connecting. This state is only entered when AVAHI_CLIENT_NO_FAIL has been passed to avahi_client_new() and the daemon is not yet available. */
|
||||
};
|
||||
};
|
||||
|
||||
enum EntryGroupState {
|
||||
enum EntryGroupState {
|
||||
ENTRY_GROUP_UNCOMMITED, /**< The group has not yet been commited, the user must still call avahi_entry_group_commit() */
|
||||
ENTRY_GROUP_REGISTERING, /**< The entries of the group are currently being registered */
|
||||
ENTRY_GROUP_ESTABLISHED, /**< The entries have successfully been established */
|
||||
ENTRY_GROUP_COLLISION, /**< A name collision for one of the entries in the group has been detected, the entries have been withdrawn */
|
||||
ENTRY_GROUP_FAILURE /**< Some kind of failure happened, the entries have been withdrawn */
|
||||
};
|
||||
};
|
||||
|
||||
enum ClientFlags {
|
||||
enum ClientFlags {
|
||||
CLIENT_IGNORE_USER_CONFIG = 1, /**< Don't read user configuration */
|
||||
CLIENT_NO_FAIL = 2 /**< Don't fail if the daemon is not available when avahi_client_new() is called, instead enter CLIENT_CONNECTING state and wait for the daemon to appear */
|
||||
};
|
||||
};
|
||||
|
||||
/** Some flags for publishing functions */
|
||||
enum PublishFlags {
|
||||
/** Some flags for publishing functions */
|
||||
enum PublishFlags {
|
||||
PUBLISH_UNIQUE = 1, /**< For raw records: The RRset is intended to be unique */
|
||||
PUBLISH_NO_PROBE = 2, /**< For raw records: Though the RRset is intended to be unique no probes shall be sent */
|
||||
PUBLISH_NO_ANNOUNCE = 4, /**< For raw records: Do not announce this RR to other hosts */
|
||||
|
|
@ -128,29 +128,29 @@ enum PublishFlags {
|
|||
PUBLISH_USE_WIDE_AREA = 128, /**< Register the record using wide area DNS (i.e. unicast DNS update) */
|
||||
PUBLISH_USE_MULTICAST = 256 /**< Register the record using multicast DNS */
|
||||
/** \endcond */
|
||||
};
|
||||
};
|
||||
|
||||
using IfIndex = int;
|
||||
using Protocol = int;
|
||||
using IfIndex = int;
|
||||
using Protocol = int;
|
||||
|
||||
struct EntryGroup;
|
||||
struct Poll;
|
||||
struct SimplePoll;
|
||||
struct Client;
|
||||
struct EntryGroup;
|
||||
struct Poll;
|
||||
struct SimplePoll;
|
||||
struct Client;
|
||||
|
||||
typedef void (*ClientCallback)(Client *, ClientState, void *userdata);
|
||||
typedef void (*EntryGroupCallback)(EntryGroup *g, EntryGroupState state, void *userdata);
|
||||
typedef void (*ClientCallback)(Client *, ClientState, void *userdata);
|
||||
typedef void (*EntryGroupCallback)(EntryGroup *g, EntryGroupState state, void *userdata);
|
||||
|
||||
typedef void (*free_fn)(void *);
|
||||
typedef void (*free_fn)(void *);
|
||||
|
||||
typedef Client *(*client_new_fn)(const Poll *poll_api, ClientFlags flags, ClientCallback callback, void *userdata, int *error);
|
||||
typedef void (*client_free_fn)(Client *);
|
||||
typedef char *(*alternative_service_name_fn)(char *);
|
||||
typedef Client *(*client_new_fn)(const Poll *poll_api, ClientFlags flags, ClientCallback callback, void *userdata, int *error);
|
||||
typedef void (*client_free_fn)(Client *);
|
||||
typedef char *(*alternative_service_name_fn)(char *);
|
||||
|
||||
typedef Client *(*entry_group_get_client_fn)(EntryGroup *);
|
||||
typedef Client *(*entry_group_get_client_fn)(EntryGroup *);
|
||||
|
||||
typedef EntryGroup *(*entry_group_new_fn)(Client *, EntryGroupCallback, void *userdata);
|
||||
typedef int (*entry_group_add_service_fn)(
|
||||
typedef EntryGroup *(*entry_group_new_fn)(Client *, EntryGroupCallback, void *userdata);
|
||||
typedef int (*entry_group_add_service_fn)(
|
||||
EntryGroup *group,
|
||||
IfIndex interface,
|
||||
Protocol protocol,
|
||||
|
|
@ -162,136 +162,140 @@ typedef int (*entry_group_add_service_fn)(
|
|||
uint16_t port,
|
||||
...);
|
||||
|
||||
typedef int (*entry_group_is_empty_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_reset_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_commit_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_is_empty_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_reset_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_commit_fn)(EntryGroup *);
|
||||
|
||||
typedef char *(*strdup_fn)(const char *);
|
||||
typedef char *(*strerror_fn)(int);
|
||||
typedef int (*client_errno_fn)(Client *);
|
||||
typedef char *(*strdup_fn)(const char *);
|
||||
typedef char *(*strerror_fn)(int);
|
||||
typedef int (*client_errno_fn)(Client *);
|
||||
|
||||
typedef Poll *(*simple_poll_get_fn)(SimplePoll *);
|
||||
typedef int (*simple_poll_loop_fn)(SimplePoll *);
|
||||
typedef void (*simple_poll_quit_fn)(SimplePoll *);
|
||||
typedef SimplePoll *(*simple_poll_new_fn)();
|
||||
typedef void (*simple_poll_free_fn)(SimplePoll *);
|
||||
typedef Poll *(*simple_poll_get_fn)(SimplePoll *);
|
||||
typedef int (*simple_poll_loop_fn)(SimplePoll *);
|
||||
typedef void (*simple_poll_quit_fn)(SimplePoll *);
|
||||
typedef SimplePoll *(*simple_poll_new_fn)();
|
||||
typedef void (*simple_poll_free_fn)(SimplePoll *);
|
||||
|
||||
free_fn free;
|
||||
client_new_fn client_new;
|
||||
client_free_fn client_free;
|
||||
alternative_service_name_fn alternative_service_name;
|
||||
entry_group_get_client_fn entry_group_get_client;
|
||||
entry_group_new_fn entry_group_new;
|
||||
entry_group_add_service_fn entry_group_add_service;
|
||||
entry_group_is_empty_fn entry_group_is_empty;
|
||||
entry_group_reset_fn entry_group_reset;
|
||||
entry_group_commit_fn entry_group_commit;
|
||||
strdup_fn strdup;
|
||||
strerror_fn strerror;
|
||||
client_errno_fn client_errno;
|
||||
simple_poll_get_fn simple_poll_get;
|
||||
simple_poll_loop_fn simple_poll_loop;
|
||||
simple_poll_quit_fn simple_poll_quit;
|
||||
simple_poll_new_fn simple_poll_new;
|
||||
simple_poll_free_fn simple_poll_free;
|
||||
free_fn free;
|
||||
client_new_fn client_new;
|
||||
client_free_fn client_free;
|
||||
alternative_service_name_fn alternative_service_name;
|
||||
entry_group_get_client_fn entry_group_get_client;
|
||||
entry_group_new_fn entry_group_new;
|
||||
entry_group_add_service_fn entry_group_add_service;
|
||||
entry_group_is_empty_fn entry_group_is_empty;
|
||||
entry_group_reset_fn entry_group_reset;
|
||||
entry_group_commit_fn entry_group_commit;
|
||||
strdup_fn strdup;
|
||||
strerror_fn strerror;
|
||||
client_errno_fn client_errno;
|
||||
simple_poll_get_fn simple_poll_get;
|
||||
simple_poll_loop_fn simple_poll_loop;
|
||||
simple_poll_quit_fn simple_poll_quit;
|
||||
simple_poll_new_fn simple_poll_new;
|
||||
simple_poll_free_fn simple_poll_free;
|
||||
|
||||
|
||||
int init_common() {
|
||||
int
|
||||
init_common() {
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libavahi-common.3.dylib", "libavahi-common.dylib" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&alternative_service_name, "avahi_alternative_service_name" },
|
||||
{ (dyn::apiproc *)&free, "avahi_free" },
|
||||
{ (dyn::apiproc *)&strdup, "avahi_strdup" },
|
||||
{ (dyn::apiproc *)&strerror, "avahi_strerror" },
|
||||
{ (dyn::apiproc *)&simple_poll_get, "avahi_simple_poll_get" },
|
||||
{ (dyn::apiproc *)&simple_poll_loop, "avahi_simple_poll_loop" },
|
||||
{ (dyn::apiproc *)&simple_poll_quit, "avahi_simple_poll_quit" },
|
||||
{ (dyn::apiproc *)&simple_poll_new, "avahi_simple_poll_new" },
|
||||
{ (dyn::apiproc *)&simple_poll_free, "avahi_simple_poll_free" },
|
||||
{ (dyn::apiproc *) &alternative_service_name, "avahi_alternative_service_name" },
|
||||
{ (dyn::apiproc *) &free, "avahi_free" },
|
||||
{ (dyn::apiproc *) &strdup, "avahi_strdup" },
|
||||
{ (dyn::apiproc *) &strerror, "avahi_strerror" },
|
||||
{ (dyn::apiproc *) &simple_poll_get, "avahi_simple_poll_get" },
|
||||
{ (dyn::apiproc *) &simple_poll_loop, "avahi_simple_poll_loop" },
|
||||
{ (dyn::apiproc *) &simple_poll_quit, "avahi_simple_poll_quit" },
|
||||
{ (dyn::apiproc *) &simple_poll_new, "avahi_simple_poll_new" },
|
||||
{ (dyn::apiproc *) &simple_poll_free, "avahi_simple_poll_free" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int init_client() {
|
||||
if(init_common()) {
|
||||
int
|
||||
init_client() {
|
||||
if (init_common()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *handle { nullptr };
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if(funcs_loaded) return 0;
|
||||
if (funcs_loaded) return 0;
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libavahi-client.3.dylib", "libavahi-client.dylib" });
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::tuple<dyn::apiproc *, const char *>> funcs {
|
||||
{ (dyn::apiproc *)&client_new, "avahi_client_new" },
|
||||
{ (dyn::apiproc *)&client_free, "avahi_client_free" },
|
||||
{ (dyn::apiproc *)&entry_group_get_client, "avahi_entry_group_get_client" },
|
||||
{ (dyn::apiproc *)&entry_group_new, "avahi_entry_group_new" },
|
||||
{ (dyn::apiproc *)&entry_group_add_service, "avahi_entry_group_add_service" },
|
||||
{ (dyn::apiproc *)&entry_group_is_empty, "avahi_entry_group_is_empty" },
|
||||
{ (dyn::apiproc *)&entry_group_reset, "avahi_entry_group_reset" },
|
||||
{ (dyn::apiproc *)&entry_group_commit, "avahi_entry_group_commit" },
|
||||
{ (dyn::apiproc *)&client_errno, "avahi_client_errno" },
|
||||
{ (dyn::apiproc *) &client_new, "avahi_client_new" },
|
||||
{ (dyn::apiproc *) &client_free, "avahi_client_free" },
|
||||
{ (dyn::apiproc *) &entry_group_get_client, "avahi_entry_group_get_client" },
|
||||
{ (dyn::apiproc *) &entry_group_new, "avahi_entry_group_new" },
|
||||
{ (dyn::apiproc *) &entry_group_add_service, "avahi_entry_group_add_service" },
|
||||
{ (dyn::apiproc *) &entry_group_is_empty, "avahi_entry_group_is_empty" },
|
||||
{ (dyn::apiproc *) &entry_group_reset, "avahi_entry_group_reset" },
|
||||
{ (dyn::apiproc *) &entry_group_commit, "avahi_entry_group_commit" },
|
||||
{ (dyn::apiproc *) &client_errno, "avahi_client_errno" },
|
||||
};
|
||||
|
||||
if(dyn::load(handle, funcs)) {
|
||||
if (dyn::load(handle, funcs)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
funcs_loaded = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace avahi
|
||||
|
||||
namespace platf::publish {
|
||||
|
||||
template<class T>
|
||||
void free(T *p) {
|
||||
template <class T>
|
||||
void
|
||||
free(T *p) {
|
||||
avahi::free(p);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
using ptr_t = util::safe_ptr<T, free<T>>;
|
||||
using client_t = util::dyn_safe_ptr<avahi::Client, &avahi::client_free>;
|
||||
using poll_t = util::dyn_safe_ptr<avahi::SimplePoll, &avahi::simple_poll_free>;
|
||||
template <class T>
|
||||
using ptr_t = util::safe_ptr<T, free<T>>;
|
||||
using client_t = util::dyn_safe_ptr<avahi::Client, &avahi::client_free>;
|
||||
using poll_t = util::dyn_safe_ptr<avahi::SimplePoll, &avahi::simple_poll_free>;
|
||||
|
||||
avahi::EntryGroup *group = nullptr;
|
||||
avahi::EntryGroup *group = nullptr;
|
||||
|
||||
poll_t poll;
|
||||
client_t client;
|
||||
poll_t poll;
|
||||
client_t client;
|
||||
|
||||
ptr_t<char> name;
|
||||
ptr_t<char> name;
|
||||
|
||||
void create_services(avahi::Client *c);
|
||||
void
|
||||
create_services(avahi::Client *c);
|
||||
|
||||
void entry_group_callback(avahi::EntryGroup *g, avahi::EntryGroupState state, void *) {
|
||||
void
|
||||
entry_group_callback(avahi::EntryGroup *g, avahi::EntryGroupState state, void *) {
|
||||
group = g;
|
||||
|
||||
switch(state) {
|
||||
switch (state) {
|
||||
case avahi::ENTRY_GROUP_ESTABLISHED:
|
||||
BOOST_LOG(info) << "Avahi service " << name.get() << " successfully established.";
|
||||
break;
|
||||
|
|
@ -309,23 +313,24 @@ void entry_group_callback(avahi::EntryGroup *g, avahi::EntryGroupState state, vo
|
|||
case avahi::ENTRY_GROUP_UNCOMMITED:
|
||||
case avahi::ENTRY_GROUP_REGISTERING:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void create_services(avahi::Client *c) {
|
||||
void
|
||||
create_services(avahi::Client *c) {
|
||||
int ret;
|
||||
|
||||
auto fg = util::fail_guard([]() {
|
||||
avahi::simple_poll_quit(poll.get());
|
||||
});
|
||||
|
||||
if(!group) {
|
||||
if(!(group = avahi::entry_group_new(c, entry_group_callback, nullptr))) {
|
||||
if (!group) {
|
||||
if (!(group = avahi::entry_group_new(c, entry_group_callback, nullptr))) {
|
||||
BOOST_LOG(error) << "avahi::entry_group_new() failed: "sv << avahi::strerror(avahi::client_errno(c));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(avahi::entry_group_is_empty(group)) {
|
||||
if (avahi::entry_group_is_empty(group)) {
|
||||
BOOST_LOG(info) << "Adding avahi service "sv << name.get();
|
||||
|
||||
ret = avahi::entry_group_add_service(
|
||||
|
|
@ -338,8 +343,8 @@ void create_services(avahi::Client *c) {
|
|||
map_port(nvhttp::PORT_HTTP),
|
||||
nullptr);
|
||||
|
||||
if(ret < 0) {
|
||||
if(ret == avahi::ERR_COLLISION) {
|
||||
if (ret < 0) {
|
||||
if (ret == avahi::ERR_COLLISION) {
|
||||
// A service name collision with a local service happened. Let's pick a new name
|
||||
name.reset(avahi::alternative_service_name(name.get()));
|
||||
BOOST_LOG(info) << "Service name collision, renaming service to "sv << name.get();
|
||||
|
|
@ -357,17 +362,18 @@ void create_services(avahi::Client *c) {
|
|||
}
|
||||
|
||||
ret = avahi::entry_group_commit(group);
|
||||
if(ret < 0) {
|
||||
if (ret < 0) {
|
||||
BOOST_LOG(error) << "Failed to commit entry group: "sv << avahi::strerror(ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fg.disable();
|
||||
}
|
||||
}
|
||||
|
||||
void client_callback(avahi::Client *c, avahi::ClientState state, void *) {
|
||||
switch(state) {
|
||||
void
|
||||
client_callback(avahi::Client *c, avahi::ClientState state, void *) {
|
||||
switch (state) {
|
||||
case avahi::CLIENT_S_RUNNING:
|
||||
create_services(c);
|
||||
break;
|
||||
|
|
@ -377,39 +383,41 @@ void client_callback(avahi::Client *c, avahi::ClientState state, void *) {
|
|||
break;
|
||||
case avahi::CLIENT_S_COLLISION:
|
||||
case avahi::CLIENT_S_REGISTERING:
|
||||
if(group)
|
||||
if (group)
|
||||
avahi::entry_group_reset(group);
|
||||
break;
|
||||
case avahi::CLIENT_CONNECTING:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class deinit_t : public ::platf::deinit_t {
|
||||
public:
|
||||
class deinit_t: public ::platf::deinit_t {
|
||||
public:
|
||||
std::thread poll_thread;
|
||||
|
||||
deinit_t(std::thread poll_thread) : poll_thread { std::move(poll_thread) } {}
|
||||
deinit_t(std::thread poll_thread):
|
||||
poll_thread { std::move(poll_thread) } {}
|
||||
|
||||
~deinit_t() override {
|
||||
if(avahi::simple_poll_quit && poll) {
|
||||
if (avahi::simple_poll_quit && poll) {
|
||||
avahi::simple_poll_quit(poll.get());
|
||||
}
|
||||
|
||||
if(poll_thread.joinable()) {
|
||||
if (poll_thread.joinable()) {
|
||||
poll_thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<::platf::deinit_t> start() {
|
||||
if(avahi::init_client()) {
|
||||
[[nodiscard]] std::unique_ptr<::platf::deinit_t>
|
||||
start() {
|
||||
if (avahi::init_client()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int avhi_error;
|
||||
|
||||
poll.reset(avahi::simple_poll_new());
|
||||
if(!poll) {
|
||||
if (!poll) {
|
||||
BOOST_LOG(error) << "Failed to create simple poll object."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -419,11 +427,11 @@ public:
|
|||
client.reset(
|
||||
avahi::client_new(avahi::simple_poll_get(poll.get()), avahi::ClientFlags(0), client_callback, nullptr, &avhi_error));
|
||||
|
||||
if(!client) {
|
||||
if (!client) {
|
||||
BOOST_LOG(error) << "Failed to create client: "sv << avahi::strerror(avhi_error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<deinit_t>(std::thread { avahi::simple_poll_loop, poll.get() });
|
||||
}
|
||||
}
|
||||
}; // namespace platf::publish
|
||||
|
|
|
|||
|
|
@ -6,16 +6,15 @@
|
|||
// http://eretik.omegahg.com/
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#undef DEFINE_GUID
|
||||
#ifdef __cplusplus
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
#else
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
#endif
|
||||
#undef DEFINE_GUID
|
||||
#ifdef __cplusplus
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
#else
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
#endif
|
||||
|
||||
DEFINE_GUID(IID_IPolicyConfig, 0xf8679f50, 0x850a, 0x41cf, 0x9c, 0x72, 0x43, 0x0f, 0x29, 0x02, 0x90, 0xc8);
|
||||
DEFINE_GUID(CLSID_CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf, 0x0d, 0xe6, 0x3d, 0xf4, 0x0c, 0x2b, 0xc9);
|
||||
|
|
@ -37,13 +36,15 @@ class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient;
|
|||
//
|
||||
// @compatible: Windows 7 and Later
|
||||
// ----------------------------------------------------------------------------
|
||||
interface IPolicyConfig : public IUnknown {
|
||||
interface IPolicyConfig: public IUnknown {
|
||||
public:
|
||||
virtual HRESULT GetMixFormat(
|
||||
virtual HRESULT
|
||||
GetMixFormat(
|
||||
PCWSTR,
|
||||
WAVEFORMATEX **);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetDeviceFormat(
|
||||
PCWSTR,
|
||||
INT,
|
||||
WAVEFORMATEX **);
|
||||
|
|
@ -51,7 +52,8 @@ public:
|
|||
virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat(
|
||||
PCWSTR);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetDeviceFormat(
|
||||
PCWSTR,
|
||||
WAVEFORMATEX *,
|
||||
WAVEFORMATEX *);
|
||||
|
|
@ -66,25 +68,30 @@ public:
|
|||
PCWSTR,
|
||||
PINT64);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetShareMode(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetShareMode(
|
||||
PCWSTR,
|
||||
struct DeviceShareMode *);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetShareMode(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetShareMode(
|
||||
PCWSTR,
|
||||
struct DeviceShareMode *);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetPropertyValue(
|
||||
PCWSTR,
|
||||
const PROPERTYKEY &,
|
||||
PROPVARIANT *);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetPropertyValue(
|
||||
PCWSTR,
|
||||
const PROPERTYKEY &,
|
||||
PROPVARIANT *);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetDefaultEndpoint(
|
||||
PCWSTR wszDeviceId,
|
||||
ERole eRole);
|
||||
|
||||
|
|
@ -108,18 +115,21 @@ class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") CPolicyConfigVistaCl
|
|||
//
|
||||
// @compatible: Windows Vista and Later
|
||||
// ----------------------------------------------------------------------------
|
||||
interface IPolicyConfigVista : public IUnknown {
|
||||
interface IPolicyConfigVista: public IUnknown {
|
||||
public:
|
||||
virtual HRESULT GetMixFormat(
|
||||
virtual HRESULT
|
||||
GetMixFormat(
|
||||
PCWSTR,
|
||||
WAVEFORMATEX **); // not available on Windows 7, use method from IPolicyConfig
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetDeviceFormat(
|
||||
PCWSTR,
|
||||
INT,
|
||||
WAVEFORMATEX **);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetDeviceFormat(
|
||||
PCWSTR,
|
||||
WAVEFORMATEX *,
|
||||
WAVEFORMATEX *);
|
||||
|
|
@ -134,25 +144,30 @@ public:
|
|||
PCWSTR,
|
||||
PINT64); // not available on Windows 7, use method from IPolicyConfig
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetShareMode(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetShareMode(
|
||||
PCWSTR,
|
||||
struct DeviceShareMode *); // not available on Windows 7, use method from IPolicyConfig
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetShareMode(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetShareMode(
|
||||
PCWSTR,
|
||||
struct DeviceShareMode *); // not available on Windows 7, use method from IPolicyConfig
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
GetPropertyValue(
|
||||
PCWSTR,
|
||||
const PROPERTYKEY &,
|
||||
PROPVARIANT *);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetPropertyValue(
|
||||
PCWSTR,
|
||||
const PROPERTYKEY &,
|
||||
PROPVARIANT *);
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
|
||||
virtual HRESULT STDMETHODCALLTYPE
|
||||
SetDefaultEndpoint(
|
||||
PCWSTR wszDeviceId,
|
||||
ERole eRole);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,31 +34,33 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
|
|||
|
||||
using namespace std::literals;
|
||||
namespace platf::audio {
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
constexpr auto SAMPLE_RATE = 48000;
|
||||
|
||||
template<class T>
|
||||
void Release(T *p) {
|
||||
template <class T>
|
||||
void
|
||||
Release(T *p) {
|
||||
p->Release();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void co_task_free(T *p) {
|
||||
CoTaskMemFree((LPVOID)p);
|
||||
}
|
||||
template <class T>
|
||||
void
|
||||
co_task_free(T *p) {
|
||||
CoTaskMemFree((LPVOID) p);
|
||||
}
|
||||
|
||||
using device_enum_t = util::safe_ptr<IMMDeviceEnumerator, Release<IMMDeviceEnumerator>>;
|
||||
using device_t = util::safe_ptr<IMMDevice, Release<IMMDevice>>;
|
||||
using collection_t = util::safe_ptr<IMMDeviceCollection, Release<IMMDeviceCollection>>;
|
||||
using audio_client_t = util::safe_ptr<IAudioClient, Release<IAudioClient>>;
|
||||
using audio_capture_t = util::safe_ptr<IAudioCaptureClient, Release<IAudioCaptureClient>>;
|
||||
using wave_format_t = util::safe_ptr<WAVEFORMATEX, co_task_free<WAVEFORMATEX>>;
|
||||
using wstring_t = util::safe_ptr<WCHAR, co_task_free<WCHAR>>;
|
||||
using handle_t = util::safe_ptr_v2<void, BOOL, CloseHandle>;
|
||||
using policy_t = util::safe_ptr<IPolicyConfig, Release<IPolicyConfig>>;
|
||||
using prop_t = util::safe_ptr<IPropertyStore, Release<IPropertyStore>>;
|
||||
using device_enum_t = util::safe_ptr<IMMDeviceEnumerator, Release<IMMDeviceEnumerator>>;
|
||||
using device_t = util::safe_ptr<IMMDevice, Release<IMMDevice>>;
|
||||
using collection_t = util::safe_ptr<IMMDeviceCollection, Release<IMMDeviceCollection>>;
|
||||
using audio_client_t = util::safe_ptr<IAudioClient, Release<IAudioClient>>;
|
||||
using audio_capture_t = util::safe_ptr<IAudioCaptureClient, Release<IAudioCaptureClient>>;
|
||||
using wave_format_t = util::safe_ptr<WAVEFORMATEX, co_task_free<WAVEFORMATEX>>;
|
||||
using wstring_t = util::safe_ptr<WCHAR, co_task_free<WCHAR>>;
|
||||
using handle_t = util::safe_ptr_v2<void, BOOL, CloseHandle>;
|
||||
using policy_t = util::safe_ptr<IPolicyConfig, Release<IPolicyConfig>>;
|
||||
using prop_t = util::safe_ptr<IPropertyStore, Release<IPropertyStore>>;
|
||||
|
||||
class co_init_t : public deinit_t {
|
||||
public:
|
||||
class co_init_t: public deinit_t {
|
||||
public:
|
||||
co_init_t() {
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY);
|
||||
}
|
||||
|
|
@ -66,10 +68,10 @@ public:
|
|||
~co_init_t() override {
|
||||
CoUninitialize();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class prop_var_t {
|
||||
public:
|
||||
class prop_var_t {
|
||||
public:
|
||||
prop_var_t() {
|
||||
PropVariantInit(&prop);
|
||||
}
|
||||
|
|
@ -79,10 +81,10 @@ public:
|
|||
}
|
||||
|
||||
PROPVARIANT prop;
|
||||
};
|
||||
};
|
||||
|
||||
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
|
||||
struct format_t {
|
||||
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
|
||||
struct format_t {
|
||||
enum type_e : int {
|
||||
none,
|
||||
stereo,
|
||||
|
|
@ -93,7 +95,7 @@ struct format_t {
|
|||
std::string_view name;
|
||||
int channels;
|
||||
int channel_mask;
|
||||
} formats[] {
|
||||
} formats[] {
|
||||
{
|
||||
format_t::stereo,
|
||||
"Stereo"sv,
|
||||
|
|
@ -124,9 +126,9 @@ struct format_t {
|
|||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
static format_t surround_51_side_speakers {
|
||||
static format_t surround_51_side_speakers {
|
||||
format_t::surr51,
|
||||
"Surround 5.1"sv,
|
||||
6,
|
||||
|
|
@ -136,9 +138,10 @@ static format_t surround_51_side_speakers {
|
|||
SPEAKER_LOW_FREQUENCY |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT,
|
||||
};
|
||||
};
|
||||
|
||||
WAVEFORMATEXTENSIBLE create_wave_format(const format_t &format) {
|
||||
WAVEFORMATEXTENSIBLE
|
||||
create_wave_format(const format_t &format) {
|
||||
WAVEFORMATEXTENSIBLE wave_format;
|
||||
|
||||
wave_format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
|
|
@ -154,19 +157,20 @@ WAVEFORMATEXTENSIBLE create_wave_format(const format_t &format) {
|
|||
wave_format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
return wave_format;
|
||||
}
|
||||
}
|
||||
|
||||
int set_wave_format(audio::wave_format_t &wave_format, const format_t &format) {
|
||||
int
|
||||
set_wave_format(audio::wave_format_t &wave_format, const format_t &format) {
|
||||
wave_format->nSamplesPerSec = SAMPLE_RATE;
|
||||
wave_format->wBitsPerSample = 16;
|
||||
|
||||
switch(wave_format->wFormatTag) {
|
||||
switch (wave_format->wFormatTag) {
|
||||
case WAVE_FORMAT_PCM:
|
||||
break;
|
||||
case WAVE_FORMAT_IEEE_FLOAT:
|
||||
break;
|
||||
case WAVE_FORMAT_EXTENSIBLE: {
|
||||
auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get();
|
||||
auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get();
|
||||
wave_ex->Samples.wValidBitsPerSample = 16;
|
||||
wave_ex->dwChannelMask = format.channel_mask;
|
||||
wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
|
@ -182,17 +186,18 @@ int set_wave_format(audio::wave_format_t &wave_format, const format_t &format) {
|
|||
wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
||||
audio_client_t
|
||||
make_audio_client(device_t &device, const format_t &format) {
|
||||
audio_client_t audio_client;
|
||||
auto status = device->Activate(
|
||||
IID_IAudioClient,
|
||||
CLSCTX_ALL,
|
||||
nullptr,
|
||||
(void **)&audio_client);
|
||||
(void **) &audio_client);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return nullptr;
|
||||
|
|
@ -205,39 +210,42 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
|||
AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
|
||||
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, // Enable automatic resampling to 48 KHz
|
||||
0, 0,
|
||||
(LPWAVEFORMATEX)&wave_format,
|
||||
(LPWAVEFORMATEX) &wave_format,
|
||||
nullptr);
|
||||
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(debug) << "Couldn't initialize audio client for ["sv << format.name << "]: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audio_client;
|
||||
}
|
||||
}
|
||||
|
||||
const wchar_t *no_null(const wchar_t *str) {
|
||||
const wchar_t *
|
||||
no_null(const wchar_t *str) {
|
||||
return str ? str : L"Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool validate_device(device_t &device) {
|
||||
bool
|
||||
validate_device(device_t &device) {
|
||||
bool valid = false;
|
||||
|
||||
// Check for any valid format
|
||||
for(const auto &format : formats) {
|
||||
for (const auto &format : formats) {
|
||||
auto audio_client = make_audio_client(device, format);
|
||||
|
||||
BOOST_LOG(debug) << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv);
|
||||
|
||||
if(audio_client) {
|
||||
if (audio_client) {
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
}
|
||||
|
||||
device_t default_device(device_enum_t &device_enum) {
|
||||
device_t
|
||||
default_device(device_enum_t &device_enum) {
|
||||
device_t device;
|
||||
HRESULT status;
|
||||
status = device_enum->GetDefaultAudioEndpoint(
|
||||
|
|
@ -245,25 +253,25 @@ device_t default_device(device_enum_t &device_enum) {
|
|||
eConsole,
|
||||
&device);
|
||||
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create audio Device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
class mic_wasapi_t : public mic_t {
|
||||
public:
|
||||
capture_e sample(std::vector<std::int16_t> &sample_out) override {
|
||||
class mic_wasapi_t: public mic_t {
|
||||
public:
|
||||
capture_e
|
||||
sample(std::vector<std::int16_t> &sample_out) override {
|
||||
auto sample_size = sample_out.size();
|
||||
|
||||
// Refill the sample buffer if needed
|
||||
while(sample_buf_pos - std::begin(sample_buf) < sample_size) {
|
||||
while (sample_buf_pos - std::begin(sample_buf) < sample_size) {
|
||||
auto capture_result = _fill_buffer();
|
||||
if(capture_result != capture_e::ok) {
|
||||
if (capture_result != capture_e::ok) {
|
||||
return capture_result;
|
||||
}
|
||||
}
|
||||
|
|
@ -278,10 +286,10 @@ public:
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
|
||||
int init(std::uint32_t sample_rate, std::uint32_t frame_size, std::uint32_t channels_out) {
|
||||
int
|
||||
init(std::uint32_t sample_rate, std::uint32_t frame_size, std::uint32_t channels_out) {
|
||||
audio_event.reset(CreateEventA(nullptr, FALSE, FALSE, nullptr));
|
||||
if(!audio_event) {
|
||||
if (!audio_event) {
|
||||
BOOST_LOG(error) << "Couldn't create Event handle"sv;
|
||||
|
||||
return -1;
|
||||
|
|
@ -294,21 +302,21 @@ public:
|
|||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator,
|
||||
(void **)&device_enum);
|
||||
(void **) &device_enum);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create Device Enumerator [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto device = default_device(device_enum);
|
||||
if(!device) {
|
||||
if (!device) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(auto &format : formats) {
|
||||
if(format.channels != channels_out) {
|
||||
for (auto &format : formats) {
|
||||
if (format.channels != channels_out) {
|
||||
BOOST_LOG(debug) << "Skipping audio format ["sv << format.name << "] with channel count ["sv << format.channels << " != "sv << channels_out << ']';
|
||||
continue;
|
||||
}
|
||||
|
|
@ -316,14 +324,14 @@ public:
|
|||
BOOST_LOG(debug) << "Trying audio format ["sv << format.name << ']';
|
||||
audio_client = make_audio_client(device, format);
|
||||
|
||||
if(audio_client) {
|
||||
if (audio_client) {
|
||||
BOOST_LOG(debug) << "Found audio format ["sv << format.name << ']';
|
||||
channels = channels_out;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!audio_client) {
|
||||
if (!audio_client) {
|
||||
BOOST_LOG(error) << "Couldn't find supported format for audio"sv;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -334,7 +342,7 @@ public:
|
|||
|
||||
std::uint32_t frames;
|
||||
status = audio_client->GetBufferSize(&frames);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't acquire the number of audio frames [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
|
|
@ -344,22 +352,22 @@ public:
|
|||
sample_buf = util::buffer_t<std::int16_t> { std::max(frames, frame_size) * 2 * channels_out };
|
||||
sample_buf_pos = std::begin(sample_buf);
|
||||
|
||||
status = audio_client->GetService(IID_IAudioCaptureClient, (void **)&audio_capture);
|
||||
if(FAILED(status)) {
|
||||
status = audio_client->GetService(IID_IAudioCaptureClient, (void **) &audio_capture);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't initialize audio capture client [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = audio_client->SetEventHandle(audio_event.get());
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't set event handle [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = audio_client->Start();
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't start recording [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
|
|
@ -369,13 +377,14 @@ public:
|
|||
}
|
||||
|
||||
~mic_wasapi_t() override {
|
||||
if(audio_client) {
|
||||
if (audio_client) {
|
||||
audio_client->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
capture_e _fill_buffer() {
|
||||
private:
|
||||
capture_e
|
||||
_fill_buffer() {
|
||||
HRESULT status;
|
||||
|
||||
// Total number of samples
|
||||
|
|
@ -390,7 +399,7 @@ private:
|
|||
} block_aligned;
|
||||
|
||||
status = WaitForSingleObjectEx(audio_event.get(), default_latency_ms, FALSE);
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
|
|
@ -401,18 +410,18 @@ private:
|
|||
}
|
||||
|
||||
std::uint32_t packet_size {};
|
||||
for(
|
||||
for (
|
||||
status = audio_capture->GetNextPacketSize(&packet_size);
|
||||
SUCCEEDED(status) && packet_size > 0;
|
||||
status = audio_capture->GetNextPacketSize(&packet_size)) {
|
||||
DWORD buffer_flags;
|
||||
status = audio_capture->GetBuffer(
|
||||
(BYTE **)&sample_aligned.samples,
|
||||
(BYTE **) &sample_aligned.samples,
|
||||
&block_aligned.audio_sample_size,
|
||||
&buffer_flags,
|
||||
nullptr, nullptr);
|
||||
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case S_OK:
|
||||
break;
|
||||
case AUDCLNT_E_DEVICE_INVALIDATED:
|
||||
|
|
@ -425,7 +434,7 @@ private:
|
|||
sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos;
|
||||
auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * channels);
|
||||
|
||||
if(buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) {
|
||||
if (buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) {
|
||||
std::fill_n(sample_buf_pos, n, 0);
|
||||
}
|
||||
else {
|
||||
|
|
@ -437,18 +446,18 @@ private:
|
|||
audio_capture->ReleaseBuffer(block_aligned.audio_sample_size);
|
||||
}
|
||||
|
||||
if(status == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||
if (status == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
handle_t audio_event;
|
||||
|
||||
device_enum_t device_enum;
|
||||
|
|
@ -461,11 +470,12 @@ public:
|
|||
util::buffer_t<std::int16_t> sample_buf;
|
||||
std::int16_t *sample_buf_pos;
|
||||
int channels;
|
||||
};
|
||||
};
|
||||
|
||||
class audio_control_t : public ::platf::audio_control_t {
|
||||
public:
|
||||
std::optional<sink_t> sink_info() override {
|
||||
class audio_control_t: public ::platf::audio_control_t {
|
||||
public:
|
||||
std::optional<sink_t>
|
||||
sink_info() override {
|
||||
auto virtual_adapter_name = L"Steam Streaming Speakers"sv;
|
||||
|
||||
sink_t sink;
|
||||
|
|
@ -476,16 +486,16 @@ public:
|
|||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator,
|
||||
(void **)&device_enum);
|
||||
(void **) &device_enum);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto device = default_device(device_enum);
|
||||
if(!device) {
|
||||
if (!device) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -496,7 +506,7 @@ public:
|
|||
|
||||
collection_t collection;
|
||||
status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return std::nullopt;
|
||||
|
|
@ -506,11 +516,11 @@ public:
|
|||
collection->GetCount(&count);
|
||||
|
||||
std::string virtual_device_id = config::audio.virtual_sink;
|
||||
for(auto x = 0; x < count; ++x) {
|
||||
for (auto x = 0; x < count; ++x) {
|
||||
audio::device_t device;
|
||||
collection->Item(x, &device);
|
||||
|
||||
if(!validate_device(device)) {
|
||||
if (!validate_device(device)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -528,21 +538,21 @@ public:
|
|||
prop->GetValue(PKEY_DeviceInterface_FriendlyName, &adapter_friendly_name.prop);
|
||||
prop->GetValue(PKEY_Device_DeviceDesc, &device_desc.prop);
|
||||
|
||||
auto adapter_name = no_null((LPWSTR)adapter_friendly_name.prop.pszVal);
|
||||
auto adapter_name = no_null((LPWSTR) adapter_friendly_name.prop.pszVal);
|
||||
BOOST_LOG(verbose)
|
||||
<< L"===== Device ====="sv << std::endl
|
||||
<< L"Device ID : "sv << wstring.get() << std::endl
|
||||
<< L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl
|
||||
<< L"Device name : "sv << no_null((LPWSTR) device_friendly_name.prop.pszVal) << std::endl
|
||||
<< L"Adapter name : "sv << adapter_name << std::endl
|
||||
<< L"Device description : "sv << no_null((LPWSTR)device_desc.prop.pszVal) << std::endl
|
||||
<< L"Device description : "sv << no_null((LPWSTR) device_desc.prop.pszVal) << std::endl
|
||||
<< std::endl;
|
||||
|
||||
if(virtual_device_id.empty() && adapter_name == virtual_adapter_name) {
|
||||
if (virtual_device_id.empty() && adapter_name == virtual_adapter_name) {
|
||||
virtual_device_id = converter.to_bytes(wstring.get());
|
||||
}
|
||||
}
|
||||
|
||||
if(!virtual_device_id.empty()) {
|
||||
if (!virtual_device_id.empty()) {
|
||||
sink.null = std::make_optional(sink_t::null_t {
|
||||
"virtual-"s.append(formats[format_t::stereo - 1].name) + virtual_device_id,
|
||||
"virtual-"s.append(formats[format_t::surr51 - 1].name) + virtual_device_id,
|
||||
|
|
@ -553,10 +563,11 @@ public:
|
|||
return sink;
|
||||
}
|
||||
|
||||
std::unique_ptr<mic_t> microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
|
||||
std::unique_ptr<mic_t>
|
||||
microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) override {
|
||||
auto mic = std::make_unique<mic_wasapi_t>();
|
||||
|
||||
if(mic->init(sample_rate, frame_size, channels)) {
|
||||
if (mic->init(sample_rate, frame_size, channels)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -571,19 +582,20 @@ public:
|
|||
* virtual-(format name)
|
||||
* If it doesn't contain that prefix, then the format will not be changed
|
||||
*/
|
||||
std::optional<std::wstring> set_format(const std::string &sink) {
|
||||
std::optional<std::wstring>
|
||||
set_format(const std::string &sink) {
|
||||
std::string_view sv { sink.c_str(), sink.size() };
|
||||
|
||||
format_t::type_e type = format_t::none;
|
||||
// sink format:
|
||||
// [virtual-(format name)]device_id
|
||||
auto prefix = "virtual-"sv;
|
||||
if(sv.find(prefix) == 0) {
|
||||
if (sv.find(prefix) == 0) {
|
||||
sv = sv.substr(prefix.size(), sv.size() - prefix.size());
|
||||
|
||||
for(auto &format : formats) {
|
||||
for (auto &format : formats) {
|
||||
auto &name = format.name;
|
||||
if(sv.find(name) == 0) {
|
||||
if (sv.find(name) == 0) {
|
||||
type = format.type;
|
||||
sv = sv.substr(name.size(), sv.size() - name.size());
|
||||
|
||||
|
|
@ -594,7 +606,7 @@ public:
|
|||
|
||||
auto wstring_device_id = converter.from_bytes(sv.data());
|
||||
|
||||
if(type == format_t::none) {
|
||||
if (type == format_t::none) {
|
||||
// wstring_device_id does not contain virtual-(format name)
|
||||
// It's a simple deviceId, just pass it back
|
||||
return std::make_optional(std::move(wstring_device_id));
|
||||
|
|
@ -602,24 +614,24 @@ public:
|
|||
|
||||
wave_format_t wave_format;
|
||||
auto status = policy->GetMixFormat(wstring_device_id.c_str(), &wave_format);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
set_wave_format(wave_format, formats[(int)type - 1]);
|
||||
set_wave_format(wave_format, formats[(int) type - 1]);
|
||||
|
||||
WAVEFORMATEXTENSIBLE p {};
|
||||
status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), (WAVEFORMATEX *)&p);
|
||||
status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), (WAVEFORMATEX *) &p);
|
||||
|
||||
// Surround 5.1 might contain side-{left, right} instead of speaker in the back
|
||||
// Try again with different speaker mask.
|
||||
if(status == 0x88890008 && type == format_t::surr51) {
|
||||
if (status == 0x88890008 && type == format_t::surr51) {
|
||||
set_wave_format(wave_format, surround_51_side_speakers);
|
||||
status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), (WAVEFORMATEX *)&p);
|
||||
status = policy->SetDeviceFormat(wstring_device_id.c_str(), wave_format.get(), (WAVEFORMATEX *) &p);
|
||||
}
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't set Wave Format [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return std::nullopt;
|
||||
|
|
@ -628,16 +640,17 @@ public:
|
|||
return std::make_optional(std::move(wstring_device_id));
|
||||
}
|
||||
|
||||
int set_sink(const std::string &sink) override {
|
||||
int
|
||||
set_sink(const std::string &sink) override {
|
||||
auto wstring_device_id = set_format(sink);
|
||||
if(!wstring_device_id) {
|
||||
if (!wstring_device_id) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int failure {};
|
||||
for(int x = 0; x < (int)ERole_enum_count; ++x) {
|
||||
auto status = policy->SetDefaultEndpoint(wstring_device_id->c_str(), (ERole)x);
|
||||
if(status) {
|
||||
for (int x = 0; x < (int) ERole_enum_count; ++x) {
|
||||
auto status = policy->SetDefaultEndpoint(wstring_device_id->c_str(), (ERole) x);
|
||||
if (status) {
|
||||
BOOST_LOG(warning) << "Couldn't set ["sv << sink << "] to role ["sv << x << ']';
|
||||
|
||||
++failure;
|
||||
|
|
@ -647,15 +660,16 @@ public:
|
|||
return failure;
|
||||
}
|
||||
|
||||
int init() {
|
||||
int
|
||||
init() {
|
||||
auto status = CoCreateInstance(
|
||||
CLSID_CPolicyConfigClient,
|
||||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IPolicyConfig,
|
||||
(void **)&policy);
|
||||
(void **) &policy);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't create audio policy config: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
|
|
@ -667,30 +681,33 @@ public:
|
|||
~audio_control_t() override {}
|
||||
|
||||
policy_t policy;
|
||||
};
|
||||
};
|
||||
} // namespace platf::audio
|
||||
|
||||
namespace platf {
|
||||
|
||||
// It's not big enough to justify it's own source file :/
|
||||
namespace dxgi {
|
||||
int init();
|
||||
}
|
||||
// It's not big enough to justify it's own source file :/
|
||||
namespace dxgi {
|
||||
int
|
||||
init();
|
||||
}
|
||||
|
||||
std::unique_ptr<audio_control_t> audio_control() {
|
||||
std::unique_ptr<audio_control_t>
|
||||
audio_control() {
|
||||
auto control = std::make_unique<audio::audio_control_t>();
|
||||
|
||||
if(control->init()) {
|
||||
if (control->init()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return control;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<deinit_t> init() {
|
||||
if(dxgi::init()) {
|
||||
std::unique_ptr<deinit_t>
|
||||
init() {
|
||||
if (dxgi::init()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<platf::audio::co_init_t>();
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -16,77 +16,81 @@
|
|||
#include "src/utility.h"
|
||||
|
||||
namespace platf::dxgi {
|
||||
extern const char *format_str[];
|
||||
extern const char *format_str[];
|
||||
|
||||
// Add D3D11_CREATE_DEVICE_DEBUG here to enable the D3D11 debug runtime.
|
||||
// You should have a debugger like WinDbg attached to receive debug messages.
|
||||
auto constexpr D3D11_CREATE_DEVICE_FLAGS = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
|
||||
// Add D3D11_CREATE_DEVICE_DEBUG here to enable the D3D11 debug runtime.
|
||||
// You should have a debugger like WinDbg attached to receive debug messages.
|
||||
auto constexpr D3D11_CREATE_DEVICE_FLAGS = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
|
||||
|
||||
template<class T>
|
||||
void Release(T *dxgi) {
|
||||
template <class T>
|
||||
void
|
||||
Release(T *dxgi) {
|
||||
dxgi->Release();
|
||||
}
|
||||
}
|
||||
|
||||
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
|
||||
using dxgi_t = util::safe_ptr<IDXGIDevice, Release<IDXGIDevice>>;
|
||||
using dxgi1_t = util::safe_ptr<IDXGIDevice1, Release<IDXGIDevice1>>;
|
||||
using device_t = util::safe_ptr<ID3D11Device, Release<ID3D11Device>>;
|
||||
using device1_t = util::safe_ptr<ID3D11Device1, Release<ID3D11Device1>>;
|
||||
using device_ctx_t = util::safe_ptr<ID3D11DeviceContext, Release<ID3D11DeviceContext>>;
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
using output1_t = util::safe_ptr<IDXGIOutput1, Release<IDXGIOutput1>>;
|
||||
using output5_t = util::safe_ptr<IDXGIOutput5, Release<IDXGIOutput5>>;
|
||||
using output6_t = util::safe_ptr<IDXGIOutput6, Release<IDXGIOutput6>>;
|
||||
using dup_t = util::safe_ptr<IDXGIOutputDuplication, Release<IDXGIOutputDuplication>>;
|
||||
using texture2d_t = util::safe_ptr<ID3D11Texture2D, Release<ID3D11Texture2D>>;
|
||||
using texture1d_t = util::safe_ptr<ID3D11Texture1D, Release<ID3D11Texture1D>>;
|
||||
using resource_t = util::safe_ptr<IDXGIResource, Release<IDXGIResource>>;
|
||||
using resource1_t = util::safe_ptr<IDXGIResource1, Release<IDXGIResource1>>;
|
||||
using multithread_t = util::safe_ptr<ID3D11Multithread, Release<ID3D11Multithread>>;
|
||||
using vs_t = util::safe_ptr<ID3D11VertexShader, Release<ID3D11VertexShader>>;
|
||||
using ps_t = util::safe_ptr<ID3D11PixelShader, Release<ID3D11PixelShader>>;
|
||||
using blend_t = util::safe_ptr<ID3D11BlendState, Release<ID3D11BlendState>>;
|
||||
using input_layout_t = util::safe_ptr<ID3D11InputLayout, Release<ID3D11InputLayout>>;
|
||||
using render_target_t = util::safe_ptr<ID3D11RenderTargetView, Release<ID3D11RenderTargetView>>;
|
||||
using shader_res_t = util::safe_ptr<ID3D11ShaderResourceView, Release<ID3D11ShaderResourceView>>;
|
||||
using buf_t = util::safe_ptr<ID3D11Buffer, Release<ID3D11Buffer>>;
|
||||
using raster_state_t = util::safe_ptr<ID3D11RasterizerState, Release<ID3D11RasterizerState>>;
|
||||
using sampler_state_t = util::safe_ptr<ID3D11SamplerState, Release<ID3D11SamplerState>>;
|
||||
using blob_t = util::safe_ptr<ID3DBlob, Release<ID3DBlob>>;
|
||||
using depth_stencil_state_t = util::safe_ptr<ID3D11DepthStencilState, Release<ID3D11DepthStencilState>>;
|
||||
using depth_stencil_view_t = util::safe_ptr<ID3D11DepthStencilView, Release<ID3D11DepthStencilView>>;
|
||||
using keyed_mutex_t = util::safe_ptr<IDXGIKeyedMutex, Release<IDXGIKeyedMutex>>;
|
||||
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
|
||||
using dxgi_t = util::safe_ptr<IDXGIDevice, Release<IDXGIDevice>>;
|
||||
using dxgi1_t = util::safe_ptr<IDXGIDevice1, Release<IDXGIDevice1>>;
|
||||
using device_t = util::safe_ptr<ID3D11Device, Release<ID3D11Device>>;
|
||||
using device1_t = util::safe_ptr<ID3D11Device1, Release<ID3D11Device1>>;
|
||||
using device_ctx_t = util::safe_ptr<ID3D11DeviceContext, Release<ID3D11DeviceContext>>;
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
using output1_t = util::safe_ptr<IDXGIOutput1, Release<IDXGIOutput1>>;
|
||||
using output5_t = util::safe_ptr<IDXGIOutput5, Release<IDXGIOutput5>>;
|
||||
using output6_t = util::safe_ptr<IDXGIOutput6, Release<IDXGIOutput6>>;
|
||||
using dup_t = util::safe_ptr<IDXGIOutputDuplication, Release<IDXGIOutputDuplication>>;
|
||||
using texture2d_t = util::safe_ptr<ID3D11Texture2D, Release<ID3D11Texture2D>>;
|
||||
using texture1d_t = util::safe_ptr<ID3D11Texture1D, Release<ID3D11Texture1D>>;
|
||||
using resource_t = util::safe_ptr<IDXGIResource, Release<IDXGIResource>>;
|
||||
using resource1_t = util::safe_ptr<IDXGIResource1, Release<IDXGIResource1>>;
|
||||
using multithread_t = util::safe_ptr<ID3D11Multithread, Release<ID3D11Multithread>>;
|
||||
using vs_t = util::safe_ptr<ID3D11VertexShader, Release<ID3D11VertexShader>>;
|
||||
using ps_t = util::safe_ptr<ID3D11PixelShader, Release<ID3D11PixelShader>>;
|
||||
using blend_t = util::safe_ptr<ID3D11BlendState, Release<ID3D11BlendState>>;
|
||||
using input_layout_t = util::safe_ptr<ID3D11InputLayout, Release<ID3D11InputLayout>>;
|
||||
using render_target_t = util::safe_ptr<ID3D11RenderTargetView, Release<ID3D11RenderTargetView>>;
|
||||
using shader_res_t = util::safe_ptr<ID3D11ShaderResourceView, Release<ID3D11ShaderResourceView>>;
|
||||
using buf_t = util::safe_ptr<ID3D11Buffer, Release<ID3D11Buffer>>;
|
||||
using raster_state_t = util::safe_ptr<ID3D11RasterizerState, Release<ID3D11RasterizerState>>;
|
||||
using sampler_state_t = util::safe_ptr<ID3D11SamplerState, Release<ID3D11SamplerState>>;
|
||||
using blob_t = util::safe_ptr<ID3DBlob, Release<ID3DBlob>>;
|
||||
using depth_stencil_state_t = util::safe_ptr<ID3D11DepthStencilState, Release<ID3D11DepthStencilState>>;
|
||||
using depth_stencil_view_t = util::safe_ptr<ID3D11DepthStencilView, Release<ID3D11DepthStencilView>>;
|
||||
using keyed_mutex_t = util::safe_ptr<IDXGIKeyedMutex, Release<IDXGIKeyedMutex>>;
|
||||
|
||||
namespace video {
|
||||
using device_t = util::safe_ptr<ID3D11VideoDevice, Release<ID3D11VideoDevice>>;
|
||||
using ctx_t = util::safe_ptr<ID3D11VideoContext, Release<ID3D11VideoContext>>;
|
||||
using processor_t = util::safe_ptr<ID3D11VideoProcessor, Release<ID3D11VideoProcessor>>;
|
||||
using processor_out_t = util::safe_ptr<ID3D11VideoProcessorOutputView, Release<ID3D11VideoProcessorOutputView>>;
|
||||
using processor_in_t = util::safe_ptr<ID3D11VideoProcessorInputView, Release<ID3D11VideoProcessorInputView>>;
|
||||
using processor_enum_t = util::safe_ptr<ID3D11VideoProcessorEnumerator, Release<ID3D11VideoProcessorEnumerator>>;
|
||||
} // namespace video
|
||||
namespace video {
|
||||
using device_t = util::safe_ptr<ID3D11VideoDevice, Release<ID3D11VideoDevice>>;
|
||||
using ctx_t = util::safe_ptr<ID3D11VideoContext, Release<ID3D11VideoContext>>;
|
||||
using processor_t = util::safe_ptr<ID3D11VideoProcessor, Release<ID3D11VideoProcessor>>;
|
||||
using processor_out_t = util::safe_ptr<ID3D11VideoProcessorOutputView, Release<ID3D11VideoProcessorOutputView>>;
|
||||
using processor_in_t = util::safe_ptr<ID3D11VideoProcessorInputView, Release<ID3D11VideoProcessorInputView>>;
|
||||
using processor_enum_t = util::safe_ptr<ID3D11VideoProcessorEnumerator, Release<ID3D11VideoProcessorEnumerator>>;
|
||||
} // namespace video
|
||||
|
||||
class hwdevice_t;
|
||||
struct cursor_t {
|
||||
class hwdevice_t;
|
||||
struct cursor_t {
|
||||
std::vector<std::uint8_t> img_data;
|
||||
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info;
|
||||
int x, y;
|
||||
bool visible;
|
||||
};
|
||||
};
|
||||
|
||||
class gpu_cursor_t {
|
||||
public:
|
||||
gpu_cursor_t() : cursor_view { 0, 0, 0, 0, 0.0f, 1.0f } {};
|
||||
void set_pos(LONG rel_x, LONG rel_y, bool visible) {
|
||||
class gpu_cursor_t {
|
||||
public:
|
||||
gpu_cursor_t():
|
||||
cursor_view { 0, 0, 0, 0, 0.0f, 1.0f } {};
|
||||
void
|
||||
set_pos(LONG rel_x, LONG rel_y, bool visible) {
|
||||
cursor_view.TopLeftX = rel_x;
|
||||
cursor_view.TopLeftY = rel_y;
|
||||
|
||||
this->visible = visible;
|
||||
}
|
||||
|
||||
void set_texture(LONG width, LONG height, texture2d_t &&texture) {
|
||||
void
|
||||
set_texture(LONG width, LONG height, texture2d_t &&texture) {
|
||||
cursor_view.Width = width;
|
||||
cursor_view.Height = height;
|
||||
|
||||
|
|
@ -99,25 +103,30 @@ public:
|
|||
D3D11_VIEWPORT cursor_view;
|
||||
|
||||
bool visible;
|
||||
};
|
||||
};
|
||||
|
||||
class duplication_t {
|
||||
public:
|
||||
class duplication_t {
|
||||
public:
|
||||
dup_t dup;
|
||||
bool has_frame {};
|
||||
bool use_dwmflush {};
|
||||
|
||||
capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p);
|
||||
capture_e reset(dup_t::pointer dup_p = dup_t::pointer());
|
||||
capture_e release_frame();
|
||||
capture_e
|
||||
next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p);
|
||||
capture_e
|
||||
reset(dup_t::pointer dup_p = dup_t::pointer());
|
||||
capture_e
|
||||
release_frame();
|
||||
|
||||
~duplication_t();
|
||||
};
|
||||
};
|
||||
|
||||
class display_base_t : public display_t {
|
||||
public:
|
||||
int init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override;
|
||||
class display_base_t: public display_t {
|
||||
public:
|
||||
int
|
||||
init(const ::video::config_t &config, const std::string &display_name);
|
||||
capture_e
|
||||
capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override;
|
||||
|
||||
std::chrono::nanoseconds delay;
|
||||
|
||||
|
|
@ -142,53 +151,77 @@ public:
|
|||
|
||||
typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);
|
||||
|
||||
virtual bool is_hdr() override;
|
||||
virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) override;
|
||||
virtual bool
|
||||
is_hdr() override;
|
||||
virtual bool
|
||||
get_hdr_metadata(SS_HDR_METADATA &metadata) override;
|
||||
|
||||
protected:
|
||||
int get_pixel_pitch() {
|
||||
protected:
|
||||
int
|
||||
get_pixel_pitch() {
|
||||
return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4;
|
||||
}
|
||||
|
||||
const char *dxgi_format_to_string(DXGI_FORMAT format);
|
||||
const char *colorspace_to_string(DXGI_COLOR_SPACE_TYPE type);
|
||||
const char *
|
||||
dxgi_format_to_string(DXGI_FORMAT format);
|
||||
const char *
|
||||
colorspace_to_string(DXGI_COLOR_SPACE_TYPE type);
|
||||
|
||||
virtual capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor_visible) = 0;
|
||||
virtual int complete_img(img_t *img, bool dummy) = 0;
|
||||
virtual std::vector<DXGI_FORMAT> get_supported_sdr_capture_formats() = 0;
|
||||
virtual std::vector<DXGI_FORMAT> get_supported_hdr_capture_formats() = 0;
|
||||
};
|
||||
virtual capture_e
|
||||
snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor_visible) = 0;
|
||||
virtual int
|
||||
complete_img(img_t *img, bool dummy) = 0;
|
||||
virtual std::vector<DXGI_FORMAT>
|
||||
get_supported_sdr_capture_formats() = 0;
|
||||
virtual std::vector<DXGI_FORMAT>
|
||||
get_supported_hdr_capture_formats() = 0;
|
||||
};
|
||||
|
||||
class display_ram_t : public display_base_t {
|
||||
public:
|
||||
virtual capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
class display_ram_t: public display_base_t {
|
||||
public:
|
||||
virtual capture_e
|
||||
snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
|
||||
std::shared_ptr<img_t> alloc_img() override;
|
||||
int dummy_img(img_t *img) override;
|
||||
int complete_img(img_t *img, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT> get_supported_sdr_capture_formats() override;
|
||||
std::vector<DXGI_FORMAT> get_supported_hdr_capture_formats() override;
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override;
|
||||
int
|
||||
dummy_img(img_t *img) override;
|
||||
int
|
||||
complete_img(img_t *img, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT>
|
||||
get_supported_sdr_capture_formats() override;
|
||||
std::vector<DXGI_FORMAT>
|
||||
get_supported_hdr_capture_formats() override;
|
||||
|
||||
int init(const ::video::config_t &config, const std::string &display_name);
|
||||
int
|
||||
init(const ::video::config_t &config, const std::string &display_name);
|
||||
|
||||
cursor_t cursor;
|
||||
D3D11_MAPPED_SUBRESOURCE img_info;
|
||||
texture2d_t texture;
|
||||
};
|
||||
};
|
||||
|
||||
class display_vram_t : public display_base_t, public std::enable_shared_from_this<display_vram_t> {
|
||||
public:
|
||||
virtual capture_e snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
class display_vram_t: public display_base_t, public std::enable_shared_from_this<display_vram_t> {
|
||||
public:
|
||||
virtual capture_e
|
||||
snapshot(img_t *img, std::chrono::milliseconds timeout, bool cursor_visible) override;
|
||||
|
||||
std::shared_ptr<img_t> alloc_img() override;
|
||||
int dummy_img(img_t *img_base) override;
|
||||
int complete_img(img_t *img_base, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT> get_supported_sdr_capture_formats() override;
|
||||
std::vector<DXGI_FORMAT> get_supported_hdr_capture_formats() override;
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override;
|
||||
int
|
||||
dummy_img(img_t *img_base) override;
|
||||
int
|
||||
complete_img(img_t *img_base, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT>
|
||||
get_supported_sdr_capture_formats() override;
|
||||
std::vector<DXGI_FORMAT>
|
||||
get_supported_hdr_capture_formats() override;
|
||||
|
||||
int init(const ::video::config_t &config, const std::string &display_name);
|
||||
int
|
||||
init(const ::video::config_t &config, const std::string &display_name);
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> make_hwdevice(pix_fmt_e pix_fmt) override;
|
||||
std::shared_ptr<platf::hwdevice_t>
|
||||
make_hwdevice(pix_fmt_e pix_fmt) override;
|
||||
|
||||
sampler_state_t sampler_linear;
|
||||
|
||||
|
|
@ -205,7 +238,7 @@ public:
|
|||
texture2d_t last_frame_copy;
|
||||
|
||||
std::atomic<uint32_t> next_image_id;
|
||||
};
|
||||
};
|
||||
} // namespace platf::dxgi
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,24 +20,25 @@ typedef long NTSTATUS;
|
|||
#include "src/video.h"
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
using namespace std::literals;
|
||||
}
|
||||
namespace platf::dxgi {
|
||||
namespace bp = boost::process;
|
||||
namespace bp = boost::process;
|
||||
|
||||
capture_e duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p) {
|
||||
capture_e
|
||||
duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p) {
|
||||
auto capture_status = release_frame();
|
||||
if(capture_status != capture_e::ok) {
|
||||
if (capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
}
|
||||
|
||||
if(use_dwmflush) {
|
||||
if (use_dwmflush) {
|
||||
DwmFlush();
|
||||
}
|
||||
|
||||
auto status = dup->AcquireNextFrame(timeout.count(), &frame_info, res_p);
|
||||
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case S_OK:
|
||||
has_frame = true;
|
||||
return capture_e::ok;
|
||||
|
|
@ -51,23 +52,25 @@ capture_e duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::ch
|
|||
BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view();
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
capture_e duplication_t::reset(dup_t::pointer dup_p) {
|
||||
capture_e
|
||||
duplication_t::reset(dup_t::pointer dup_p) {
|
||||
auto capture_status = release_frame();
|
||||
|
||||
dup.reset(dup_p);
|
||||
|
||||
return capture_status;
|
||||
}
|
||||
}
|
||||
|
||||
capture_e duplication_t::release_frame() {
|
||||
if(!has_frame) {
|
||||
capture_e
|
||||
duplication_t::release_frame() {
|
||||
if (!has_frame) {
|
||||
return capture_e::ok;
|
||||
}
|
||||
|
||||
auto status = dup->ReleaseFrame();
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case S_OK:
|
||||
has_frame = false;
|
||||
return capture_e::ok;
|
||||
|
|
@ -82,20 +85,21 @@ capture_e duplication_t::release_frame() {
|
|||
BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view();
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
duplication_t::~duplication_t() {
|
||||
duplication_t::~duplication_t() {
|
||||
release_frame();
|
||||
}
|
||||
}
|
||||
|
||||
capture_e display_base_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<::platf::img_t> img, bool *cursor) {
|
||||
capture_e
|
||||
display_base_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<::platf::img_t> img, bool *cursor) {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
// Use CREATE_WAITABLE_TIMER_HIGH_RESOLUTION if supported (Windows 10 1809+)
|
||||
HANDLE timer = CreateWaitableTimerEx(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
||||
if(!timer) {
|
||||
if (!timer) {
|
||||
timer = CreateWaitableTimerEx(nullptr, nullptr, 0, TIMER_ALL_ACCESS);
|
||||
if(!timer) {
|
||||
if (!timer) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to create timer: "sv << winerr;
|
||||
return capture_e::error;
|
||||
|
|
@ -106,18 +110,18 @@ capture_e display_base_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<:
|
|||
CloseHandle(timer);
|
||||
});
|
||||
|
||||
while(img) {
|
||||
while (img) {
|
||||
// This will return false if the HDR state changes or for any number of other
|
||||
// display or GPU changes. We should reinit to examine the updated state of
|
||||
// the display subsystem. It is recommended to call this once per frame.
|
||||
if(!factory->IsCurrent()) {
|
||||
if (!factory->IsCurrent()) {
|
||||
return platf::capture_e::reinit;
|
||||
}
|
||||
|
||||
// If the wait time is between 1 us and 1 second, wait the specified time
|
||||
// and offset the next frame time from the exact current frame time target.
|
||||
auto wait_time_us = std::chrono::duration_cast<std::chrono::microseconds>(next_frame - std::chrono::steady_clock::now()).count();
|
||||
if(wait_time_us > 0 && wait_time_us < 1000000) {
|
||||
if (wait_time_us > 0 && wait_time_us < 1000000) {
|
||||
LARGE_INTEGER due_time { .QuadPart = -10LL * wait_time_us };
|
||||
SetWaitableTimer(timer, &due_time, 0, nullptr, nullptr, false);
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
|
|
@ -132,7 +136,7 @@ capture_e display_base_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<:
|
|||
}
|
||||
|
||||
auto status = snapshot(img.get(), 1000ms, *cursor);
|
||||
switch(status) {
|
||||
switch (status) {
|
||||
case platf::capture_e::reinit:
|
||||
case platf::capture_e::error:
|
||||
return status;
|
||||
|
|
@ -143,15 +147,16 @@ capture_e display_base_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<:
|
|||
img = snapshot_cb(img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return capture_e::ok;
|
||||
}
|
||||
}
|
||||
|
||||
bool set_gpu_preference_on_self(int preference) {
|
||||
bool
|
||||
set_gpu_preference_on_self(int preference) {
|
||||
// The GPU preferences key uses app path as the value name.
|
||||
WCHAR sunshine_path[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, sunshine_path, ARRAYSIZE(sunshine_path));
|
||||
|
|
@ -165,29 +170,30 @@ bool set_gpu_preference_on_self(int preference) {
|
|||
REG_SZ,
|
||||
value_data,
|
||||
(wcslen(value_data) + 1) * sizeof(WCHAR));
|
||||
if(status != ERROR_SUCCESS) {
|
||||
if (status != ERROR_SUCCESS) {
|
||||
BOOST_LOG(error) << "Failed to set GPU preference: "sv << status;
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "Set GPU preference: "sv << preference;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// On hybrid graphics systems, Windows will change the order of GPUs reported by
|
||||
// DXGI in accordance with the user's GPU preference. If the selected GPU is a
|
||||
// render-only device with no displays, DXGI will add virtual outputs to the
|
||||
// that device to avoid confusing applications. While this works properly for most
|
||||
// applications, it breaks the Desktop Duplication API because DXGI doesn't proxy
|
||||
// the virtual DXGIOutput to the real GPU it is attached to. When trying to call
|
||||
// DuplicateOutput() on one of these virtual outputs, it fails with DXGI_ERROR_UNSUPPORTED
|
||||
// (even if you try sneaky stuff like passing the ID3D11Device for the iGPU and the
|
||||
// virtual DXGIOutput from the dGPU). Because the GPU preference is once-per-process,
|
||||
// we spawn a helper tool to probe for us before we set our own GPU preference.
|
||||
bool probe_for_gpu_preference(const std::string &display_name) {
|
||||
// On hybrid graphics systems, Windows will change the order of GPUs reported by
|
||||
// DXGI in accordance with the user's GPU preference. If the selected GPU is a
|
||||
// render-only device with no displays, DXGI will add virtual outputs to the
|
||||
// that device to avoid confusing applications. While this works properly for most
|
||||
// applications, it breaks the Desktop Duplication API because DXGI doesn't proxy
|
||||
// the virtual DXGIOutput to the real GPU it is attached to. When trying to call
|
||||
// DuplicateOutput() on one of these virtual outputs, it fails with DXGI_ERROR_UNSUPPORTED
|
||||
// (even if you try sneaky stuff like passing the ID3D11Device for the iGPU and the
|
||||
// virtual DXGIOutput from the dGPU). Because the GPU preference is once-per-process,
|
||||
// we spawn a helper tool to probe for us before we set our own GPU preference.
|
||||
bool
|
||||
probe_for_gpu_preference(const std::string &display_name) {
|
||||
// If we've already been through here, there's nothing to do this time.
|
||||
static bool set_gpu_preference = false;
|
||||
if(set_gpu_preference) {
|
||||
if (set_gpu_preference) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +203,7 @@ bool probe_for_gpu_preference(const std::string &display_name) {
|
|||
// the GPU driver control panel options. Since ddprobe.exe can have different
|
||||
// GPU driver overrides than Sunshine.exe, we want to avoid a scenario where
|
||||
// autoselection might work for ddprobe.exe but not for us.
|
||||
for(int i = 1; i < 5; i++) {
|
||||
for (int i = 1; i < 5; i++) {
|
||||
// Run the probe tool. It returns the status of DuplicateOutput().
|
||||
//
|
||||
// Arg format: [GPU preference] [Display name]
|
||||
|
|
@ -205,7 +211,7 @@ bool probe_for_gpu_preference(const std::string &display_name) {
|
|||
try {
|
||||
result = bp::system(cmd, std::to_string(i), display_name, bp::std_out > bp::null, bp::std_err > bp::null);
|
||||
}
|
||||
catch(bp::process_error &e) {
|
||||
catch (bp::process_error &e) {
|
||||
BOOST_LOG(error) << "Failed to start ddprobe.exe: "sv << e.what();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -215,9 +221,9 @@ bool probe_for_gpu_preference(const std::string &display_name) {
|
|||
// E_ACCESSDENIED can happen at the login screen. If we get this error,
|
||||
// we know capture would have been supported, because DXGI_ERROR_UNSUPPORTED
|
||||
// would have been raised first if it wasn't.
|
||||
if(result == S_OK || result == E_ACCESSDENIED) {
|
||||
if (result == S_OK || result == E_ACCESSDENIED) {
|
||||
// We found a working GPU preference, so set ourselves to use that.
|
||||
if(set_gpu_preference_on_self(i)) {
|
||||
if (set_gpu_preference_on_self(i)) {
|
||||
set_gpu_preference = true;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -233,9 +239,10 @@ bool probe_for_gpu_preference(const std::string &display_name) {
|
|||
|
||||
// If none of the manual options worked, leave the GPU preference alone
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool test_dxgi_duplication(adapter_t &adapter, output_t &output) {
|
||||
bool
|
||||
test_dxgi_duplication(adapter_t &adapter, output_t &output) {
|
||||
D3D_FEATURE_LEVEL featureLevels[] {
|
||||
D3D_FEATURE_LEVEL_11_1,
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
|
|
@ -257,23 +264,23 @@ bool test_dxgi_duplication(adapter_t &adapter, output_t &output) {
|
|||
&device,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create D3D11 device for DD test [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return false;
|
||||
}
|
||||
|
||||
output1_t output1;
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1);
|
||||
if(FAILED(status)) {
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **) &output1);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we can use the Desktop Duplication API on this output
|
||||
for(int x = 0; x < 2; ++x) {
|
||||
for (int x = 0; x < 2; ++x) {
|
||||
dup_t dup;
|
||||
status = output1->DuplicateOutput((IUnknown *)device.get(), &dup);
|
||||
if(SUCCEEDED(status)) {
|
||||
status = output1->DuplicateOutput((IUnknown *) device.get(), &dup);
|
||||
if (SUCCEEDED(status)) {
|
||||
return true;
|
||||
}
|
||||
Sleep(200);
|
||||
|
|
@ -281,9 +288,10 @@ bool test_dxgi_duplication(adapter_t &adapter, output_t &output) {
|
|||
|
||||
BOOST_LOG(error) << "DuplicateOutput() test failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int display_base_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
int
|
||||
display_base_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
std::once_flag windows_cpp_once_flag;
|
||||
|
||||
std::call_once(windows_cpp_once_flag, []() {
|
||||
|
|
@ -292,8 +300,8 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
typedef BOOL (*User32_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value);
|
||||
|
||||
auto user32 = LoadLibraryA("user32.dll");
|
||||
auto f = (User32_SetProcessDpiAwarenessContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext");
|
||||
if(f) {
|
||||
auto f = (User32_SetProcessDpiAwarenessContext) GetProcAddress(user32, "SetProcessDpiAwarenessContext");
|
||||
if (f) {
|
||||
f(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
|
||||
|
|
@ -312,12 +320,12 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
HRESULT status;
|
||||
|
||||
// We must set the GPU preference before calling any DXGI APIs!
|
||||
if(!probe_for_gpu_preference(display_name)) {
|
||||
if (!probe_for_gpu_preference(display_name)) {
|
||||
BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv;
|
||||
}
|
||||
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory);
|
||||
if(FAILED(status)) {
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -328,28 +336,28 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
auto output_name = converter.from_bytes(display_name);
|
||||
|
||||
adapter_t::pointer adapter_p;
|
||||
for(int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) {
|
||||
for (int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) {
|
||||
dxgi::adapter_t adapter_tmp { adapter_p };
|
||||
|
||||
DXGI_ADAPTER_DESC1 adapter_desc;
|
||||
adapter_tmp->GetDesc1(&adapter_desc);
|
||||
|
||||
if(!adapter_name.empty() && adapter_desc.Description != adapter_name) {
|
||||
if (!adapter_name.empty() && adapter_desc.Description != adapter_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dxgi::output_t::pointer output_p;
|
||||
for(int y = 0; adapter_tmp->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
for (int y = 0; adapter_tmp->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
dxgi::output_t output_tmp { output_p };
|
||||
|
||||
DXGI_OUTPUT_DESC desc;
|
||||
output_tmp->GetDesc(&desc);
|
||||
|
||||
if(!output_name.empty() && desc.DeviceName != output_name) {
|
||||
if (!output_name.empty() && desc.DeviceName != output_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(desc.AttachedToDesktop && test_dxgi_duplication(adapter_tmp, output_tmp)) {
|
||||
if (desc.AttachedToDesktop && test_dxgi_duplication(adapter_tmp, output_tmp)) {
|
||||
output = std::move(output_tmp);
|
||||
|
||||
offset_x = desc.DesktopCoordinates.left;
|
||||
|
|
@ -364,13 +372,13 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
}
|
||||
}
|
||||
|
||||
if(output) {
|
||||
if (output) {
|
||||
adapter = std::move(adapter_tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!output) {
|
||||
if (!output) {
|
||||
BOOST_LOG(error) << "Failed to locate an output device"sv;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -385,8 +393,8 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
D3D_FEATURE_LEVEL_9_1
|
||||
};
|
||||
|
||||
status = adapter->QueryInterface(IID_IDXGIAdapter, (void **)&adapter_p);
|
||||
if(FAILED(status)) {
|
||||
status = adapter->QueryInterface(IID_IDXGIAdapter, (void **) &adapter_p);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -404,7 +412,7 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
|
||||
adapter_p->Release();
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create D3D11 device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
|
|
@ -433,11 +441,11 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
timing_info.cbSize = sizeof(timing_info);
|
||||
|
||||
status = DwmGetCompositionTimingInfo(NULL, &timing_info);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to detect active refresh rate.";
|
||||
}
|
||||
else {
|
||||
refresh_rate = std::round((double)timing_info.rateRefresh.uiNumerator / (double)timing_info.rateRefresh.uiDenominator);
|
||||
refresh_rate = std::round((double) timing_info.rateRefresh.uiNumerator / (double) timing_info.rateRefresh.uiDenominator);
|
||||
}
|
||||
|
||||
dup.use_dwmflush = config::video.dwmflush && !(config.framerate > refresh_rate) ? true : false;
|
||||
|
|
@ -449,13 +457,13 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
HANDLE token;
|
||||
LUID val;
|
||||
|
||||
if(OpenProcessToken(GetCurrentProcess(), flags, &token) &&
|
||||
if (OpenProcessToken(GetCurrentProcess(), flags, &token) &&
|
||||
!!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) {
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = val;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if(!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) {
|
||||
if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) {
|
||||
BOOST_LOG(warning) << "Could not set privilege to increase GPU priority";
|
||||
}
|
||||
}
|
||||
|
|
@ -463,26 +471,26 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
CloseHandle(token);
|
||||
|
||||
HMODULE gdi32 = GetModuleHandleA("GDI32");
|
||||
if(gdi32) {
|
||||
if (gdi32) {
|
||||
PD3DKMTSetProcessSchedulingPriorityClass fn =
|
||||
(PD3DKMTSetProcessSchedulingPriorityClass)GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass");
|
||||
if(fn) {
|
||||
(PD3DKMTSetProcessSchedulingPriorityClass) GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass");
|
||||
if (fn) {
|
||||
status = fn(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to set realtime GPU priority. Please run application as administrator for optimal performance.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dxgi::dxgi_t dxgi;
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi);
|
||||
if(FAILED(status)) {
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = dxgi->SetGPUThreadPriority(7);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to increase capture GPU thread priority. Please run application as administrator for optimal performance.";
|
||||
}
|
||||
}
|
||||
|
|
@ -490,14 +498,14 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
// Try to reduce latency
|
||||
{
|
||||
dxgi::dxgi1_t dxgi {};
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi);
|
||||
if(FAILED(status)) {
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = dxgi->SetMaximumFrameLatency(1);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to set maximum frame latency [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
}
|
||||
}
|
||||
|
|
@ -506,19 +514,19 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
{
|
||||
// IDXGIOutput5 is optional, but can provide improved performance and wide color support
|
||||
dxgi::output5_t output5 {};
|
||||
status = output->QueryInterface(IID_IDXGIOutput5, (void **)&output5);
|
||||
if(SUCCEEDED(status)) {
|
||||
status = output->QueryInterface(IID_IDXGIOutput5, (void **) &output5);
|
||||
if (SUCCEEDED(status)) {
|
||||
// Ask the display implementation which formats it supports
|
||||
auto supported_formats = config.dynamicRange ? get_supported_hdr_capture_formats() : get_supported_sdr_capture_formats();
|
||||
if(supported_formats.empty()) {
|
||||
if (supported_formats.empty()) {
|
||||
BOOST_LOG(warning) << "No compatible capture formats for this encoder"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We try this twice, in case we still get an error on reinitialization
|
||||
for(int x = 0; x < 2; ++x) {
|
||||
status = output5->DuplicateOutput1((IUnknown *)device.get(), 0, supported_formats.size(), supported_formats.data(), &dup.dup);
|
||||
if(SUCCEEDED(status)) {
|
||||
for (int x = 0; x < 2; ++x) {
|
||||
status = output5->DuplicateOutput1((IUnknown *) device.get(), 0, supported_formats.size(), supported_formats.data(), &dup.dup);
|
||||
if (SUCCEEDED(status)) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(200ms);
|
||||
|
|
@ -527,7 +535,7 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
// We don't retry with DuplicateOutput() because we can hit this codepath when we're racing
|
||||
// with mode changes and we don't want to accidentally fall back to suboptimal capture if
|
||||
// we get unlucky and succeed below.
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "DuplicateOutput1 Failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -536,21 +544,21 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
BOOST_LOG(warning) << "IDXGIOutput5 is not supported by your OS. Capture performance may be reduced."sv;
|
||||
|
||||
dxgi::output1_t output1 {};
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1);
|
||||
if(FAILED(status)) {
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **) &output1);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(int x = 0; x < 2; ++x) {
|
||||
status = output1->DuplicateOutput((IUnknown *)device.get(), &dup.dup);
|
||||
if(SUCCEEDED(status)) {
|
||||
for (int x = 0; x < 2; ++x) {
|
||||
status = output1->DuplicateOutput((IUnknown *) device.get(), &dup.dup);
|
||||
if (SUCCEEDED(status)) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -564,8 +572,8 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
BOOST_LOG(info) << "Desktop format ["sv << dxgi_format_to_string(dup_desc.ModeDesc.Format) << ']';
|
||||
|
||||
dxgi::output6_t output6 {};
|
||||
status = output->QueryInterface(IID_IDXGIOutput6, (void **)&output6);
|
||||
if(SUCCEEDED(status)) {
|
||||
status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6);
|
||||
if (SUCCEEDED(status)) {
|
||||
DXGI_OUTPUT_DESC1 desc1;
|
||||
output6->GetDesc1(&desc1);
|
||||
|
||||
|
|
@ -586,13 +594,14 @@ int display_base_t::init(const ::video::config_t &config, const std::string &dis
|
|||
capture_format = DXGI_FORMAT_UNKNOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool display_base_t::is_hdr() {
|
||||
bool
|
||||
display_base_t::is_hdr() {
|
||||
dxgi::output6_t output6 {};
|
||||
|
||||
auto status = output->QueryInterface(IID_IDXGIOutput6, (void **)&output6);
|
||||
if(FAILED(status)) {
|
||||
auto status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -601,15 +610,16 @@ bool display_base_t::is_hdr() {
|
|||
output6->GetDesc1(&desc1);
|
||||
|
||||
return desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
|
||||
}
|
||||
}
|
||||
|
||||
bool display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
||||
bool
|
||||
display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
||||
dxgi::output6_t output6 {};
|
||||
|
||||
std::memset(&metadata, 0, sizeof(metadata));
|
||||
|
||||
auto status = output->QueryInterface(IID_IDXGIOutput6, (void **)&output6);
|
||||
if(FAILED(status)) {
|
||||
auto status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -654,9 +664,9 @@ bool display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
|||
metadata.maxFullFrameLuminance = desc1.MaxFullFrameLuminance;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const char *format_str[] = {
|
||||
const char *format_str[] = {
|
||||
"DXGI_FORMAT_UNKNOWN",
|
||||
"DXGI_FORMAT_R32G32B32A32_TYPELESS",
|
||||
"DXGI_FORMAT_R32G32B32A32_FLOAT",
|
||||
|
|
@ -779,13 +789,15 @@ const char *format_str[] = {
|
|||
"DXGI_FORMAT_P208",
|
||||
"DXGI_FORMAT_V208",
|
||||
"DXGI_FORMAT_V408"
|
||||
};
|
||||
};
|
||||
|
||||
const char *display_base_t::dxgi_format_to_string(DXGI_FORMAT format) {
|
||||
const char *
|
||||
display_base_t::dxgi_format_to_string(DXGI_FORMAT format) {
|
||||
return format_str[format];
|
||||
}
|
||||
}
|
||||
|
||||
const char *display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) {
|
||||
const char *
|
||||
display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) {
|
||||
const char *type_str[] = {
|
||||
"DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709",
|
||||
"DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709",
|
||||
|
|
@ -814,37 +826,39 @@ const char *display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) {
|
|||
"DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020",
|
||||
};
|
||||
|
||||
if(type < ARRAYSIZE(type_str)) {
|
||||
if (type < ARRAYSIZE(type_str)) {
|
||||
return type_str[type];
|
||||
}
|
||||
else {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace platf::dxgi
|
||||
|
||||
namespace platf {
|
||||
std::shared_ptr<display_t> display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if(hwdevice_type == mem_type_e::dxgi) {
|
||||
std::shared_ptr<display_t>
|
||||
display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if (hwdevice_type == mem_type_e::dxgi) {
|
||||
auto disp = std::make_shared<dxgi::display_vram_t>();
|
||||
|
||||
if(!disp->init(config, display_name)) {
|
||||
if (!disp->init(config, display_name)) {
|
||||
return disp;
|
||||
}
|
||||
}
|
||||
else if(hwdevice_type == mem_type_e::system) {
|
||||
else if (hwdevice_type == mem_type_e::system) {
|
||||
auto disp = std::make_shared<dxgi::display_ram_t>();
|
||||
|
||||
if(!disp->init(config, display_name)) {
|
||||
if (!disp->init(config, display_name)) {
|
||||
return disp;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> display_names(mem_type_e) {
|
||||
std::vector<std::string>
|
||||
display_names(mem_type_e) {
|
||||
std::vector<std::string> display_names;
|
||||
|
||||
HRESULT status;
|
||||
|
|
@ -854,19 +868,19 @@ std::vector<std::string> display_names(mem_type_e) {
|
|||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
|
||||
|
||||
// We must set the GPU preference before calling any DXGI APIs!
|
||||
if(!dxgi::probe_for_gpu_preference(config::video.output_name)) {
|
||||
if (!dxgi::probe_for_gpu_preference(config::video.output_name)) {
|
||||
BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv;
|
||||
}
|
||||
|
||||
dxgi::factory1_t factory;
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory);
|
||||
if(FAILED(status)) {
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return {};
|
||||
}
|
||||
|
||||
dxgi::adapter_t adapter;
|
||||
for(int x = 0; factory->EnumAdapters1(x, &adapter) != DXGI_ERROR_NOT_FOUND; ++x) {
|
||||
for (int x = 0; factory->EnumAdapters1(x, &adapter) != DXGI_ERROR_NOT_FOUND; ++x) {
|
||||
DXGI_ADAPTER_DESC1 adapter_desc;
|
||||
adapter->GetDesc1(&adapter_desc);
|
||||
|
||||
|
|
@ -883,7 +897,7 @@ std::vector<std::string> display_names(mem_type_e) {
|
|||
<< " ====== OUTPUT ======"sv << std::endl;
|
||||
|
||||
dxgi::output_t::pointer output_p {};
|
||||
for(int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
for (int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
dxgi::output_t output { output_p };
|
||||
|
||||
DXGI_OUTPUT_DESC desc;
|
||||
|
|
@ -901,13 +915,13 @@ std::vector<std::string> display_names(mem_type_e) {
|
|||
<< std::endl;
|
||||
|
||||
// Don't include the display in the list if we can't actually capture it
|
||||
if(desc.AttachedToDesktop && dxgi::test_dxgi_duplication(adapter, output)) {
|
||||
if (desc.AttachedToDesktop && dxgi::test_dxgi_duplication(adapter, output)) {
|
||||
display_names.emplace_back(std::move(device_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return display_names;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -2,18 +2,19 @@
|
|||
#include "src/main.h"
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
using namespace std::literals;
|
||||
}
|
||||
|
||||
namespace platf::dxgi {
|
||||
struct img_t : public ::platf::img_t {
|
||||
struct img_t: public ::platf::img_t {
|
||||
~img_t() override {
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
||||
void
|
||||
blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
||||
int height = cursor.shape_info.Height / 2;
|
||||
int width = cursor.shape_info.Width;
|
||||
int pitch = cursor.shape_info.Pitch;
|
||||
|
|
@ -29,7 +30,7 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
|||
auto cursor_width = width - cursor_skip_x - cursor_truncate_x;
|
||||
auto cursor_height = height - cursor_skip_y - cursor_truncate_y;
|
||||
|
||||
if(cursor_height > height || cursor_width > width) {
|
||||
if (cursor_height > height || cursor_width > width) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -44,17 +45,17 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
|||
auto pixels_per_byte = width / pitch;
|
||||
auto bytes_per_row = delta_width / pixels_per_byte;
|
||||
|
||||
auto img_data = (int *)img.data;
|
||||
for(int i = 0; i < delta_height; ++i) {
|
||||
auto img_data = (int *) img.data;
|
||||
for (int i = 0; i < delta_height; ++i) {
|
||||
auto and_mask = &cursor_img_data[i * pitch];
|
||||
auto xor_mask = &cursor_img_data[(i + height) * pitch];
|
||||
|
||||
auto img_pixel_p = &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + img_skip_x];
|
||||
|
||||
auto skip_x = cursor_skip_x;
|
||||
for(int x = 0; x < bytes_per_row; ++x) {
|
||||
for(auto bit = 0u; bit < 8; ++bit) {
|
||||
if(skip_x > 0) {
|
||||
for (int x = 0; x < bytes_per_row; ++x) {
|
||||
for (auto bit = 0u; bit < 8; ++bit) {
|
||||
if (skip_x > 0) {
|
||||
--skip_x;
|
||||
|
||||
continue;
|
||||
|
|
@ -73,15 +74,16 @@ void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) {
|
|||
++xor_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void apply_color_alpha(int *img_pixel_p, int cursor_pixel) {
|
||||
auto colors_out = (std::uint8_t *)&cursor_pixel;
|
||||
auto colors_in = (std::uint8_t *)img_pixel_p;
|
||||
void
|
||||
apply_color_alpha(int *img_pixel_p, int cursor_pixel) {
|
||||
auto colors_out = (std::uint8_t *) &cursor_pixel;
|
||||
auto colors_in = (std::uint8_t *) img_pixel_p;
|
||||
|
||||
//TODO: When use of IDXGIOutput5 is implemented, support different color formats
|
||||
auto alpha = colors_out[3];
|
||||
if(alpha == 255) {
|
||||
if (alpha == 255) {
|
||||
*img_pixel_p = cursor_pixel;
|
||||
}
|
||||
else {
|
||||
|
|
@ -89,20 +91,22 @@ void apply_color_alpha(int *img_pixel_p, int cursor_pixel) {
|
|||
colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255;
|
||||
colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void apply_color_masked(int *img_pixel_p, int cursor_pixel) {
|
||||
void
|
||||
apply_color_masked(int *img_pixel_p, int cursor_pixel) {
|
||||
//TODO: When use of IDXGIOutput5 is implemented, support different color formats
|
||||
auto alpha = ((std::uint8_t *)&cursor_pixel)[3];
|
||||
if(alpha == 0xFF) {
|
||||
auto alpha = ((std::uint8_t *) &cursor_pixel)[3];
|
||||
if (alpha == 0xFF) {
|
||||
*img_pixel_p ^= cursor_pixel;
|
||||
}
|
||||
else {
|
||||
*img_pixel_p = cursor_pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) {
|
||||
void
|
||||
blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) {
|
||||
int height = cursor.shape_info.Height;
|
||||
int width = cursor.shape_info.Width;
|
||||
int pitch = cursor.shape_info.Pitch;
|
||||
|
|
@ -121,24 +125,24 @@ void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) {
|
|||
auto cursor_width = width - cursor_skip_x - cursor_truncate_x;
|
||||
auto cursor_height = height - cursor_skip_y - cursor_truncate_y;
|
||||
|
||||
if(cursor_height > height || cursor_width > width) {
|
||||
if (cursor_height > height || cursor_width > width) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto cursor_img_data = (int *)&cursor.img_data[cursor_skip_y * pitch];
|
||||
auto cursor_img_data = (int *) &cursor.img_data[cursor_skip_y * pitch];
|
||||
|
||||
int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y));
|
||||
int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x));
|
||||
|
||||
auto img_data = (int *)img.data;
|
||||
auto img_data = (int *) img.data;
|
||||
|
||||
for(int i = 0; i < delta_height; ++i) {
|
||||
for (int i = 0; i < delta_height; ++i) {
|
||||
auto cursor_begin = &cursor_img_data[i * cursor.shape_info.Width + cursor_skip_x];
|
||||
auto cursor_end = &cursor_begin[delta_width];
|
||||
|
||||
auto img_pixel_p = &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + img_skip_x];
|
||||
std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) {
|
||||
if(masked) {
|
||||
if (masked) {
|
||||
apply_color_masked(img_pixel_p, cursor_pixel);
|
||||
}
|
||||
else {
|
||||
|
|
@ -147,10 +151,11 @@ void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) {
|
|||
++img_pixel_p;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blend_cursor(const cursor_t &cursor, img_t &img) {
|
||||
switch(cursor.shape_info.Type) {
|
||||
void
|
||||
blend_cursor(const cursor_t &cursor, img_t &img) {
|
||||
switch (cursor.shape_info.Type) {
|
||||
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
|
||||
blend_cursor_color(cursor, img, false);
|
||||
break;
|
||||
|
|
@ -163,10 +168,11 @@ void blend_cursor(const cursor_t &cursor, img_t &img) {
|
|||
default:
|
||||
BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) {
|
||||
auto img = (img_t *)img_base;
|
||||
capture_e
|
||||
display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) {
|
||||
auto img = (img_t *) img_base;
|
||||
|
||||
HRESULT status;
|
||||
|
||||
|
|
@ -176,7 +182,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
|||
auto capture_status = dup.next_frame(frame_info, timeout, &res_p);
|
||||
resource_t res { res_p };
|
||||
|
||||
if(capture_status != capture_e::ok) {
|
||||
if (capture_status != capture_e::ok) {
|
||||
return capture_status;
|
||||
}
|
||||
|
||||
|
|
@ -184,36 +190,36 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
|||
const bool frame_update_flag = frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0;
|
||||
const bool update_flag = mouse_update_flag || frame_update_flag;
|
||||
|
||||
if(!update_flag) {
|
||||
if (!update_flag) {
|
||||
return capture_e::timeout;
|
||||
}
|
||||
|
||||
if(frame_info.PointerShapeBufferSize > 0) {
|
||||
if (frame_info.PointerShapeBufferSize > 0) {
|
||||
auto &img_data = cursor.img_data;
|
||||
|
||||
img_data.resize(frame_info.PointerShapeBufferSize);
|
||||
|
||||
UINT dummy;
|
||||
status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
|
||||
if(frame_info.LastMouseUpdateTime.QuadPart) {
|
||||
if (frame_info.LastMouseUpdateTime.QuadPart) {
|
||||
cursor.x = frame_info.PointerPosition.Position.x;
|
||||
cursor.y = frame_info.PointerPosition.Position.y;
|
||||
cursor.visible = frame_info.PointerPosition.Visible;
|
||||
}
|
||||
|
||||
if(frame_update_flag) {
|
||||
if (frame_update_flag) {
|
||||
{
|
||||
texture2d_t src {};
|
||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src);
|
||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **) &src);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
|
|
@ -222,7 +228,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
|||
src->GetDesc(&desc);
|
||||
|
||||
// If we don't know the capture format yet, grab it from this texture and create the staging texture
|
||||
if(capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
if (capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
capture_format = desc.Format;
|
||||
BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
|
||||
|
|
@ -238,7 +244,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
|||
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &texture);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
|
|
@ -246,14 +252,14 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
|||
|
||||
// It's possible for our display enumeration to race with mode changes and result in
|
||||
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
|
||||
if(desc.Width != width || desc.Height != height) {
|
||||
if (desc.Width != width || desc.Height != height) {
|
||||
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
// It's also possible for the capture format to change on the fly. If that happens,
|
||||
// reinitialize capture to try format detection again and create new images.
|
||||
if(capture_format != desc.Format) {
|
||||
if (capture_format != desc.Format) {
|
||||
BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
|
@ -264,44 +270,45 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
|||
}
|
||||
|
||||
// If we don't know the final capture format yet, encode a dummy image
|
||||
if(capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
if (capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv;
|
||||
|
||||
if(dummy_img(img)) {
|
||||
if (dummy_img(img)) {
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Map the staging texture for CPU access (making it inaccessible for the GPU)
|
||||
status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
// Now that we know the capture format, we can finish creating the image
|
||||
if(complete_img(img, false)) {
|
||||
if (complete_img(img, false)) {
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
std::copy_n((std::uint8_t *)img_info.pData, height * img_info.RowPitch, (std::uint8_t *)img->data);
|
||||
std::copy_n((std::uint8_t *) img_info.pData, height * img_info.RowPitch, (std::uint8_t *) img->data);
|
||||
|
||||
// Unmap the staging texture to allow GPU access again
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
}
|
||||
|
||||
if(cursor_visible && cursor.visible) {
|
||||
if (cursor_visible && cursor.visible) {
|
||||
blend_cursor(cursor, *img);
|
||||
}
|
||||
|
||||
return capture_e::ok;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::img_t> display_ram_t::alloc_img() {
|
||||
std::shared_ptr<platf::img_t>
|
||||
display_ram_t::alloc_img() {
|
||||
auto img = std::make_shared<img_t>();
|
||||
|
||||
// Initialize fields that are format-independent
|
||||
|
|
@ -309,59 +316,64 @@ std::shared_ptr<platf::img_t> display_ram_t::alloc_img() {
|
|||
img->height = height;
|
||||
|
||||
return img;
|
||||
}
|
||||
}
|
||||
|
||||
int display_ram_t::complete_img(platf::img_t *img, bool dummy) {
|
||||
int
|
||||
display_ram_t::complete_img(platf::img_t *img, bool dummy) {
|
||||
// If this is not a dummy image, we must know the format by now
|
||||
if(!dummy && capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
if (!dummy && capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(error) << "display_ram_t::complete_img() called with unknown capture format!";
|
||||
return -1;
|
||||
}
|
||||
|
||||
img->pixel_pitch = get_pixel_pitch();
|
||||
|
||||
if(dummy && !img->row_pitch) {
|
||||
if (dummy && !img->row_pitch) {
|
||||
// Assume our dummy image will have no padding
|
||||
img->row_pitch = img->pixel_pitch * img->width;
|
||||
}
|
||||
|
||||
// Reallocate the image buffer if the pitch changes
|
||||
if(!dummy && img->row_pitch != img_info.RowPitch) {
|
||||
if (!dummy && img->row_pitch != img_info.RowPitch) {
|
||||
img->row_pitch = img_info.RowPitch;
|
||||
delete img->data;
|
||||
img->data = nullptr;
|
||||
}
|
||||
|
||||
if(!img->data) {
|
||||
if (!img->data) {
|
||||
img->data = new std::uint8_t[img->row_pitch * height];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int display_ram_t::dummy_img(platf::img_t *img) {
|
||||
if(complete_img(img, true)) {
|
||||
int
|
||||
display_ram_t::dummy_img(platf::img_t *img) {
|
||||
if (complete_img(img, true)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::fill_n((std::uint8_t *)img->data, height * img->row_pitch, 0);
|
||||
std::fill_n((std::uint8_t *) img->data, height * img->row_pitch, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DXGI_FORMAT> display_ram_t::get_supported_sdr_capture_formats() {
|
||||
std::vector<DXGI_FORMAT>
|
||||
display_ram_t::get_supported_sdr_capture_formats() {
|
||||
return { DXGI_FORMAT_B8G8R8A8_UNORM };
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DXGI_FORMAT> display_ram_t::get_supported_hdr_capture_formats() {
|
||||
std::vector<DXGI_FORMAT>
|
||||
display_ram_t::get_supported_hdr_capture_formats() {
|
||||
// HDR is unsupported
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int display_ram_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
if(display_base_t::init(config, display_name)) {
|
||||
int
|
||||
display_ram_t::init(const ::video::config_t &config, const std::string &display_name) {
|
||||
if (display_base_t::init(config, display_name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace platf::dxgi
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -10,49 +10,53 @@
|
|||
#include "src/platform/common.h"
|
||||
|
||||
namespace platf {
|
||||
using namespace std::literals;
|
||||
using namespace std::literals;
|
||||
|
||||
thread_local HDESK _lastKnownInputDesktop = nullptr;
|
||||
thread_local HDESK _lastKnownInputDesktop = nullptr;
|
||||
|
||||
constexpr touch_port_t target_touch_port {
|
||||
constexpr touch_port_t target_touch_port {
|
||||
0, 0,
|
||||
65535, 65535
|
||||
};
|
||||
};
|
||||
|
||||
using client_t = util::safe_ptr<_VIGEM_CLIENT_T, vigem_free>;
|
||||
using target_t = util::safe_ptr<_VIGEM_TARGET_T, vigem_target_free>;
|
||||
using client_t = util::safe_ptr<_VIGEM_CLIENT_T, vigem_free>;
|
||||
using target_t = util::safe_ptr<_VIGEM_TARGET_T, vigem_target_free>;
|
||||
|
||||
static VIGEM_TARGET_TYPE map(const std::string_view &gp) {
|
||||
if(gp == "x360"sv) {
|
||||
static VIGEM_TARGET_TYPE
|
||||
map(const std::string_view &gp) {
|
||||
if (gp == "x360"sv) {
|
||||
return Xbox360Wired;
|
||||
}
|
||||
|
||||
return DualShock4Wired;
|
||||
}
|
||||
}
|
||||
|
||||
void CALLBACK x360_notify(
|
||||
void CALLBACK
|
||||
x360_notify(
|
||||
client_t::pointer client,
|
||||
target_t::pointer target,
|
||||
std::uint8_t largeMotor, std::uint8_t smallMotor,
|
||||
std::uint8_t /* led_number */,
|
||||
void *userdata);
|
||||
|
||||
void CALLBACK ds4_notify(
|
||||
void CALLBACK
|
||||
ds4_notify(
|
||||
client_t::pointer client,
|
||||
target_t::pointer target,
|
||||
std::uint8_t largeMotor, std::uint8_t smallMotor,
|
||||
DS4_LIGHTBAR_COLOR /* led_color */,
|
||||
void *userdata);
|
||||
|
||||
class vigem_t {
|
||||
public:
|
||||
int init() {
|
||||
class vigem_t {
|
||||
public:
|
||||
int
|
||||
init() {
|
||||
VIGEM_ERROR status;
|
||||
|
||||
client.reset(vigem_alloc());
|
||||
|
||||
status = vigem_connect(client.get());
|
||||
if(!VIGEM_SUCCESS(status)) {
|
||||
if (!VIGEM_SUCCESS(status)) {
|
||||
BOOST_LOG(warning) << "Couldn't setup connection to ViGEm for gamepad support ["sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
|
|
@ -63,11 +67,12 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int alloc_gamepad_interal(int nr, rumble_queue_t &rumble_queue, VIGEM_TARGET_TYPE gp_type) {
|
||||
int
|
||||
alloc_gamepad_interal(int nr, rumble_queue_t &rumble_queue, VIGEM_TARGET_TYPE gp_type) {
|
||||
auto &[rumble, gp] = gamepads[nr];
|
||||
assert(!gp);
|
||||
|
||||
if(gp_type == Xbox360Wired) {
|
||||
if (gp_type == Xbox360Wired) {
|
||||
gp.reset(vigem_target_x360_alloc());
|
||||
}
|
||||
else {
|
||||
|
|
@ -75,7 +80,7 @@ public:
|
|||
}
|
||||
|
||||
auto status = vigem_target_add(client.get(), gp.get());
|
||||
if(!VIGEM_SUCCESS(status)) {
|
||||
if (!VIGEM_SUCCESS(status)) {
|
||||
BOOST_LOG(error) << "Couldn't add Gamepad to ViGEm connection ["sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
|
|
@ -83,26 +88,27 @@ public:
|
|||
|
||||
rumble = std::move(rumble_queue);
|
||||
|
||||
if(gp_type == Xbox360Wired) {
|
||||
if (gp_type == Xbox360Wired) {
|
||||
status = vigem_target_x360_register_notification(client.get(), gp.get(), x360_notify, this);
|
||||
}
|
||||
else {
|
||||
status = vigem_target_ds4_register_notification(client.get(), gp.get(), ds4_notify, this);
|
||||
}
|
||||
|
||||
if(!VIGEM_SUCCESS(status)) {
|
||||
if (!VIGEM_SUCCESS(status)) {
|
||||
BOOST_LOG(warning) << "Couldn't register notifications for rumble support ["sv << util::hex(status).to_string_view() << ']';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_target(int nr) {
|
||||
void
|
||||
free_target(int nr) {
|
||||
auto &[_, gp] = gamepads[nr];
|
||||
|
||||
if(gp && vigem_target_is_attached(gp.get())) {
|
||||
if (gp && vigem_target_is_attached(gp.get())) {
|
||||
auto status = vigem_target_remove(client.get(), gp.get());
|
||||
if(!VIGEM_SUCCESS(status)) {
|
||||
if (!VIGEM_SUCCESS(status)) {
|
||||
BOOST_LOG(warning) << "Couldn't detach gamepad from ViGEm ["sv << util::hex(status).to_string_view() << ']';
|
||||
}
|
||||
}
|
||||
|
|
@ -110,12 +116,13 @@ public:
|
|||
gp.reset();
|
||||
}
|
||||
|
||||
void rumble(target_t::pointer target, std::uint8_t smallMotor, std::uint8_t largeMotor) {
|
||||
for(int x = 0; x < gamepads.size(); ++x) {
|
||||
void
|
||||
rumble(target_t::pointer target, std::uint8_t smallMotor, std::uint8_t largeMotor) {
|
||||
for (int x = 0; x < gamepads.size(); ++x) {
|
||||
auto &[rumble_queue, gp] = gamepads[x];
|
||||
|
||||
if(gp.get() == target) {
|
||||
rumble_queue->raise(x, ((std::uint16_t)smallMotor) << 8, ((std::uint16_t)largeMotor) << 8);
|
||||
if (gp.get() == target) {
|
||||
rumble_queue->raise(x, ((std::uint16_t) smallMotor) << 8, ((std::uint16_t) largeMotor) << 8);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -123,11 +130,11 @@ public:
|
|||
}
|
||||
|
||||
~vigem_t() {
|
||||
if(client) {
|
||||
for(auto &[_, gp] : gamepads) {
|
||||
if(gp && vigem_target_is_attached(gp.get())) {
|
||||
if (client) {
|
||||
for (auto &[_, gp] : gamepads) {
|
||||
if (gp && vigem_target_is_attached(gp.get())) {
|
||||
auto status = vigem_target_remove(client.get(), gp.get());
|
||||
if(!VIGEM_SUCCESS(status)) {
|
||||
if (!VIGEM_SUCCESS(status)) {
|
||||
BOOST_LOG(warning) << "Couldn't detach gamepad from ViGEm ["sv << util::hex(status).to_string_view() << ']';
|
||||
}
|
||||
}
|
||||
|
|
@ -140,37 +147,37 @@ public:
|
|||
std::vector<std::pair<rumble_queue_t, target_t>> gamepads;
|
||||
|
||||
client_t client;
|
||||
};
|
||||
};
|
||||
|
||||
void CALLBACK x360_notify(
|
||||
void CALLBACK
|
||||
x360_notify(
|
||||
client_t::pointer client,
|
||||
target_t::pointer target,
|
||||
std::uint8_t largeMotor, std::uint8_t smallMotor,
|
||||
std::uint8_t /* led_number */,
|
||||
void *userdata) {
|
||||
|
||||
BOOST_LOG(debug)
|
||||
<< "largeMotor: "sv << (int)largeMotor << std::endl
|
||||
<< "smallMotor: "sv << (int)smallMotor;
|
||||
<< "largeMotor: "sv << (int) largeMotor << std::endl
|
||||
<< "smallMotor: "sv << (int) smallMotor;
|
||||
|
||||
task_pool.push(&vigem_t::rumble, (vigem_t *)userdata, target, smallMotor, largeMotor);
|
||||
}
|
||||
task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, smallMotor, largeMotor);
|
||||
}
|
||||
|
||||
void CALLBACK ds4_notify(
|
||||
void CALLBACK
|
||||
ds4_notify(
|
||||
client_t::pointer client,
|
||||
target_t::pointer target,
|
||||
std::uint8_t largeMotor, std::uint8_t smallMotor,
|
||||
DS4_LIGHTBAR_COLOR /* led_color */,
|
||||
void *userdata) {
|
||||
|
||||
BOOST_LOG(debug)
|
||||
<< "largeMotor: "sv << (int)largeMotor << std::endl
|
||||
<< "smallMotor: "sv << (int)smallMotor;
|
||||
<< "largeMotor: "sv << (int) largeMotor << std::endl
|
||||
<< "smallMotor: "sv << (int) smallMotor;
|
||||
|
||||
task_pool.push(&vigem_t::rumble, (vigem_t *)userdata, target, smallMotor, largeMotor);
|
||||
}
|
||||
task_pool.push(&vigem_t::rumble, (vigem_t *) userdata, target, smallMotor, largeMotor);
|
||||
}
|
||||
|
||||
struct input_raw_t {
|
||||
struct input_raw_t {
|
||||
~input_raw_t() {
|
||||
delete vigem;
|
||||
}
|
||||
|
|
@ -178,14 +185,15 @@ struct input_raw_t {
|
|||
vigem_t *vigem;
|
||||
HKL keyboard_layout;
|
||||
HKL active_layout;
|
||||
};
|
||||
};
|
||||
|
||||
input_t input() {
|
||||
input_t
|
||||
input() {
|
||||
input_t result { new input_raw_t {} };
|
||||
auto &raw = *(input_raw_t *)result.get();
|
||||
auto &raw = *(input_raw_t *) result.get();
|
||||
|
||||
raw.vigem = new vigem_t {};
|
||||
if(raw.vigem->init()) {
|
||||
if (raw.vigem->init()) {
|
||||
delete raw.vigem;
|
||||
raw.vigem = nullptr;
|
||||
}
|
||||
|
|
@ -193,35 +201,37 @@ input_t input() {
|
|||
// Moonlight currently sends keys normalized to the US English layout.
|
||||
// We need to use that layout when converting to scancodes.
|
||||
raw.keyboard_layout = LoadKeyboardLayoutA("00000409", 0);
|
||||
if(!raw.keyboard_layout || LOWORD(raw.keyboard_layout) != 0x409) {
|
||||
if (!raw.keyboard_layout || LOWORD(raw.keyboard_layout) != 0x409) {
|
||||
BOOST_LOG(warning) << "Unable to load US English keyboard layout for scancode translation. Keyboard input may not work in games."sv;
|
||||
raw.keyboard_layout = NULL;
|
||||
}
|
||||
|
||||
// Activate layout for current process only
|
||||
raw.active_layout = ActivateKeyboardLayout(raw.keyboard_layout, KLF_SETFORPROCESS);
|
||||
if(!raw.active_layout) {
|
||||
if (!raw.active_layout) {
|
||||
BOOST_LOG(warning) << "Unable to activate US English keyboard layout for scancode translation. Keyboard input may not work in games."sv;
|
||||
raw.keyboard_layout = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void send_input(INPUT &i) {
|
||||
retry:
|
||||
void
|
||||
send_input(INPUT &i) {
|
||||
retry:
|
||||
auto send = SendInput(1, &i, sizeof(INPUT));
|
||||
if(send != 1) {
|
||||
if (send != 1) {
|
||||
auto hDesk = syncThreadDesktop();
|
||||
if(_lastKnownInputDesktop != hDesk) {
|
||||
if (_lastKnownInputDesktop != hDesk) {
|
||||
_lastKnownInputDesktop = hDesk;
|
||||
goto retry;
|
||||
}
|
||||
BOOST_LOG(error) << "Couldn't send input"sv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {
|
||||
void
|
||||
abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y) {
|
||||
INPUT i {};
|
||||
|
||||
i.type = INPUT_MOUSE;
|
||||
|
|
@ -234,16 +244,17 @@ void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y)
|
|||
// MOUSEEVENTF_VIRTUALDESK maps to the entirety of the desktop rather than the primary desktop
|
||||
MOUSEEVENTF_VIRTUALDESK;
|
||||
|
||||
auto scaled_x = std::lround((x + touch_port.offset_x) * ((float)target_touch_port.width / (float)touch_port.width));
|
||||
auto scaled_y = std::lround((y + touch_port.offset_y) * ((float)target_touch_port.height / (float)touch_port.height));
|
||||
auto scaled_x = std::lround((x + touch_port.offset_x) * ((float) target_touch_port.width / (float) touch_port.width));
|
||||
auto scaled_y = std::lround((y + touch_port.offset_y) * ((float) target_touch_port.height / (float) touch_port.height));
|
||||
|
||||
mi.dx = scaled_x;
|
||||
mi.dy = scaled_y;
|
||||
|
||||
send_input(i);
|
||||
}
|
||||
}
|
||||
|
||||
void move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
void
|
||||
move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
INPUT i {};
|
||||
|
||||
i.type = INPUT_MOUSE;
|
||||
|
|
@ -254,10 +265,11 @@ void move_mouse(input_t &input, int deltaX, int deltaY) {
|
|||
mi.dy = deltaY;
|
||||
|
||||
send_input(i);
|
||||
}
|
||||
}
|
||||
|
||||
void button_mouse(input_t &input, int button, bool release) {
|
||||
constexpr auto KEY_STATE_DOWN = (SHORT)0x8000;
|
||||
void
|
||||
button_mouse(input_t &input, int button, bool release) {
|
||||
constexpr auto KEY_STATE_DOWN = (SHORT) 0x8000;
|
||||
|
||||
INPUT i {};
|
||||
|
||||
|
|
@ -265,19 +277,19 @@ void button_mouse(input_t &input, int button, bool release) {
|
|||
auto &mi = i.mi;
|
||||
|
||||
int mouse_button;
|
||||
if(button == 1) {
|
||||
if (button == 1) {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN;
|
||||
mouse_button = VK_LBUTTON;
|
||||
}
|
||||
else if(button == 2) {
|
||||
else if (button == 2) {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN;
|
||||
mouse_button = VK_MBUTTON;
|
||||
}
|
||||
else if(button == 3) {
|
||||
else if (button == 3) {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN;
|
||||
mouse_button = VK_RBUTTON;
|
||||
}
|
||||
else if(button == 4) {
|
||||
else if (button == 4) {
|
||||
mi.dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN;
|
||||
mi.mouseData = XBUTTON1;
|
||||
mouse_button = VK_XBUTTON1;
|
||||
|
|
@ -290,16 +302,17 @@ void button_mouse(input_t &input, int button, bool release) {
|
|||
|
||||
auto key_state = GetAsyncKeyState(mouse_button);
|
||||
bool key_state_down = (key_state & KEY_STATE_DOWN) != 0;
|
||||
if(key_state_down != release) {
|
||||
if (key_state_down != release) {
|
||||
BOOST_LOG(warning) << "Button state of mouse_button ["sv << button << "] does not match the desired state"sv;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
send_input(i);
|
||||
}
|
||||
}
|
||||
|
||||
void scroll(input_t &input, int distance) {
|
||||
void
|
||||
scroll(input_t &input, int distance) {
|
||||
INPUT i {};
|
||||
|
||||
i.type = INPUT_MOUSE;
|
||||
|
|
@ -309,9 +322,10 @@ void scroll(input_t &input, int distance) {
|
|||
mi.mouseData = distance;
|
||||
|
||||
send_input(i);
|
||||
}
|
||||
}
|
||||
|
||||
void hscroll(input_t &input, int distance) {
|
||||
void
|
||||
hscroll(input_t &input, int distance) {
|
||||
INPUT i {};
|
||||
|
||||
i.type = INPUT_MOUSE;
|
||||
|
|
@ -321,22 +335,23 @@ void hscroll(input_t &input, int distance) {
|
|||
mi.mouseData = distance;
|
||||
|
||||
send_input(i);
|
||||
}
|
||||
}
|
||||
|
||||
void keyboard(input_t &input, uint16_t modcode, bool release) {
|
||||
auto raw = (input_raw_t *)input.get();
|
||||
void
|
||||
keyboard(input_t &input, uint16_t modcode, bool release) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
|
||||
INPUT i {};
|
||||
i.type = INPUT_KEYBOARD;
|
||||
auto &ki = i.ki;
|
||||
|
||||
// For some reason, MapVirtualKey(VK_LWIN, MAPVK_VK_TO_VSC) doesn't seem to work :/
|
||||
if(modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE && raw->keyboard_layout != NULL) {
|
||||
if (modcode != VK_LWIN && modcode != VK_RWIN && modcode != VK_PAUSE && raw->keyboard_layout != NULL) {
|
||||
ki.wScan = MapVirtualKeyEx(modcode, MAPVK_VK_TO_VSC, raw->keyboard_layout);
|
||||
}
|
||||
|
||||
// If we can map this to a scancode, send it as a scancode for maximum game compatibility.
|
||||
if(ki.wScan) {
|
||||
if (ki.wScan) {
|
||||
ki.dwFlags = KEYEVENTF_SCANCODE;
|
||||
}
|
||||
else {
|
||||
|
|
@ -345,7 +360,7 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
|
|||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keystroke-message-flags
|
||||
switch(modcode) {
|
||||
switch (modcode) {
|
||||
case VK_RMENU:
|
||||
case VK_RCONTROL:
|
||||
case VK_INSERT:
|
||||
|
|
@ -365,24 +380,25 @@ void keyboard(input_t &input, uint16_t modcode, bool release) {
|
|||
break;
|
||||
}
|
||||
|
||||
if(release) {
|
||||
if (release) {
|
||||
ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||
}
|
||||
|
||||
send_input(i);
|
||||
}
|
||||
}
|
||||
|
||||
void unicode(input_t &input, char *utf8, int size) {
|
||||
void
|
||||
unicode(input_t &input, char *utf8, int size) {
|
||||
// We can do no worse than one UTF-16 character per byte of UTF-8
|
||||
WCHAR wide[size];
|
||||
|
||||
int chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8, size, wide, size);
|
||||
if(chars <= 0) {
|
||||
if (chars <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send all key down events
|
||||
for(int i = 0; i < chars; i++) {
|
||||
for (int i = 0; i < chars; i++) {
|
||||
INPUT input {};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wScan = wide[i];
|
||||
|
|
@ -391,48 +407,52 @@ void unicode(input_t &input, char *utf8, int size) {
|
|||
}
|
||||
|
||||
// Send all key up events
|
||||
for(int i = 0; i < chars; i++) {
|
||||
for (int i = 0; i < chars; i++) {
|
||||
INPUT input {};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wScan = wide[i];
|
||||
input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
|
||||
send_input(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) {
|
||||
auto raw = (input_raw_t *)input.get();
|
||||
int
|
||||
alloc_gamepad(input_t &input, int nr, rumble_queue_t rumble_queue) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
|
||||
if(!raw->vigem) {
|
||||
if (!raw->vigem) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return raw->vigem->alloc_gamepad_interal(nr, rumble_queue, map(config::input.gamepad));
|
||||
}
|
||||
}
|
||||
|
||||
void free_gamepad(input_t &input, int nr) {
|
||||
auto raw = (input_raw_t *)input.get();
|
||||
void
|
||||
free_gamepad(input_t &input, int nr) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
|
||||
if(!raw->vigem) {
|
||||
if (!raw->vigem) {
|
||||
return;
|
||||
}
|
||||
|
||||
raw->vigem->free_target(nr);
|
||||
}
|
||||
}
|
||||
|
||||
static VIGEM_ERROR x360_update(client_t::pointer client, target_t::pointer gp, const gamepad_state_t &gamepad_state) {
|
||||
auto &xusb = *(PXUSB_REPORT)&gamepad_state;
|
||||
static VIGEM_ERROR
|
||||
x360_update(client_t::pointer client, target_t::pointer gp, const gamepad_state_t &gamepad_state) {
|
||||
auto &xusb = *(PXUSB_REPORT) &gamepad_state;
|
||||
|
||||
return vigem_target_x360_update(client, gp, xusb);
|
||||
}
|
||||
}
|
||||
|
||||
static DS4_DPAD_DIRECTIONS ds4_dpad(const gamepad_state_t &gamepad_state) {
|
||||
static DS4_DPAD_DIRECTIONS
|
||||
ds4_dpad(const gamepad_state_t &gamepad_state) {
|
||||
auto flags = gamepad_state.buttonFlags;
|
||||
if(flags & DPAD_UP) {
|
||||
if(flags & DPAD_RIGHT) {
|
||||
if (flags & DPAD_UP) {
|
||||
if (flags & DPAD_RIGHT) {
|
||||
return DS4_BUTTON_DPAD_NORTHEAST;
|
||||
}
|
||||
else if(flags & DPAD_LEFT) {
|
||||
else if (flags & DPAD_LEFT) {
|
||||
return DS4_BUTTON_DPAD_NORTHWEST;
|
||||
}
|
||||
else {
|
||||
|
|
@ -440,11 +460,11 @@ static DS4_DPAD_DIRECTIONS ds4_dpad(const gamepad_state_t &gamepad_state) {
|
|||
}
|
||||
}
|
||||
|
||||
else if(flags & DPAD_DOWN) {
|
||||
if(flags & DPAD_RIGHT) {
|
||||
else if (flags & DPAD_DOWN) {
|
||||
if (flags & DPAD_RIGHT) {
|
||||
return DS4_BUTTON_DPAD_SOUTHEAST;
|
||||
}
|
||||
else if(flags & DPAD_LEFT) {
|
||||
else if (flags & DPAD_LEFT) {
|
||||
return DS4_BUTTON_DPAD_SOUTHWEST;
|
||||
}
|
||||
else {
|
||||
|
|
@ -452,18 +472,19 @@ static DS4_DPAD_DIRECTIONS ds4_dpad(const gamepad_state_t &gamepad_state) {
|
|||
}
|
||||
}
|
||||
|
||||
else if(flags & DPAD_RIGHT) {
|
||||
else if (flags & DPAD_RIGHT) {
|
||||
return DS4_BUTTON_DPAD_EAST;
|
||||
}
|
||||
|
||||
else if(flags & DPAD_LEFT) {
|
||||
else if (flags & DPAD_LEFT) {
|
||||
return DS4_BUTTON_DPAD_WEST;
|
||||
}
|
||||
|
||||
return DS4_BUTTON_DPAD_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static DS4_BUTTONS ds4_buttons(const gamepad_state_t &gamepad_state) {
|
||||
static DS4_BUTTONS
|
||||
ds4_buttons(const gamepad_state_t &gamepad_state) {
|
||||
int buttons {};
|
||||
|
||||
auto flags = gamepad_state.buttonFlags;
|
||||
|
|
@ -482,29 +503,33 @@ static DS4_BUTTONS ds4_buttons(const gamepad_state_t &gamepad_state) {
|
|||
if(gamepad_state.rt > 0) buttons |= DS4_BUTTON_TRIGGER_RIGHT;
|
||||
// clang-format on
|
||||
|
||||
return (DS4_BUTTONS)buttons;
|
||||
}
|
||||
return (DS4_BUTTONS) buttons;
|
||||
}
|
||||
|
||||
static DS4_SPECIAL_BUTTONS ds4_special_buttons(const gamepad_state_t &gamepad_state) {
|
||||
static DS4_SPECIAL_BUTTONS
|
||||
ds4_special_buttons(const gamepad_state_t &gamepad_state) {
|
||||
int buttons {};
|
||||
|
||||
if(gamepad_state.buttonFlags & BACK) buttons |= DS4_SPECIAL_BUTTON_TOUCHPAD;
|
||||
if(gamepad_state.buttonFlags & HOME) buttons |= DS4_SPECIAL_BUTTON_PS;
|
||||
if (gamepad_state.buttonFlags & BACK) buttons |= DS4_SPECIAL_BUTTON_TOUCHPAD;
|
||||
if (gamepad_state.buttonFlags & HOME) buttons |= DS4_SPECIAL_BUTTON_PS;
|
||||
|
||||
return (DS4_SPECIAL_BUTTONS)buttons;
|
||||
}
|
||||
return (DS4_SPECIAL_BUTTONS) buttons;
|
||||
}
|
||||
|
||||
static std::uint8_t to_ds4_triggerX(std::int16_t v) {
|
||||
static std::uint8_t
|
||||
to_ds4_triggerX(std::int16_t v) {
|
||||
return (v + std::numeric_limits<std::uint16_t>::max() / 2 + 1) / 257;
|
||||
}
|
||||
}
|
||||
|
||||
static std::uint8_t to_ds4_triggerY(std::int16_t v) {
|
||||
static std::uint8_t
|
||||
to_ds4_triggerY(std::int16_t v) {
|
||||
auto new_v = -((std::numeric_limits<std::uint16_t>::max() / 2 + v - 1)) / 257;
|
||||
|
||||
return new_v == 0 ? 0xFF : (std::uint8_t)new_v;
|
||||
}
|
||||
return new_v == 0 ? 0xFF : (std::uint8_t) new_v;
|
||||
}
|
||||
|
||||
static VIGEM_ERROR ds4_update(client_t::pointer client, target_t::pointer gp, const gamepad_state_t &gamepad_state) {
|
||||
static VIGEM_ERROR
|
||||
ds4_update(client_t::pointer client, target_t::pointer gp, const gamepad_state_t &gamepad_state) {
|
||||
DS4_REPORT report;
|
||||
|
||||
DS4_REPORT_INIT(&report);
|
||||
|
|
@ -522,14 +547,14 @@ static VIGEM_ERROR ds4_update(client_t::pointer client, target_t::pointer gp, co
|
|||
report.bThumbRY = to_ds4_triggerY(gamepad_state.rsY);
|
||||
|
||||
return vigem_target_ds4_update(client, gp, report);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
auto vigem = ((input_raw_t *)input.get())->vigem;
|
||||
void
|
||||
gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
auto vigem = ((input_raw_t *) input.get())->vigem;
|
||||
|
||||
// If there is no gamepad support
|
||||
if(!vigem) {
|
||||
if (!vigem) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -537,30 +562,32 @@ void gamepad(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
|||
|
||||
VIGEM_ERROR status;
|
||||
|
||||
if(vigem_target_get_type(gp.get()) == Xbox360Wired) {
|
||||
if (vigem_target_get_type(gp.get()) == Xbox360Wired) {
|
||||
status = x360_update(vigem->client.get(), gp.get(), gamepad_state);
|
||||
}
|
||||
else {
|
||||
status = ds4_update(vigem->client.get(), gp.get(), gamepad_state);
|
||||
}
|
||||
|
||||
if(!VIGEM_SUCCESS(status)) {
|
||||
if (!VIGEM_SUCCESS(status)) {
|
||||
BOOST_LOG(warning) << "Couldn't send gamepad input to ViGEm ["sv << util::hex(status).to_string_view() << ']';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void freeInput(void *p) {
|
||||
auto input = (input_raw_t *)p;
|
||||
void
|
||||
freeInput(void *p) {
|
||||
auto input = (input_raw_t *) p;
|
||||
|
||||
delete input;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string_view> &supported_gamepads() {
|
||||
std::vector<std::string_view> &
|
||||
supported_gamepads() {
|
||||
// ds4 == ps4
|
||||
static std::vector<std::string_view> gps {
|
||||
"x360"sv, "ds4"sv, "ps4"sv
|
||||
};
|
||||
|
||||
return gps;
|
||||
}
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
// UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK
|
||||
#ifndef UDP_SEND_MSG_SIZE
|
||||
#define UDP_SEND_MSG_SIZE 2
|
||||
#define UDP_SEND_MSG_SIZE 2
|
||||
#endif
|
||||
|
||||
// MinGW headers are missing qWAVE stuff
|
||||
|
|
@ -37,94 +37,99 @@ typedef UINT32 QOS_FLOWID, *PQOS_FLOWID;
|
|||
#include <qos2.h>
|
||||
|
||||
#ifndef WLAN_API_MAKE_VERSION
|
||||
#define WLAN_API_MAKE_VERSION(_major, _minor) (((DWORD)(_minor)) << 16 | (_major))
|
||||
#define WLAN_API_MAKE_VERSION(_major, _minor) (((DWORD) (_minor)) << 16 | (_major))
|
||||
#endif
|
||||
|
||||
namespace bp = boost::process;
|
||||
|
||||
using namespace std::literals;
|
||||
namespace platf {
|
||||
using adapteraddrs_t = util::c_ptr<IP_ADAPTER_ADDRESSES>;
|
||||
using adapteraddrs_t = util::c_ptr<IP_ADAPTER_ADDRESSES>;
|
||||
|
||||
bool enabled_mouse_keys = false;
|
||||
MOUSEKEYS previous_mouse_keys_state;
|
||||
bool enabled_mouse_keys = false;
|
||||
MOUSEKEYS previous_mouse_keys_state;
|
||||
|
||||
HANDLE qos_handle = nullptr;
|
||||
HANDLE qos_handle = nullptr;
|
||||
|
||||
decltype(QOSCreateHandle) *fn_QOSCreateHandle = nullptr;
|
||||
decltype(QOSAddSocketToFlow) *fn_QOSAddSocketToFlow = nullptr;
|
||||
decltype(QOSRemoveSocketFromFlow) *fn_QOSRemoveSocketFromFlow = nullptr;
|
||||
decltype(QOSCreateHandle) *fn_QOSCreateHandle = nullptr;
|
||||
decltype(QOSAddSocketToFlow) *fn_QOSAddSocketToFlow = nullptr;
|
||||
decltype(QOSRemoveSocketFromFlow) *fn_QOSRemoveSocketFromFlow = nullptr;
|
||||
|
||||
HANDLE wlan_handle = nullptr;
|
||||
HANDLE wlan_handle = nullptr;
|
||||
|
||||
decltype(WlanOpenHandle) *fn_WlanOpenHandle = nullptr;
|
||||
decltype(WlanCloseHandle) *fn_WlanCloseHandle = nullptr;
|
||||
decltype(WlanFreeMemory) *fn_WlanFreeMemory = nullptr;
|
||||
decltype(WlanEnumInterfaces) *fn_WlanEnumInterfaces = nullptr;
|
||||
decltype(WlanSetInterface) *fn_WlanSetInterface = nullptr;
|
||||
decltype(WlanOpenHandle) *fn_WlanOpenHandle = nullptr;
|
||||
decltype(WlanCloseHandle) *fn_WlanCloseHandle = nullptr;
|
||||
decltype(WlanFreeMemory) *fn_WlanFreeMemory = nullptr;
|
||||
decltype(WlanEnumInterfaces) *fn_WlanEnumInterfaces = nullptr;
|
||||
decltype(WlanSetInterface) *fn_WlanSetInterface = nullptr;
|
||||
|
||||
std::filesystem::path appdata() {
|
||||
std::filesystem::path
|
||||
appdata() {
|
||||
WCHAR sunshine_path[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, sunshine_path, _countof(sunshine_path));
|
||||
return std::filesystem::path { sunshine_path }.remove_filename() / L"config"sv;
|
||||
}
|
||||
}
|
||||
|
||||
std::string from_sockaddr(const sockaddr *const socket_address) {
|
||||
std::string
|
||||
from_sockaddr(const sockaddr *const socket_address) {
|
||||
char data[INET6_ADDRSTRLEN];
|
||||
|
||||
auto family = socket_address->sa_family;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)socket_address)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) socket_address)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)socket_address)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) socket_address)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
return std::string { data };
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
std::pair<std::uint16_t, std::string>
|
||||
from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
char data[INET6_ADDRSTRLEN];
|
||||
|
||||
auto family = ip_addr->sa_family;
|
||||
std::uint16_t port;
|
||||
if(family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6 *)ip_addr)->sin6_port;
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6 *) ip_addr)->sin6_port;
|
||||
}
|
||||
|
||||
if(family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in *)ip_addr)->sin_port;
|
||||
if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in *) ip_addr)->sin_port;
|
||||
}
|
||||
|
||||
return { port, std::string { data } };
|
||||
}
|
||||
}
|
||||
|
||||
adapteraddrs_t get_adapteraddrs() {
|
||||
adapteraddrs_t
|
||||
get_adapteraddrs() {
|
||||
adapteraddrs_t info { nullptr };
|
||||
ULONG size = 0;
|
||||
|
||||
while(GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, info.get(), &size) == ERROR_BUFFER_OVERFLOW) {
|
||||
info.reset((PIP_ADAPTER_ADDRESSES)malloc(size));
|
||||
while (GetAdaptersAddresses(AF_UNSPEC, 0, nullptr, info.get(), &size) == ERROR_BUFFER_OVERFLOW) {
|
||||
info.reset((PIP_ADAPTER_ADDRESSES) malloc(size));
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_mac_address(const std::string_view &address) {
|
||||
std::string
|
||||
get_mac_address(const std::string_view &address) {
|
||||
adapteraddrs_t info = get_adapteraddrs();
|
||||
for(auto adapter_pos = info.get(); adapter_pos != nullptr; adapter_pos = adapter_pos->Next) {
|
||||
for(auto addr_pos = adapter_pos->FirstUnicastAddress; addr_pos != nullptr; addr_pos = addr_pos->Next) {
|
||||
if(adapter_pos->PhysicalAddressLength != 0 && address == from_sockaddr(addr_pos->Address.lpSockaddr)) {
|
||||
for (auto adapter_pos = info.get(); adapter_pos != nullptr; adapter_pos = adapter_pos->Next) {
|
||||
for (auto addr_pos = adapter_pos->FirstUnicastAddress; addr_pos != nullptr; addr_pos = addr_pos->Next) {
|
||||
if (adapter_pos->PhysicalAddressLength != 0 && address == from_sockaddr(addr_pos->Address.lpSockaddr)) {
|
||||
std::stringstream mac_addr;
|
||||
mac_addr << std::hex;
|
||||
for(int i = 0; i < adapter_pos->PhysicalAddressLength; i++) {
|
||||
if(i > 0) {
|
||||
for (int i = 0; i < adapter_pos->PhysicalAddressLength; i++) {
|
||||
if (i > 0) {
|
||||
mac_addr << ':';
|
||||
}
|
||||
mac_addr << std::setw(2) << std::setfill('0') << (int)adapter_pos->PhysicalAddress[i];
|
||||
mac_addr << std::setw(2) << std::setfill('0') << (int) adapter_pos->PhysicalAddress[i];
|
||||
}
|
||||
return mac_addr.str();
|
||||
}
|
||||
|
|
@ -132,18 +137,19 @@ std::string get_mac_address(const std::string_view &address) {
|
|||
}
|
||||
BOOST_LOG(warning) << "Unable to find MAC address for "sv << address;
|
||||
return "00:00:00:00:00:00"s;
|
||||
}
|
||||
}
|
||||
|
||||
HDESK syncThreadDesktop() {
|
||||
HDESK
|
||||
syncThreadDesktop() {
|
||||
auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL);
|
||||
if(!hDesk) {
|
||||
if (!hDesk) {
|
||||
auto err = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to Open Input Desktop [0x"sv << util::hex(err).to_string_view() << ']';
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!SetThreadDesktop(hDesk)) {
|
||||
if (!SetThreadDesktop(hDesk)) {
|
||||
auto err = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to sync desktop to thread [0x"sv << util::hex(err).to_string_view() << ']';
|
||||
}
|
||||
|
|
@ -151,9 +157,10 @@ HDESK syncThreadDesktop() {
|
|||
CloseDesktop(hDesk);
|
||||
|
||||
return hDesk;
|
||||
}
|
||||
}
|
||||
|
||||
void print_status(const std::string_view &prefix, HRESULT status) {
|
||||
void
|
||||
print_status(const std::string_view &prefix, HRESULT status) {
|
||||
char err_string[1024];
|
||||
|
||||
DWORD bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
|
|
@ -165,9 +172,10 @@ void print_status(const std::string_view &prefix, HRESULT status) {
|
|||
nullptr);
|
||||
|
||||
BOOST_LOG(error) << prefix << ": "sv << std::string_view { err_string, bytes };
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring utf8_to_wide_string(const std::string &str) {
|
||||
std::wstring
|
||||
utf8_to_wide_string(const std::string &str) {
|
||||
// Determine the size required for the destination string
|
||||
int chars = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), NULL, 0);
|
||||
|
||||
|
|
@ -177,9 +185,10 @@ std::wstring utf8_to_wide_string(const std::string &str) {
|
|||
// Do the conversion for real
|
||||
chars = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.length(), buffer, chars);
|
||||
return std::wstring(buffer, chars);
|
||||
}
|
||||
}
|
||||
|
||||
std::string wide_to_utf8_string(const std::wstring &str) {
|
||||
std::string
|
||||
wide_to_utf8_string(const std::wstring &str) {
|
||||
// Determine the size required for the destination string
|
||||
int bytes = WideCharToMultiByte(CP_UTF8, 0, str.data(), str.length(), NULL, 0, NULL, NULL);
|
||||
|
||||
|
|
@ -189,12 +198,13 @@ std::string wide_to_utf8_string(const std::wstring &str) {
|
|||
// Do the conversion for real
|
||||
bytes = WideCharToMultiByte(CP_UTF8, 0, str.data(), str.length(), buffer, bytes, NULL, NULL);
|
||||
return std::string(buffer, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE duplicate_shell_token() {
|
||||
HANDLE
|
||||
duplicate_shell_token() {
|
||||
// Get the shell window (will usually be owned by explorer.exe)
|
||||
HWND shell_window = GetShellWindow();
|
||||
if(!shell_window) {
|
||||
if (!shell_window) {
|
||||
BOOST_LOG(error) << "No shell window found. Is explorer.exe running?"sv;
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -203,7 +213,7 @@ HANDLE duplicate_shell_token() {
|
|||
DWORD shell_pid;
|
||||
GetWindowThreadProcessId(shell_window, &shell_pid);
|
||||
HANDLE shell_process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, shell_pid);
|
||||
if(!shell_process) {
|
||||
if (!shell_process) {
|
||||
BOOST_LOG(error) << "Failed to open shell process: "sv << GetLastError();
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -212,7 +222,7 @@ HANDLE duplicate_shell_token() {
|
|||
HANDLE shell_token;
|
||||
BOOL ret = OpenProcessToken(shell_process, TOKEN_DUPLICATE, &shell_token);
|
||||
CloseHandle(shell_process);
|
||||
if(!ret) {
|
||||
if (!ret) {
|
||||
BOOST_LOG(error) << "Failed to open shell process token: "sv << GetLastError();
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -221,28 +231,29 @@ HANDLE duplicate_shell_token() {
|
|||
HANDLE new_token;
|
||||
ret = DuplicateTokenEx(shell_token, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &new_token);
|
||||
CloseHandle(shell_token);
|
||||
if(!ret) {
|
||||
if (!ret) {
|
||||
BOOST_LOG(error) << "Failed to duplicate shell process token: "sv << GetLastError();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new_token;
|
||||
}
|
||||
}
|
||||
|
||||
PTOKEN_USER get_token_user(HANDLE token) {
|
||||
PTOKEN_USER
|
||||
get_token_user(HANDLE token) {
|
||||
DWORD return_length;
|
||||
if(GetTokenInformation(token, TokenUser, NULL, 0, &return_length) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
if (GetTokenInformation(token, TokenUser, NULL, 0, &return_length) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to get token information size: "sv << winerr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto user = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, return_length);
|
||||
if(!user) {
|
||||
auto user = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), 0, return_length);
|
||||
if (!user) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!GetTokenInformation(token, TokenUser, user, return_length, &return_length)) {
|
||||
if (!GetTokenInformation(token, TokenUser, user, return_length, &return_length)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to get token information: "sv << winerr;
|
||||
HeapFree(GetProcessHeap(), 0, user);
|
||||
|
|
@ -250,15 +261,17 @@ PTOKEN_USER get_token_user(HANDLE token) {
|
|||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
void free_token_user(PTOKEN_USER user) {
|
||||
void
|
||||
free_token_user(PTOKEN_USER user) {
|
||||
HeapFree(GetProcessHeap(), 0, user);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_token_same_user_as_process(HANDLE other_token) {
|
||||
bool
|
||||
is_token_same_user_as_process(HANDLE other_token) {
|
||||
HANDLE process_token;
|
||||
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token)) {
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to open process token: "sv << winerr;
|
||||
return false;
|
||||
|
|
@ -266,12 +279,12 @@ bool is_token_same_user_as_process(HANDLE other_token) {
|
|||
|
||||
auto process_user = get_token_user(process_token);
|
||||
CloseHandle(process_token);
|
||||
if(!process_user) {
|
||||
if (!process_user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto token_user = get_token_user(other_token);
|
||||
if(!token_user) {
|
||||
if (!token_user) {
|
||||
free_token_user(process_user);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -282,17 +295,18 @@ bool is_token_same_user_as_process(HANDLE other_token) {
|
|||
free_token_user(token_user);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
bool merge_user_environment_block(bp::environment &env, HANDLE shell_token) {
|
||||
bool
|
||||
merge_user_environment_block(bp::environment &env, HANDLE shell_token) {
|
||||
// Get the target user's environment block
|
||||
PVOID env_block;
|
||||
if(!CreateEnvironmentBlock(&env_block, shell_token, FALSE)) {
|
||||
if (!CreateEnvironmentBlock(&env_block, shell_token, FALSE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the environment block and populate env
|
||||
for(auto c = (PWCHAR)env_block; *c != UNICODE_NULL; c += wcslen(c) + 1) {
|
||||
for (auto c = (PWCHAR) env_block; *c != UNICODE_NULL; c += wcslen(c) + 1) {
|
||||
// Environment variable entries end with a null-terminator, so std::wstring() will get an entire entry.
|
||||
std::string env_tuple = wide_to_utf8_string(std::wstring { c });
|
||||
std::string env_name = env_tuple.substr(0, env_tuple.find('='));
|
||||
|
|
@ -301,13 +315,13 @@ bool merge_user_environment_block(bp::environment &env, HANDLE shell_token) {
|
|||
// Perform a case-insensitive search to see if this variable name already exists
|
||||
auto itr = std::find_if(env.cbegin(), env.cend(),
|
||||
[&](const auto &e) { return boost::iequals(e.get_name(), env_name); });
|
||||
if(itr != env.cend()) {
|
||||
if (itr != env.cend()) {
|
||||
// Use this existing name if it is already present to ensure we merge properly
|
||||
env_name = itr->get_name();
|
||||
}
|
||||
|
||||
// For the PATH variable, we will merge the values together
|
||||
if(boost::iequals(env_name, "PATH")) {
|
||||
if (boost::iequals(env_name, "PATH")) {
|
||||
env[env_name] = env_val + ";" + env[env_name].to_string();
|
||||
}
|
||||
else {
|
||||
|
|
@ -318,17 +332,19 @@ bool merge_user_environment_block(bp::environment &env, HANDLE shell_token) {
|
|||
|
||||
DestroyEnvironmentBlock(env_block);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: This does NOT append a null terminator
|
||||
void append_string_to_environment_block(wchar_t *env_block, int &offset, const std::wstring &wstr) {
|
||||
// Note: This does NOT append a null terminator
|
||||
void
|
||||
append_string_to_environment_block(wchar_t *env_block, int &offset, const std::wstring &wstr) {
|
||||
std::memcpy(&env_block[offset], wstr.data(), wstr.length() * sizeof(wchar_t));
|
||||
offset += wstr.length();
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring create_environment_block(bp::environment &env) {
|
||||
std::wstring
|
||||
create_environment_block(bp::environment &env) {
|
||||
int size = 0;
|
||||
for(const auto &entry : env) {
|
||||
for (const auto &entry : env) {
|
||||
auto name = entry.get_name();
|
||||
auto value = entry.to_string();
|
||||
size += utf8_to_wide_string(name).length() + 1 /* L'=' */ + utf8_to_wide_string(value).length() + 1 /* L'\0' */;
|
||||
|
|
@ -338,7 +354,7 @@ std::wstring create_environment_block(bp::environment &env) {
|
|||
|
||||
wchar_t env_block[size];
|
||||
int offset = 0;
|
||||
for(const auto &entry : env) {
|
||||
for (const auto &entry : env) {
|
||||
auto name = entry.get_name();
|
||||
auto value = entry.to_string();
|
||||
|
||||
|
|
@ -353,33 +369,36 @@ std::wstring create_environment_block(bp::environment &env) {
|
|||
env_block[offset++] = L'\0';
|
||||
|
||||
return std::wstring(env_block, offset);
|
||||
}
|
||||
}
|
||||
|
||||
LPPROC_THREAD_ATTRIBUTE_LIST allocate_proc_thread_attr_list(DWORD attribute_count) {
|
||||
LPPROC_THREAD_ATTRIBUTE_LIST
|
||||
allocate_proc_thread_attr_list(DWORD attribute_count) {
|
||||
SIZE_T size;
|
||||
InitializeProcThreadAttributeList(NULL, attribute_count, 0, &size);
|
||||
|
||||
auto list = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, size);
|
||||
if(list == NULL) {
|
||||
auto list = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, size);
|
||||
if (list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!InitializeProcThreadAttributeList(list, attribute_count, 0, &size)) {
|
||||
if (!InitializeProcThreadAttributeList(list, attribute_count, 0, &size)) {
|
||||
HeapFree(GetProcessHeap(), 0, list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
void free_proc_thread_attr_list(LPPROC_THREAD_ATTRIBUTE_LIST list) {
|
||||
void
|
||||
free_proc_thread_attr_list(LPPROC_THREAD_ATTRIBUTE_LIST list) {
|
||||
DeleteProcThreadAttributeList(list);
|
||||
HeapFree(GetProcessHeap(), 0, list);
|
||||
}
|
||||
}
|
||||
|
||||
bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
bp::child
|
||||
run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
HANDLE shell_token = duplicate_shell_token();
|
||||
if(!shell_token) {
|
||||
if (!shell_token) {
|
||||
// This can happen if the shell has crashed. Fail the launch rather than risking launching with
|
||||
// Sunshine's permissions unmodified.
|
||||
ec = std::make_error_code(std::errc::no_such_process);
|
||||
|
|
@ -391,7 +410,7 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
});
|
||||
|
||||
// Populate env with user-specific environment variables
|
||||
if(!merge_user_environment_block(env, shell_token)) {
|
||||
if (!merge_user_environment_block(env, shell_token)) {
|
||||
ec = std::make_error_code(std::errc::not_enough_memory);
|
||||
return bp::child();
|
||||
}
|
||||
|
|
@ -406,7 +425,7 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
|
||||
// Allocate a process attribute list with space for 1 element
|
||||
startup_info.lpAttributeList = allocate_proc_thread_attr_list(1);
|
||||
if(startup_info.lpAttributeList == NULL) {
|
||||
if (startup_info.lpAttributeList == NULL) {
|
||||
ec = std::make_error_code(std::errc::not_enough_memory);
|
||||
return bp::child();
|
||||
}
|
||||
|
|
@ -415,8 +434,8 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
free_proc_thread_attr_list(list);
|
||||
});
|
||||
|
||||
if(file) {
|
||||
HANDLE log_file_handle = (HANDLE)_get_osfhandle(_fileno(file));
|
||||
if (file) {
|
||||
HANDLE log_file_handle = (HANDLE) _get_osfhandle(_fileno(file));
|
||||
|
||||
// Populate std handles if the caller gave us a log file to use
|
||||
startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||
|
|
@ -439,12 +458,12 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
// This will launch the child process elevated if Sunshine is elevated.
|
||||
PROCESS_INFORMATION process_info;
|
||||
BOOL ret;
|
||||
if(!is_token_same_user_as_process(shell_token)) {
|
||||
if (!is_token_same_user_as_process(shell_token)) {
|
||||
// Impersonate the user when launching the process. This will ensure that appropriate access
|
||||
// checks are done against the user token, not our SYSTEM token. It will also allow network
|
||||
// shares and mapped network drives to be used as launch targets, since those credentials
|
||||
// are stored per-user.
|
||||
if(!ImpersonateLoggedOnUser(shell_token)) {
|
||||
if (!ImpersonateLoggedOnUser(shell_token)) {
|
||||
auto winerror = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to impersonate user: "sv << winerror;
|
||||
ec = std::make_error_code(std::errc::permission_denied);
|
||||
|
|
@ -456,20 +475,20 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
// Set CREATE_NEW_CONSOLE to avoid writing stdout to Sunshine's log if 'file' is not specified.
|
||||
ret = CreateProcessAsUserW(shell_token,
|
||||
NULL,
|
||||
(LPWSTR)wcmd.c_str(),
|
||||
(LPWSTR) wcmd.c_str(),
|
||||
NULL,
|
||||
NULL,
|
||||
!!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES),
|
||||
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB,
|
||||
env_block.data(),
|
||||
start_dir.empty() ? NULL : start_dir.c_str(),
|
||||
(LPSTARTUPINFOW)&startup_info,
|
||||
(LPSTARTUPINFOW) &startup_info,
|
||||
&process_info);
|
||||
|
||||
if(!ret) {
|
||||
if (!ret) {
|
||||
auto error = GetLastError();
|
||||
|
||||
if(error == 740) {
|
||||
if (error == 740) {
|
||||
BOOST_LOG(info) << "Could not execute previous command because it required elevation. Running the command again with elevation, for security reasons this will prompt user interaction."sv;
|
||||
startup_info.StartupInfo.wShowWindow = SW_HIDE;
|
||||
startup_info.StartupInfo.dwFlags = startup_info.StartupInfo.dwFlags | STARTF_USESHOWWINDOW;
|
||||
|
|
@ -482,14 +501,14 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
// This is our intended behavior: to require interaction before elevating the command.
|
||||
ret = CreateProcessAsUserW(shell_token,
|
||||
nullptr,
|
||||
(LPWSTR)elevated_command.c_str(),
|
||||
(LPWSTR) elevated_command.c_str(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
!!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES),
|
||||
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB,
|
||||
env_block.data(),
|
||||
start_dir.empty() ? nullptr : start_dir.c_str(),
|
||||
(LPSTARTUPINFOW)&startup_info,
|
||||
(LPSTARTUPINFOW) &startup_info,
|
||||
&process_info);
|
||||
}
|
||||
}
|
||||
|
|
@ -497,7 +516,7 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
// End impersonation of the logged on user. If this fails (which is extremely unlikely),
|
||||
// we will be running with an unknown user token. The only safe thing to do in that case
|
||||
// is terminate ourselves.
|
||||
if(!RevertToSelf()) {
|
||||
if (!RevertToSelf()) {
|
||||
auto winerror = GetLastError();
|
||||
BOOST_LOG(fatal) << "Failed to revert to self after impersonation: "sv << winerror;
|
||||
std::abort();
|
||||
|
|
@ -505,22 +524,22 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
}
|
||||
else {
|
||||
ret = CreateProcessW(NULL,
|
||||
(LPWSTR)wcmd.c_str(),
|
||||
(LPWSTR) wcmd.c_str(),
|
||||
NULL,
|
||||
NULL,
|
||||
!!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES),
|
||||
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB,
|
||||
env_block.data(),
|
||||
start_dir.empty() ? NULL : start_dir.c_str(),
|
||||
(LPSTARTUPINFOW)&startup_info,
|
||||
(LPSTARTUPINFOW) &startup_info,
|
||||
&process_info);
|
||||
}
|
||||
|
||||
if(ret) {
|
||||
if (ret) {
|
||||
// Since we are always spawning a process with a less privileged token than ourselves,
|
||||
// bp::child() should have no problem opening it with any access rights it wants.
|
||||
auto child = bp::child((bp::pid_t)process_info.dwProcessId);
|
||||
if(group) {
|
||||
auto child = bp::child((bp::pid_t) process_info.dwProcessId);
|
||||
if (group) {
|
||||
group->add(child);
|
||||
}
|
||||
|
||||
|
|
@ -540,12 +559,13 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work
|
|||
ec = std::make_error_code(std::errc::invalid_argument);
|
||||
return bp::child();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adjust_thread_priority(thread_priority_e priority) {
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority) {
|
||||
int win32_priority;
|
||||
|
||||
switch(priority) {
|
||||
switch (priority) {
|
||||
case thread_priority_e::low:
|
||||
win32_priority = THREAD_PRIORITY_BELOW_NORMAL;
|
||||
break;
|
||||
|
|
@ -559,33 +579,34 @@ void adjust_thread_priority(thread_priority_e priority) {
|
|||
win32_priority = THREAD_PRIORITY_HIGHEST;
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unknown thread priority: "sv << (int)priority;
|
||||
BOOST_LOG(error) << "Unknown thread priority: "sv << (int) priority;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!SetThreadPriority(GetCurrentThread(), win32_priority)) {
|
||||
if (!SetThreadPriority(GetCurrentThread(), win32_priority)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(warning) << "Unable to set thread priority to "sv << win32_priority << ": "sv << winerr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void streaming_will_start() {
|
||||
void
|
||||
streaming_will_start() {
|
||||
static std::once_flag load_wlanapi_once_flag;
|
||||
std::call_once(load_wlanapi_once_flag, []() {
|
||||
// wlanapi.dll is not installed by default on Windows Server, so we load it dynamically
|
||||
HMODULE wlanapi = LoadLibraryExA("wlanapi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if(!wlanapi) {
|
||||
if (!wlanapi) {
|
||||
BOOST_LOG(debug) << "wlanapi.dll is not available on this OS"sv;
|
||||
return;
|
||||
}
|
||||
|
||||
fn_WlanOpenHandle = (decltype(fn_WlanOpenHandle))GetProcAddress(wlanapi, "WlanOpenHandle");
|
||||
fn_WlanCloseHandle = (decltype(fn_WlanCloseHandle))GetProcAddress(wlanapi, "WlanCloseHandle");
|
||||
fn_WlanFreeMemory = (decltype(fn_WlanFreeMemory))GetProcAddress(wlanapi, "WlanFreeMemory");
|
||||
fn_WlanEnumInterfaces = (decltype(fn_WlanEnumInterfaces))GetProcAddress(wlanapi, "WlanEnumInterfaces");
|
||||
fn_WlanSetInterface = (decltype(fn_WlanSetInterface))GetProcAddress(wlanapi, "WlanSetInterface");
|
||||
fn_WlanOpenHandle = (decltype(fn_WlanOpenHandle)) GetProcAddress(wlanapi, "WlanOpenHandle");
|
||||
fn_WlanCloseHandle = (decltype(fn_WlanCloseHandle)) GetProcAddress(wlanapi, "WlanCloseHandle");
|
||||
fn_WlanFreeMemory = (decltype(fn_WlanFreeMemory)) GetProcAddress(wlanapi, "WlanFreeMemory");
|
||||
fn_WlanEnumInterfaces = (decltype(fn_WlanEnumInterfaces)) GetProcAddress(wlanapi, "WlanEnumInterfaces");
|
||||
fn_WlanSetInterface = (decltype(fn_WlanSetInterface)) GetProcAddress(wlanapi, "WlanSetInterface");
|
||||
|
||||
if(!fn_WlanOpenHandle || !fn_WlanCloseHandle || !fn_WlanFreeMemory || !fn_WlanEnumInterfaces || !fn_WlanSetInterface) {
|
||||
if (!fn_WlanOpenHandle || !fn_WlanCloseHandle || !fn_WlanFreeMemory || !fn_WlanEnumInterfaces || !fn_WlanSetInterface) {
|
||||
BOOST_LOG(error) << "wlanapi.dll is missing exports?"sv;
|
||||
|
||||
fn_WlanOpenHandle = nullptr;
|
||||
|
|
@ -609,15 +630,15 @@ void streaming_will_start() {
|
|||
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
|
||||
|
||||
// Enable low latency mode on all connected WLAN NICs if wlanapi.dll is available
|
||||
if(fn_WlanOpenHandle) {
|
||||
if (fn_WlanOpenHandle) {
|
||||
DWORD negotiated_version;
|
||||
|
||||
if(fn_WlanOpenHandle(WLAN_API_MAKE_VERSION(2, 0), nullptr, &negotiated_version, &wlan_handle) == ERROR_SUCCESS) {
|
||||
if (fn_WlanOpenHandle(WLAN_API_MAKE_VERSION(2, 0), nullptr, &negotiated_version, &wlan_handle) == ERROR_SUCCESS) {
|
||||
PWLAN_INTERFACE_INFO_LIST wlan_interface_list;
|
||||
|
||||
if(fn_WlanEnumInterfaces(wlan_handle, nullptr, &wlan_interface_list) == ERROR_SUCCESS) {
|
||||
for(DWORD i = 0; i < wlan_interface_list->dwNumberOfItems; i++) {
|
||||
if(wlan_interface_list->InterfaceInfo[i].isState == wlan_interface_state_connected) {
|
||||
if (fn_WlanEnumInterfaces(wlan_handle, nullptr, &wlan_interface_list) == ERROR_SUCCESS) {
|
||||
for (DWORD i = 0; i < wlan_interface_list->dwNumberOfItems; i++) {
|
||||
if (wlan_interface_list->InterfaceInfo[i].isState == wlan_interface_state_connected) {
|
||||
// Enable media streaming mode for 802.11 wireless interfaces to reduce latency and
|
||||
// unneccessary background scanning operations that cause packet loss and jitter.
|
||||
//
|
||||
|
|
@ -626,7 +647,7 @@ void streaming_will_start() {
|
|||
BOOL value = TRUE;
|
||||
auto error = fn_WlanSetInterface(wlan_handle, &wlan_interface_list->InterfaceInfo[i].InterfaceGuid,
|
||||
wlan_intf_opcode_media_streaming_mode, sizeof(value), &value, nullptr);
|
||||
if(error == ERROR_SUCCESS) {
|
||||
if (error == ERROR_SUCCESS) {
|
||||
BOOST_LOG(info) << "WLAN interface "sv << i << " is now in low latency mode"sv;
|
||||
}
|
||||
}
|
||||
|
|
@ -642,12 +663,12 @@ void streaming_will_start() {
|
|||
}
|
||||
|
||||
// If there is no mouse connected, enable Mouse Keys to force the cursor to appear
|
||||
if(!GetSystemMetrics(SM_MOUSEPRESENT)) {
|
||||
if (!GetSystemMetrics(SM_MOUSEPRESENT)) {
|
||||
BOOST_LOG(info) << "A mouse was not detected. Sunshine will enable Mouse Keys while streaming to force the mouse cursor to appear.";
|
||||
|
||||
// Get the current state of Mouse Keys so we can restore it when streaming is over
|
||||
previous_mouse_keys_state.cbSize = sizeof(previous_mouse_keys_state);
|
||||
if(SystemParametersInfoW(SPI_GETMOUSEKEYS, 0, &previous_mouse_keys_state, 0)) {
|
||||
if (SystemParametersInfoW(SPI_GETMOUSEKEYS, 0, &previous_mouse_keys_state, 0)) {
|
||||
MOUSEKEYS new_mouse_keys_state = {};
|
||||
|
||||
// Enable Mouse Keys
|
||||
|
|
@ -655,7 +676,7 @@ void streaming_will_start() {
|
|||
new_mouse_keys_state.dwFlags = MKF_MOUSEKEYSON | MKF_AVAILABLE;
|
||||
new_mouse_keys_state.iMaxSpeed = 10;
|
||||
new_mouse_keys_state.iTimeToMaxSpeed = 1000;
|
||||
if(SystemParametersInfoW(SPI_SETMOUSEKEYS, 0, &new_mouse_keys_state, 0)) {
|
||||
if (SystemParametersInfoW(SPI_SETMOUSEKEYS, 0, &new_mouse_keys_state, 0)) {
|
||||
// Remember to restore the previous settings when we stop streaming
|
||||
enabled_mouse_keys = true;
|
||||
}
|
||||
|
|
@ -669,9 +690,10 @@ void streaming_will_start() {
|
|||
BOOST_LOG(warning) << "Unable to get current state of Mouse Keys: "sv << winerr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void streaming_will_stop() {
|
||||
void
|
||||
streaming_will_stop() {
|
||||
// Demote ourselves back to normal priority class
|
||||
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
||||
|
||||
|
|
@ -682,34 +704,37 @@ void streaming_will_stop() {
|
|||
DwmEnableMMCSS(false);
|
||||
|
||||
// Closing our WLAN client handle will undo our optimizations
|
||||
if(wlan_handle != nullptr) {
|
||||
if (wlan_handle != nullptr) {
|
||||
fn_WlanCloseHandle(wlan_handle, nullptr);
|
||||
wlan_handle = nullptr;
|
||||
}
|
||||
|
||||
// Restore Mouse Keys back to the previous settings if we turned it on
|
||||
if(enabled_mouse_keys) {
|
||||
if (enabled_mouse_keys) {
|
||||
enabled_mouse_keys = false;
|
||||
if(!SystemParametersInfoW(SPI_SETMOUSEKEYS, 0, &previous_mouse_keys_state, 0)) {
|
||||
if (!SystemParametersInfoW(SPI_SETMOUSEKEYS, 0, &previous_mouse_keys_state, 0)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(warning) << "Unable to restore original state of Mouse Keys: "sv << winerr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool restart_supported() {
|
||||
bool
|
||||
restart_supported() {
|
||||
// Restart is supported if we're running from the service
|
||||
return (GetConsoleWindow() == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool restart() {
|
||||
bool
|
||||
restart() {
|
||||
// Raise SIGINT to trigger the graceful exit logic. The service will
|
||||
// restart us in a few seconds.
|
||||
std::raise(SIGINT);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
SOCKADDR_IN to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
||||
SOCKADDR_IN
|
||||
to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
||||
SOCKADDR_IN saddr_v4 = {};
|
||||
|
||||
saddr_v4.sin_family = AF_INET;
|
||||
|
|
@ -719,9 +744,10 @@ SOCKADDR_IN to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
|||
memcpy(&saddr_v4.sin_addr, addr_bytes.data(), sizeof(saddr_v4.sin_addr));
|
||||
|
||||
return saddr_v4;
|
||||
}
|
||||
}
|
||||
|
||||
SOCKADDR_IN6 to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
||||
SOCKADDR_IN6
|
||||
to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
||||
SOCKADDR_IN6 saddr_v6 = {};
|
||||
|
||||
saddr_v6.sin6_family = AF_INET6;
|
||||
|
|
@ -732,31 +758,32 @@ SOCKADDR_IN6 to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
|||
memcpy(&saddr_v6.sin6_addr, addr_bytes.data(), sizeof(saddr_v6.sin6_addr));
|
||||
|
||||
return saddr_v6;
|
||||
}
|
||||
}
|
||||
|
||||
// Use UDP segmentation offload if it is supported by the OS. If the NIC is capable, this will use
|
||||
// hardware acceleration to reduce CPU usage. Support for USO was introduced in Windows 10 20H1.
|
||||
bool send_batch(batched_send_info_t &send_info) {
|
||||
// Use UDP segmentation offload if it is supported by the OS. If the NIC is capable, this will use
|
||||
// hardware acceleration to reduce CPU usage. Support for USO was introduced in Windows 10 20H1.
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info) {
|
||||
WSAMSG msg;
|
||||
|
||||
// Convert the target address into a SOCKADDR
|
||||
SOCKADDR_IN saddr_v4;
|
||||
SOCKADDR_IN6 saddr_v6;
|
||||
if(send_info.target_address.is_v6()) {
|
||||
if (send_info.target_address.is_v6()) {
|
||||
saddr_v6 = to_sockaddr(send_info.target_address.to_v6(), send_info.target_port);
|
||||
|
||||
msg.name = (PSOCKADDR)&saddr_v6;
|
||||
msg.name = (PSOCKADDR) &saddr_v6;
|
||||
msg.namelen = sizeof(saddr_v6);
|
||||
}
|
||||
else {
|
||||
saddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
|
||||
|
||||
msg.name = (PSOCKADDR)&saddr_v4;
|
||||
msg.name = (PSOCKADDR) &saddr_v4;
|
||||
msg.namelen = sizeof(saddr_v4);
|
||||
}
|
||||
|
||||
WSABUF buf;
|
||||
buf.buf = (char *)send_info.buffer;
|
||||
buf.buf = (char *) send_info.buffer;
|
||||
buf.len = send_info.block_size * send_info.block_count;
|
||||
|
||||
msg.lpBuffers = &buf;
|
||||
|
|
@ -767,37 +794,39 @@ bool send_batch(batched_send_info_t &send_info) {
|
|||
msg.Control.buf = cmbuf;
|
||||
msg.Control.len = 0;
|
||||
|
||||
if(send_info.block_count > 1) {
|
||||
if (send_info.block_count > 1) {
|
||||
msg.Control.len += WSA_CMSG_SPACE(sizeof(DWORD));
|
||||
|
||||
auto cm = WSA_CMSG_FIRSTHDR(&msg);
|
||||
cm->cmsg_level = IPPROTO_UDP;
|
||||
cm->cmsg_type = UDP_SEND_MSG_SIZE;
|
||||
cm->cmsg_len = WSA_CMSG_LEN(sizeof(DWORD));
|
||||
*((DWORD *)WSA_CMSG_DATA(cm)) = send_info.block_size;
|
||||
*((DWORD *) WSA_CMSG_DATA(cm)) = send_info.block_size;
|
||||
}
|
||||
|
||||
// If USO is not supported, this will fail and the caller will fall back to unbatched sends.
|
||||
DWORD bytes_sent;
|
||||
return WSASendMsg((SOCKET)send_info.native_socket, &msg, 1, &bytes_sent, nullptr, nullptr) != SOCKET_ERROR;
|
||||
}
|
||||
return WSASendMsg((SOCKET) send_info.native_socket, &msg, 1, &bytes_sent, nullptr, nullptr) != SOCKET_ERROR;
|
||||
}
|
||||
|
||||
class qos_t : public deinit_t {
|
||||
public:
|
||||
qos_t(QOS_FLOWID flow_id) : flow_id(flow_id) {}
|
||||
class qos_t: public deinit_t {
|
||||
public:
|
||||
qos_t(QOS_FLOWID flow_id):
|
||||
flow_id(flow_id) {}
|
||||
|
||||
virtual ~qos_t() {
|
||||
if(!fn_QOSRemoveSocketFromFlow(qos_handle, (SOCKET)NULL, flow_id, 0)) {
|
||||
if (!fn_QOSRemoveSocketFromFlow(qos_handle, (SOCKET) NULL, flow_id, 0)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(warning) << "QOSRemoveSocketFromFlow() failed: "sv << winerr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
QOS_FLOWID flow_id;
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type) {
|
||||
std::unique_ptr<deinit_t>
|
||||
enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type) {
|
||||
SOCKADDR_IN saddr_v4;
|
||||
SOCKADDR_IN6 saddr_v6;
|
||||
PSOCKADDR dest_addr;
|
||||
|
|
@ -806,16 +835,16 @@ std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio
|
|||
std::call_once(load_qwave_once_flag, []() {
|
||||
// qWAVE is not installed by default on Windows Server, so we load it dynamically
|
||||
HMODULE qwave = LoadLibraryExA("qwave.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if(!qwave) {
|
||||
if (!qwave) {
|
||||
BOOST_LOG(debug) << "qwave.dll is not available on this OS"sv;
|
||||
return;
|
||||
}
|
||||
|
||||
fn_QOSCreateHandle = (decltype(fn_QOSCreateHandle))GetProcAddress(qwave, "QOSCreateHandle");
|
||||
fn_QOSAddSocketToFlow = (decltype(fn_QOSAddSocketToFlow))GetProcAddress(qwave, "QOSAddSocketToFlow");
|
||||
fn_QOSRemoveSocketFromFlow = (decltype(fn_QOSRemoveSocketFromFlow))GetProcAddress(qwave, "QOSRemoveSocketFromFlow");
|
||||
fn_QOSCreateHandle = (decltype(fn_QOSCreateHandle)) GetProcAddress(qwave, "QOSCreateHandle");
|
||||
fn_QOSAddSocketToFlow = (decltype(fn_QOSAddSocketToFlow)) GetProcAddress(qwave, "QOSAddSocketToFlow");
|
||||
fn_QOSRemoveSocketFromFlow = (decltype(fn_QOSRemoveSocketFromFlow)) GetProcAddress(qwave, "QOSRemoveSocketFromFlow");
|
||||
|
||||
if(!fn_QOSCreateHandle || !fn_QOSAddSocketToFlow || !fn_QOSRemoveSocketFromFlow) {
|
||||
if (!fn_QOSCreateHandle || !fn_QOSAddSocketToFlow || !fn_QOSRemoveSocketFromFlow) {
|
||||
BOOST_LOG(error) << "qwave.dll is missing exports?"sv;
|
||||
|
||||
fn_QOSCreateHandle = nullptr;
|
||||
|
|
@ -827,7 +856,7 @@ std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio
|
|||
}
|
||||
|
||||
QOS_VERSION qos_version { 1, 0 };
|
||||
if(!fn_QOSCreateHandle(&qos_version, &qos_handle)) {
|
||||
if (!fn_QOSCreateHandle(&qos_version, &qos_handle)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(warning) << "QOSCreateHandle() failed: "sv << winerr;
|
||||
return;
|
||||
|
|
@ -835,21 +864,21 @@ std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio
|
|||
});
|
||||
|
||||
// If qWAVE is unavailable, just return
|
||||
if(!fn_QOSAddSocketToFlow || !qos_handle) {
|
||||
if (!fn_QOSAddSocketToFlow || !qos_handle) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(address.is_v6()) {
|
||||
if (address.is_v6()) {
|
||||
saddr_v6 = to_sockaddr(address.to_v6(), port);
|
||||
dest_addr = (PSOCKADDR)&saddr_v6;
|
||||
dest_addr = (PSOCKADDR) &saddr_v6;
|
||||
}
|
||||
else {
|
||||
saddr_v4 = to_sockaddr(address.to_v4(), port);
|
||||
dest_addr = (PSOCKADDR)&saddr_v4;
|
||||
dest_addr = (PSOCKADDR) &saddr_v4;
|
||||
}
|
||||
|
||||
QOS_TRAFFIC_TYPE traffic_type;
|
||||
switch(data_type) {
|
||||
switch (data_type) {
|
||||
case qos_data_type_e::audio:
|
||||
traffic_type = QOSTrafficTypeVoice;
|
||||
break;
|
||||
|
|
@ -857,18 +886,18 @@ std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio
|
|||
traffic_type = QOSTrafficTypeAudioVideo;
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unknown traffic type: "sv << (int)data_type;
|
||||
BOOST_LOG(error) << "Unknown traffic type: "sv << (int) data_type;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QOS_FLOWID flow_id = 0;
|
||||
if(!fn_QOSAddSocketToFlow(qos_handle, (SOCKET)native_socket, dest_addr, traffic_type, QOS_NON_ADAPTIVE_FLOW, &flow_id)) {
|
||||
if (!fn_QOSAddSocketToFlow(qos_handle, (SOCKET) native_socket, dest_addr, traffic_type, QOS_NON_ADAPTIVE_FLOW, &flow_id)) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(warning) << "QOSAddSocketToFlow() failed: "sv << winerr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<qos_t>(flow_id);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace platf
|
||||
|
|
@ -6,8 +6,10 @@
|
|||
#include <winnt.h>
|
||||
|
||||
namespace platf {
|
||||
void print_status(const std::string_view &prefix, HRESULT status);
|
||||
HDESK syncThreadDesktop();
|
||||
void
|
||||
print_status(const std::string_view &prefix, HRESULT status);
|
||||
HDESK
|
||||
syncThreadDesktop();
|
||||
} // namespace platf
|
||||
|
||||
#endif
|
||||
|
|
@ -59,7 +59,8 @@ typedef struct _DNS_SERVICE_INSTANCE {
|
|||
} DNS_SERVICE_INSTANCE, *PDNS_SERVICE_INSTANCE;
|
||||
#endif
|
||||
|
||||
typedef VOID WINAPI DNS_SERVICE_REGISTER_COMPLETE(
|
||||
typedef VOID WINAPI
|
||||
DNS_SERVICE_REGISTER_COMPLETE(
|
||||
_In_ DWORD Status,
|
||||
_In_ PVOID pQueryContext,
|
||||
_In_ PDNS_SERVICE_INSTANCE pInstance);
|
||||
|
|
@ -88,17 +89,19 @@ _FN(_DnsServiceRegister, DWORD, (_In_ PDNS_SERVICE_REGISTER_REQUEST pRequest, _I
|
|||
} /* extern "C" */
|
||||
|
||||
namespace platf::publish {
|
||||
VOID WINAPI register_cb(DWORD status, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) {
|
||||
auto alarm = (safe::alarm_t<PDNS_SERVICE_INSTANCE>::element_type *)pQueryContext;
|
||||
VOID WINAPI
|
||||
register_cb(DWORD status, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) {
|
||||
auto alarm = (safe::alarm_t<PDNS_SERVICE_INSTANCE>::element_type *) pQueryContext;
|
||||
|
||||
if(status) {
|
||||
if (status) {
|
||||
print_status("register_cb()"sv, status);
|
||||
}
|
||||
|
||||
alarm->ring(pInstance);
|
||||
}
|
||||
}
|
||||
|
||||
static int service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) {
|
||||
static int
|
||||
service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) {
|
||||
auto alarm = safe::make_alarm<PDNS_SERVICE_INSTANCE>();
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
|
||||
|
|
@ -121,16 +124,16 @@ static int service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) {
|
|||
|
||||
DNS_STATUS status {};
|
||||
|
||||
if(enable) {
|
||||
if (enable) {
|
||||
status = _DnsServiceRegister(&req, nullptr);
|
||||
if(status != DNS_REQUEST_PENDING) {
|
||||
if (status != DNS_REQUEST_PENDING) {
|
||||
print_status("DnsServiceRegister()"sv, status);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = _DnsServiceDeRegister(&req, nullptr);
|
||||
if(status != DNS_REQUEST_PENDING) {
|
||||
if (status != DNS_REQUEST_PENDING) {
|
||||
print_status("DnsServiceDeRegister()"sv, status);
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -139,23 +142,24 @@ static int service(bool enable, PDNS_SERVICE_INSTANCE &existing_instance) {
|
|||
alarm->wait();
|
||||
|
||||
auto registered_instance = alarm->status();
|
||||
if(enable) {
|
||||
if (enable) {
|
||||
// Store this instance for later deregistration
|
||||
existing_instance = registered_instance;
|
||||
}
|
||||
else if(registered_instance) {
|
||||
else if (registered_instance) {
|
||||
// Deregistration was successful
|
||||
_DnsServiceFreeInstance(registered_instance);
|
||||
existing_instance = nullptr;
|
||||
}
|
||||
|
||||
return registered_instance ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
class mdns_registration_t : public ::platf::deinit_t {
|
||||
public:
|
||||
mdns_registration_t() : existing_instance(nullptr) {
|
||||
if(service(true, existing_instance)) {
|
||||
class mdns_registration_t: public ::platf::deinit_t {
|
||||
public:
|
||||
mdns_registration_t():
|
||||
existing_instance(nullptr) {
|
||||
if (service(true, existing_instance)) {
|
||||
BOOST_LOG(error) << "Unable to register Sunshine mDNS service"sv;
|
||||
return;
|
||||
}
|
||||
|
|
@ -164,8 +168,8 @@ public:
|
|||
}
|
||||
|
||||
~mdns_registration_t() override {
|
||||
if(existing_instance) {
|
||||
if(service(false, existing_instance)) {
|
||||
if (existing_instance) {
|
||||
if (service(false, existing_instance)) {
|
||||
BOOST_LOG(error) << "Unable to unregister Sunshine mDNS service"sv;
|
||||
return;
|
||||
}
|
||||
|
|
@ -174,36 +178,38 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
PDNS_SERVICE_INSTANCE existing_instance;
|
||||
};
|
||||
};
|
||||
|
||||
int load_funcs(HMODULE handle) {
|
||||
int
|
||||
load_funcs(HMODULE handle) {
|
||||
auto fg = util::fail_guard([handle]() {
|
||||
FreeLibrary(handle);
|
||||
});
|
||||
|
||||
_DnsServiceFreeInstance = (_DnsServiceFreeInstance_fn)GetProcAddress(handle, "DnsServiceFreeInstance");
|
||||
_DnsServiceDeRegister = (_DnsServiceDeRegister_fn)GetProcAddress(handle, "DnsServiceDeRegister");
|
||||
_DnsServiceRegister = (_DnsServiceRegister_fn)GetProcAddress(handle, "DnsServiceRegister");
|
||||
_DnsServiceFreeInstance = (_DnsServiceFreeInstance_fn) GetProcAddress(handle, "DnsServiceFreeInstance");
|
||||
_DnsServiceDeRegister = (_DnsServiceDeRegister_fn) GetProcAddress(handle, "DnsServiceDeRegister");
|
||||
_DnsServiceRegister = (_DnsServiceRegister_fn) GetProcAddress(handle, "DnsServiceRegister");
|
||||
|
||||
if(!(_DnsServiceFreeInstance && _DnsServiceDeRegister && _DnsServiceRegister)) {
|
||||
if (!(_DnsServiceFreeInstance && _DnsServiceDeRegister && _DnsServiceRegister)) {
|
||||
BOOST_LOG(error) << "mDNS service not available in dnsapi.dll"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fg.disable();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<::platf::deinit_t> start() {
|
||||
std::unique_ptr<::platf::deinit_t>
|
||||
start() {
|
||||
HMODULE handle = LoadLibrary("dnsapi.dll");
|
||||
|
||||
if(!handle || load_funcs(handle)) {
|
||||
if (!handle || load_funcs(handle)) {
|
||||
BOOST_LOG(error) << "Couldn't load dnsapi.dll, You'll need to add PC manually from Moonlight"sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<mdns_registration_t>();
|
||||
}
|
||||
}
|
||||
} // namespace platf::publish
|
||||
|
|
|
|||
220
src/process.cpp
220
src/process.cpp
|
|
@ -25,21 +25,22 @@
|
|||
#include "utility.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// _SH constants for _wfsopen()
|
||||
#include <share.h>
|
||||
// _SH constants for _wfsopen()
|
||||
#include <share.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_APP_IMAGE_PATH SUNSHINE_ASSETS_DIR "/box.png"
|
||||
|
||||
namespace proc {
|
||||
using namespace std::literals;
|
||||
namespace bp = boost::process;
|
||||
namespace pt = boost::property_tree;
|
||||
using namespace std::literals;
|
||||
namespace bp = boost::process;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
proc_t proc;
|
||||
proc_t proc;
|
||||
|
||||
void process_end(bp::child &proc, bp::group &proc_handle) {
|
||||
if(!proc.running()) {
|
||||
void
|
||||
process_end(bp::child &proc, bp::group &proc_handle) {
|
||||
if (!proc.running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -48,16 +49,17 @@ void process_end(bp::child &proc, bp::group &proc_handle) {
|
|||
|
||||
// avoid zombie process
|
||||
proc.wait();
|
||||
}
|
||||
}
|
||||
|
||||
boost::filesystem::path find_working_directory(const std::string &cmd, bp::environment &env) {
|
||||
boost::filesystem::path
|
||||
find_working_directory(const std::string &cmd, bp::environment &env) {
|
||||
// Parse the raw command string into parts to get the actual command portion
|
||||
#ifdef _WIN32
|
||||
auto parts = boost::program_options::split_winmain(cmd);
|
||||
#else
|
||||
auto parts = boost::program_options::split_unix(cmd);
|
||||
#endif
|
||||
if(parts.empty()) {
|
||||
if (parts.empty()) {
|
||||
BOOST_LOG(error) << "Unable to parse command: "sv << cmd;
|
||||
return boost::filesystem::path();
|
||||
}
|
||||
|
|
@ -66,9 +68,9 @@ boost::filesystem::path find_working_directory(const std::string &cmd, bp::envir
|
|||
|
||||
// If the cmd path is not an absolute path, resolve it using our PATH variable
|
||||
boost::filesystem::path cmd_path(parts.at(0));
|
||||
if(!cmd_path.is_absolute()) {
|
||||
if (!cmd_path.is_absolute()) {
|
||||
cmd_path = boost::process::search_path(parts.at(0));
|
||||
if(cmd_path.empty()) {
|
||||
if (cmd_path.empty()) {
|
||||
BOOST_LOG(error) << "Unable to find executable ["sv << parts.at(0) << "]. Is it in your PATH?"sv;
|
||||
return boost::filesystem::path();
|
||||
}
|
||||
|
|
@ -78,9 +80,10 @@ boost::filesystem::path find_working_directory(const std::string &cmd, bp::envir
|
|||
|
||||
// Now that we have a complete path, we can just use parent_path()
|
||||
return cmd_path.parent_path();
|
||||
}
|
||||
}
|
||||
|
||||
int proc_t::execute(int app_id) {
|
||||
int
|
||||
proc_t::execute(int app_id) {
|
||||
// Ensure starting from a clean slate
|
||||
terminate();
|
||||
|
||||
|
|
@ -88,7 +91,7 @@ int proc_t::execute(int app_id) {
|
|||
return app.id == std::to_string(app_id);
|
||||
});
|
||||
|
||||
if(iter == _apps.end()) {
|
||||
if (iter == _apps.end()) {
|
||||
BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']';
|
||||
return 404;
|
||||
}
|
||||
|
|
@ -99,7 +102,7 @@ int proc_t::execute(int app_id) {
|
|||
_app_prep_begin = std::begin(_app.prep_cmds);
|
||||
_app_prep_it = _app_prep_begin;
|
||||
|
||||
if(!_app.output.empty() && _app.output != "null"sv) {
|
||||
if (!_app.output.empty() && _app.output != "null"sv) {
|
||||
#ifdef _WIN32
|
||||
// fopen() interprets the filename as an ANSI string on Windows, so we must convert it
|
||||
// to UTF-16 and use the wchar_t variants for proper Unicode log file path support.
|
||||
|
|
@ -121,7 +124,7 @@ int proc_t::execute(int app_id) {
|
|||
terminate();
|
||||
});
|
||||
|
||||
for(; _app_prep_it != std::end(_app.prep_cmds); ++_app_prep_it) {
|
||||
for (; _app_prep_it != std::end(_app.prep_cmds); ++_app_prep_it) {
|
||||
auto &cmd = _app_prep_it->do_cmd;
|
||||
|
||||
boost::filesystem::path working_dir = _app.working_dir.empty() ?
|
||||
|
|
@ -130,7 +133,7 @@ int proc_t::execute(int app_id) {
|
|||
BOOST_LOG(info) << "Executing Do Cmd: ["sv << cmd << ']';
|
||||
auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr);
|
||||
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "Couldn't run ["sv << cmd << "]: System: "sv << ec.message();
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -138,19 +141,19 @@ int proc_t::execute(int app_id) {
|
|||
child.wait();
|
||||
auto ret = child.exit_code();
|
||||
|
||||
if(ret != 0) {
|
||||
if (ret != 0) {
|
||||
BOOST_LOG(error) << '[' << cmd << "] failed with code ["sv << ret << ']';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &cmd : _app.detached) {
|
||||
for (auto &cmd : _app.detached) {
|
||||
boost::filesystem::path working_dir = _app.working_dir.empty() ?
|
||||
find_working_directory(cmd, _env) :
|
||||
boost::filesystem::path(_app.working_dir);
|
||||
BOOST_LOG(info) << "Spawning ["sv << cmd << "] in ["sv << working_dir << ']';
|
||||
auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr);
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(warning) << "Couldn't spawn ["sv << cmd << "]: System: "sv << ec.message();
|
||||
}
|
||||
else {
|
||||
|
|
@ -158,7 +161,7 @@ int proc_t::execute(int app_id) {
|
|||
}
|
||||
}
|
||||
|
||||
if(_app.cmd.empty()) {
|
||||
if (_app.cmd.empty()) {
|
||||
BOOST_LOG(info) << "Executing [Desktop]"sv;
|
||||
placebo = true;
|
||||
}
|
||||
|
|
@ -168,7 +171,7 @@ int proc_t::execute(int app_id) {
|
|||
boost::filesystem::path(_app.working_dir);
|
||||
BOOST_LOG(info) << "Executing: ["sv << _app.cmd << "] in ["sv << working_dir << ']';
|
||||
_process = platf::run_unprivileged(_app.cmd, working_dir, _env, _pipe.get(), ec, &_process_handle);
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(warning) << "Couldn't run ["sv << _app.cmd << "]: System: "sv << ec.message();
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -177,22 +180,24 @@ int proc_t::execute(int app_id) {
|
|||
fg.disable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int proc_t::running() {
|
||||
if(placebo || _process.running()) {
|
||||
int
|
||||
proc_t::running() {
|
||||
if (placebo || _process.running()) {
|
||||
return _app_id;
|
||||
}
|
||||
|
||||
// Perform cleanup actions now if needed
|
||||
if(_process) {
|
||||
if (_process) {
|
||||
terminate();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void proc_t::terminate() {
|
||||
void
|
||||
proc_t::terminate() {
|
||||
std::error_code ec;
|
||||
|
||||
// Ensure child process is terminated
|
||||
|
|
@ -202,10 +207,10 @@ void proc_t::terminate() {
|
|||
_process_handle = bp::group();
|
||||
_app_id = -1;
|
||||
|
||||
for(; _app_prep_it != _app_prep_begin; --_app_prep_it) {
|
||||
for (; _app_prep_it != _app_prep_begin; --_app_prep_it) {
|
||||
auto &cmd = (_app_prep_it - 1)->undo_cmd;
|
||||
|
||||
if(cmd.empty()) {
|
||||
if (cmd.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -215,76 +220,81 @@ void proc_t::terminate() {
|
|||
BOOST_LOG(info) << "Executing Undo Cmd: ["sv << cmd << ']';
|
||||
auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr);
|
||||
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(warning) << "System: "sv << ec.message();
|
||||
}
|
||||
|
||||
child.wait();
|
||||
auto ret = child.exit_code();
|
||||
|
||||
if(ret != 0) {
|
||||
if (ret != 0) {
|
||||
BOOST_LOG(warning) << "Return code ["sv << ret << ']';
|
||||
}
|
||||
}
|
||||
|
||||
_pipe.reset();
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<ctx_t> &proc_t::get_apps() const {
|
||||
const std::vector<ctx_t> &
|
||||
proc_t::get_apps() const {
|
||||
return _apps;
|
||||
}
|
||||
std::vector<ctx_t> &proc_t::get_apps() {
|
||||
}
|
||||
std::vector<ctx_t> &
|
||||
proc_t::get_apps() {
|
||||
return _apps;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets application image from application list.
|
||||
// Returns image from assets directory if found there.
|
||||
// Returns default image if image configuration is not set.
|
||||
// Returns http content-type header compatible image type.
|
||||
std::string proc_t::get_app_image(int app_id) {
|
||||
// Gets application image from application list.
|
||||
// Returns image from assets directory if found there.
|
||||
// Returns default image if image configuration is not set.
|
||||
// Returns http content-type header compatible image type.
|
||||
std::string
|
||||
proc_t::get_app_image(int app_id) {
|
||||
auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) {
|
||||
return app.id == std::to_string(app_id);
|
||||
});
|
||||
auto app_image_path = iter == _apps.end() ? std::string() : iter->image_path;
|
||||
|
||||
return validate_app_image_path(app_image_path);
|
||||
}
|
||||
}
|
||||
|
||||
proc_t::~proc_t() {
|
||||
proc_t::~proc_t() {
|
||||
terminate();
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view::iterator find_match(std::string_view::iterator begin, std::string_view::iterator end) {
|
||||
std::string_view::iterator
|
||||
find_match(std::string_view::iterator begin, std::string_view::iterator end) {
|
||||
int stack = 0;
|
||||
|
||||
--begin;
|
||||
do {
|
||||
++begin;
|
||||
switch(*begin) {
|
||||
switch (*begin) {
|
||||
case '(':
|
||||
++stack;
|
||||
break;
|
||||
case ')':
|
||||
--stack;
|
||||
}
|
||||
} while(begin != end && stack != 0);
|
||||
} while (begin != end && stack != 0);
|
||||
|
||||
if(begin == end) {
|
||||
if (begin == end) {
|
||||
throw std::out_of_range("Missing closing bracket \')\'");
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
}
|
||||
|
||||
std::string parse_env_val(bp::native_environment &env, const std::string_view &val_raw) {
|
||||
std::string
|
||||
parse_env_val(bp::native_environment &env, const std::string_view &val_raw) {
|
||||
auto pos = std::begin(val_raw);
|
||||
auto dollar = std::find(pos, std::end(val_raw), '$');
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
while(dollar != std::end(val_raw)) {
|
||||
while (dollar != std::end(val_raw)) {
|
||||
auto next = dollar + 1;
|
||||
if(next != std::end(val_raw)) {
|
||||
switch(*next) {
|
||||
if (next != std::end(val_raw)) {
|
||||
switch (*next) {
|
||||
case '(': {
|
||||
ss.write(pos, (dollar - pos));
|
||||
auto var_begin = next + 1;
|
||||
|
|
@ -297,7 +307,7 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v
|
|||
// correctly appending to PATH on Windows.
|
||||
auto itr = std::find_if(env.cbegin(), env.cend(),
|
||||
[&](const auto &e) { return boost::iequals(e.get_name(), var_name); });
|
||||
if(itr != env.cend()) {
|
||||
if (itr != env.cend()) {
|
||||
// Use an existing case-insensitive match
|
||||
var_name = itr->get_name();
|
||||
}
|
||||
|
|
@ -327,10 +337,11 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v
|
|||
ss.write(pos, (dollar - pos));
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
std::string validate_app_image_path(std::string app_image_path) {
|
||||
if(app_image_path.empty()) {
|
||||
std::string
|
||||
validate_app_image_path(std::string app_image_path) {
|
||||
if (app_image_path.empty()) {
|
||||
return DEFAULT_APP_IMAGE_PATH;
|
||||
}
|
||||
|
||||
|
|
@ -339,23 +350,23 @@ std::string validate_app_image_path(std::string app_image_path) {
|
|||
boost::to_lower(image_extension);
|
||||
|
||||
// return the default box image if extension is not "png"
|
||||
if(image_extension != ".png") {
|
||||
if (image_extension != ".png") {
|
||||
return DEFAULT_APP_IMAGE_PATH;
|
||||
}
|
||||
|
||||
// check if image is in assets directory
|
||||
auto full_image_path = std::filesystem::path(SUNSHINE_ASSETS_DIR) / app_image_path;
|
||||
if(std::filesystem::exists(full_image_path)) {
|
||||
if (std::filesystem::exists(full_image_path)) {
|
||||
return full_image_path.string();
|
||||
}
|
||||
else if(app_image_path == "./assets/steam.png") {
|
||||
else if (app_image_path == "./assets/steam.png") {
|
||||
// handle old default steam image definition
|
||||
return SUNSHINE_ASSETS_DIR "/steam.png";
|
||||
}
|
||||
|
||||
// check if specified image exists
|
||||
std::error_code code;
|
||||
if(!std::filesystem::exists(app_image_path, code)) {
|
||||
if (!std::filesystem::exists(app_image_path, code)) {
|
||||
// return default box image if image does not exist
|
||||
BOOST_LOG(warning) << "Couldn't find app image at path ["sv << app_image_path << ']';
|
||||
return DEFAULT_APP_IMAGE_PATH;
|
||||
|
|
@ -364,57 +375,60 @@ std::string validate_app_image_path(std::string app_image_path) {
|
|||
// image is a png, and not in assets directory
|
||||
// return only "content-type" http header compatible image type
|
||||
return app_image_path;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> calculate_sha256(const std::string &filename) {
|
||||
std::optional<std::string>
|
||||
calculate_sha256(const std::string &filename) {
|
||||
crypto::md_ctx_t ctx { EVP_MD_CTX_create() };
|
||||
if(!ctx) {
|
||||
if (!ctx) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(!EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr)) {
|
||||
if (!EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Read file and update calculated SHA
|
||||
char buf[1024 * 16];
|
||||
std::ifstream file(filename, std::ifstream::binary);
|
||||
while(file.good()) {
|
||||
while (file.good()) {
|
||||
file.read(buf, sizeof(buf));
|
||||
if(!EVP_DigestUpdate(ctx.get(), buf, file.gcount())) {
|
||||
if (!EVP_DigestUpdate(ctx.get(), buf, file.gcount())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
|
||||
unsigned char result[SHA256_DIGEST_LENGTH];
|
||||
if(!EVP_DigestFinal_ex(ctx.get(), result, nullptr)) {
|
||||
if (!EVP_DigestFinal_ex(ctx.get(), result, nullptr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Transform byte-array to string
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::setfill('0');
|
||||
for(const auto &byte : result) {
|
||||
ss << std::setw(2) << (int)byte;
|
||||
for (const auto &byte : result) {
|
||||
ss << std::setw(2) << (int) byte;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t calculate_crc32(const std::string &input) {
|
||||
uint32_t
|
||||
calculate_crc32(const std::string &input) {
|
||||
boost::crc_32_type result;
|
||||
result.process_bytes(input.data(), input.length());
|
||||
return result.checksum();
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<std::string, std::string> calculate_app_id(const std::string &app_name, std::string app_image_path, int index) {
|
||||
std::tuple<std::string, std::string>
|
||||
calculate_app_id(const std::string &app_name, std::string app_image_path, int index) {
|
||||
// Generate id by hashing name with image data if present
|
||||
std::vector<std::string> to_hash;
|
||||
to_hash.push_back(app_name);
|
||||
auto file_path = validate_app_image_path(app_image_path);
|
||||
if(file_path != DEFAULT_APP_IMAGE_PATH) {
|
||||
if (file_path != DEFAULT_APP_IMAGE_PATH) {
|
||||
auto file_hash = calculate_sha256(file_path);
|
||||
if(file_hash) {
|
||||
if (file_hash) {
|
||||
to_hash.push_back(file_hash.value());
|
||||
}
|
||||
else {
|
||||
|
|
@ -431,13 +445,14 @@ std::tuple<std::string, std::string> calculate_app_id(const std::string &app_nam
|
|||
auto input_with_index = ss.str();
|
||||
|
||||
// CRC32 then truncate to signed 32-bit range due to client limitations
|
||||
auto id_no_index = std::to_string(abs((int32_t)calculate_crc32(input_no_index)));
|
||||
auto id_with_index = std::to_string(abs((int32_t)calculate_crc32(input_with_index)));
|
||||
auto id_no_index = std::to_string(abs((int32_t) calculate_crc32(input_no_index)));
|
||||
auto id_with_index = std::to_string(abs((int32_t) calculate_crc32(input_with_index)));
|
||||
|
||||
return std::make_tuple(id_no_index, id_with_index);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<proc::proc_t> parse(const std::string &file_name) {
|
||||
std::optional<proc::proc_t>
|
||||
parse(const std::string &file_name) {
|
||||
pt::ptree tree;
|
||||
|
||||
try {
|
||||
|
|
@ -448,14 +463,14 @@ std::optional<proc::proc_t> parse(const std::string &file_name) {
|
|||
|
||||
auto this_env = boost::this_process::environment();
|
||||
|
||||
for(auto &[name, val] : env_vars) {
|
||||
for (auto &[name, val] : env_vars) {
|
||||
this_env[name] = parse_env_val(this_env, val.get_value<std::string>());
|
||||
}
|
||||
|
||||
std::set<std::string> ids;
|
||||
std::vector<proc::ctx_t> apps;
|
||||
int i = 0;
|
||||
for(auto &[_, app_node] : apps_node) {
|
||||
for (auto &[_, app_node] : apps_node) {
|
||||
proc::ctx_t ctx;
|
||||
|
||||
auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s);
|
||||
|
|
@ -468,9 +483,9 @@ std::optional<proc::proc_t> parse(const std::string &file_name) {
|
|||
auto working_dir = app_node.get_optional<std::string>("working-dir"s);
|
||||
|
||||
std::vector<proc::cmd_t> prep_cmds;
|
||||
if(!exclude_global_prep.value_or(false)) {
|
||||
if (!exclude_global_prep.value_or(false)) {
|
||||
prep_cmds.reserve(config::sunshine.prep_cmds.size());
|
||||
for(auto &prep_cmd : config::sunshine.prep_cmds) {
|
||||
for (auto &prep_cmd : config::sunshine.prep_cmds) {
|
||||
auto do_cmd = parse_env_val(this_env, prep_cmd.do_cmd);
|
||||
auto undo_cmd = parse_env_val(this_env, prep_cmd.undo_cmd);
|
||||
|
||||
|
|
@ -478,15 +493,15 @@ std::optional<proc::proc_t> parse(const std::string &file_name) {
|
|||
}
|
||||
}
|
||||
|
||||
if(prep_nodes_opt) {
|
||||
if (prep_nodes_opt) {
|
||||
auto &prep_nodes = *prep_nodes_opt;
|
||||
|
||||
prep_cmds.reserve(prep_cmds.size() + prep_nodes.size());
|
||||
for(auto &[_, prep_node] : prep_nodes) {
|
||||
for (auto &[_, prep_node] : prep_nodes) {
|
||||
auto do_cmd = parse_env_val(this_env, prep_node.get<std::string>("do"s));
|
||||
auto undo_cmd = prep_node.get_optional<std::string>("undo"s);
|
||||
|
||||
if(undo_cmd) {
|
||||
if (undo_cmd) {
|
||||
prep_cmds.emplace_back(std::move(do_cmd), parse_env_val(this_env, *undo_cmd));
|
||||
}
|
||||
else {
|
||||
|
|
@ -496,33 +511,33 @@ std::optional<proc::proc_t> parse(const std::string &file_name) {
|
|||
}
|
||||
|
||||
std::vector<std::string> detached;
|
||||
if(detached_nodes_opt) {
|
||||
if (detached_nodes_opt) {
|
||||
auto &detached_nodes = *detached_nodes_opt;
|
||||
|
||||
detached.reserve(detached_nodes.size());
|
||||
for(auto &[_, detached_val] : detached_nodes) {
|
||||
for (auto &[_, detached_val] : detached_nodes) {
|
||||
detached.emplace_back(parse_env_val(this_env, detached_val.get_value<std::string>()));
|
||||
}
|
||||
}
|
||||
|
||||
if(output) {
|
||||
if (output) {
|
||||
ctx.output = parse_env_val(this_env, *output);
|
||||
}
|
||||
|
||||
if(cmd) {
|
||||
if (cmd) {
|
||||
ctx.cmd = parse_env_val(this_env, *cmd);
|
||||
}
|
||||
|
||||
if(working_dir) {
|
||||
if (working_dir) {
|
||||
ctx.working_dir = parse_env_val(this_env, *working_dir);
|
||||
}
|
||||
|
||||
if(image_path) {
|
||||
if (image_path) {
|
||||
ctx.image_path = parse_env_val(this_env, *image_path);
|
||||
}
|
||||
|
||||
auto possible_ids = calculate_app_id(name, ctx.image_path, i++);
|
||||
if(ids.count(std::get<0>(possible_ids)) == 0) {
|
||||
if (ids.count(std::get<0>(possible_ids)) == 0) {
|
||||
// Avoid using index to generate id if possible
|
||||
ctx.id = std::get<0>(possible_ids);
|
||||
}
|
||||
|
|
@ -543,18 +558,19 @@ std::optional<proc::proc_t> parse(const std::string &file_name) {
|
|||
std::move(this_env), std::move(apps)
|
||||
};
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch (std::exception &e) {
|
||||
BOOST_LOG(error) << e.what();
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void refresh(const std::string &file_name) {
|
||||
void
|
||||
refresh(const std::string &file_name) {
|
||||
auto proc_opt = proc::parse(file_name);
|
||||
|
||||
if(proc_opt) {
|
||||
if (proc_opt) {
|
||||
proc = std::move(*proc_opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace proc
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#define SUNSHINE_PROCESS_H
|
||||
|
||||
#ifndef __kernel_entry
|
||||
#define __kernel_entry
|
||||
#define __kernel_entry
|
||||
#endif
|
||||
|
||||
#include <optional>
|
||||
|
|
@ -16,10 +16,10 @@
|
|||
#include "utility.h"
|
||||
|
||||
namespace proc {
|
||||
using file_t = util::safe_ptr_v2<FILE, int, fclose>;
|
||||
using file_t = util::safe_ptr_v2<FILE, int, fclose>;
|
||||
|
||||
typedef config::prep_cmd_t cmd_t;
|
||||
/*
|
||||
typedef config::prep_cmd_t cmd_t;
|
||||
/*
|
||||
* pre_cmds -- guaranteed to be executed unless any of the commands fail.
|
||||
* detached -- commands detached from Sunshine
|
||||
* cmd -- Runs indefinitely until:
|
||||
|
|
@ -31,7 +31,7 @@ typedef config::prep_cmd_t cmd_t;
|
|||
* "null" -- The output of the commands are discarded
|
||||
* filename -- The output of the commands are appended to filename
|
||||
*/
|
||||
struct ctx_t {
|
||||
struct ctx_t {
|
||||
std::vector<cmd_t> prep_cmds;
|
||||
|
||||
/**
|
||||
|
|
@ -48,34 +48,41 @@ struct ctx_t {
|
|||
std::string output;
|
||||
std::string image_path;
|
||||
std::string id;
|
||||
};
|
||||
};
|
||||
|
||||
class proc_t {
|
||||
public:
|
||||
class proc_t {
|
||||
public:
|
||||
KITTY_DEFAULT_CONSTR_MOVE_THROW(proc_t)
|
||||
|
||||
proc_t(
|
||||
boost::process::environment &&env,
|
||||
std::vector<ctx_t> &&apps) : _app_id(0),
|
||||
std::vector<ctx_t> &&apps):
|
||||
_app_id(0),
|
||||
_env(std::move(env)),
|
||||
_apps(std::move(apps)) {}
|
||||
|
||||
int execute(int app_id);
|
||||
int
|
||||
execute(int app_id);
|
||||
|
||||
/**
|
||||
* @return _app_id if a process is running, otherwise returns 0
|
||||
*/
|
||||
int running();
|
||||
int
|
||||
running();
|
||||
|
||||
~proc_t();
|
||||
|
||||
const std::vector<ctx_t> &get_apps() const;
|
||||
std::vector<ctx_t> &get_apps();
|
||||
std::string get_app_image(int app_id);
|
||||
const std::vector<ctx_t> &
|
||||
get_apps() const;
|
||||
std::vector<ctx_t> &
|
||||
get_apps();
|
||||
std::string
|
||||
get_app_image(int app_id);
|
||||
|
||||
void terminate();
|
||||
void
|
||||
terminate();
|
||||
|
||||
private:
|
||||
private:
|
||||
int _app_id;
|
||||
|
||||
boost::process::environment _env;
|
||||
|
|
@ -91,18 +98,22 @@ private:
|
|||
file_t _pipe;
|
||||
std::vector<cmd_t>::const_iterator _app_prep_it;
|
||||
std::vector<cmd_t>::const_iterator _app_prep_begin;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Calculate a stable id based on name and image data
|
||||
* @return tuple of id calculated without index (for use if no collision) and one with
|
||||
*/
|
||||
std::tuple<std::string, std::string> calculate_app_id(const std::string &app_name, std::string app_image_path, int index);
|
||||
std::tuple<std::string, std::string>
|
||||
calculate_app_id(const std::string &app_name, std::string app_image_path, int index);
|
||||
|
||||
std::string validate_app_image_path(std::string app_image_path);
|
||||
void refresh(const std::string &file_name);
|
||||
std::optional<proc::proc_t> parse(const std::string &file_name);
|
||||
std::string
|
||||
validate_app_image_path(std::string app_image_path);
|
||||
void
|
||||
refresh(const std::string &file_name);
|
||||
std::optional<proc::proc_t>
|
||||
parse(const std::string &file_name);
|
||||
|
||||
extern proc_t proc;
|
||||
extern proc_t proc;
|
||||
} // namespace proc
|
||||
#endif // SUNSHINE_PROCESS_H
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#include <iterator>
|
||||
|
||||
namespace round_robin_util {
|
||||
template<class V, class T>
|
||||
class it_wrap_t {
|
||||
public:
|
||||
template <class V, class T>
|
||||
class it_wrap_t {
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = V;
|
||||
using difference_type = V;
|
||||
|
|
@ -16,37 +16,42 @@ public:
|
|||
typedef T iterator;
|
||||
typedef std::ptrdiff_t diff_t;
|
||||
|
||||
iterator operator+=(diff_t step) {
|
||||
while(step-- > 0) {
|
||||
iterator
|
||||
operator+=(diff_t step) {
|
||||
while (step-- > 0) {
|
||||
++_this();
|
||||
}
|
||||
|
||||
return _this();
|
||||
}
|
||||
|
||||
iterator operator-=(diff_t step) {
|
||||
while(step-- > 0) {
|
||||
iterator
|
||||
operator-=(diff_t step) {
|
||||
while (step-- > 0) {
|
||||
--_this();
|
||||
}
|
||||
|
||||
return _this();
|
||||
}
|
||||
|
||||
iterator operator+(diff_t step) {
|
||||
iterator
|
||||
operator+(diff_t step) {
|
||||
iterator new_ = _this();
|
||||
|
||||
return new_ += step;
|
||||
}
|
||||
|
||||
iterator operator-(diff_t step) {
|
||||
iterator
|
||||
operator-(diff_t step) {
|
||||
iterator new_ = _this();
|
||||
|
||||
return new_ -= step;
|
||||
}
|
||||
|
||||
diff_t operator-(iterator first) {
|
||||
diff_t
|
||||
operator-(iterator first) {
|
||||
diff_t step = 0;
|
||||
while(first != _this()) {
|
||||
while (first != _this()) {
|
||||
++step;
|
||||
++first;
|
||||
}
|
||||
|
|
@ -54,16 +59,19 @@ public:
|
|||
return step;
|
||||
}
|
||||
|
||||
iterator operator++() {
|
||||
iterator
|
||||
operator++() {
|
||||
_this().inc();
|
||||
return _this();
|
||||
}
|
||||
iterator operator--() {
|
||||
iterator
|
||||
operator--() {
|
||||
_this().dec();
|
||||
return _this();
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
iterator
|
||||
operator++(int) {
|
||||
iterator new_ = _this();
|
||||
|
||||
++_this();
|
||||
|
|
@ -71,7 +79,8 @@ public:
|
|||
return new_;
|
||||
}
|
||||
|
||||
iterator operator--(int) {
|
||||
iterator
|
||||
operator--(int) {
|
||||
iterator new_ = _this();
|
||||
|
||||
--_this();
|
||||
|
|
@ -79,79 +88,97 @@ public:
|
|||
return new_;
|
||||
}
|
||||
|
||||
reference operator*() { return *_this().get(); }
|
||||
const reference operator*() const { return *_this().get(); }
|
||||
reference
|
||||
operator*() { return *_this().get(); }
|
||||
const reference
|
||||
operator*() const { return *_this().get(); }
|
||||
|
||||
pointer operator->() { return &*_this(); }
|
||||
const pointer operator->() const { return &*_this(); }
|
||||
pointer
|
||||
operator->() { return &*_this(); }
|
||||
const pointer
|
||||
operator->() const { return &*_this(); }
|
||||
|
||||
bool operator!=(const iterator &other) const {
|
||||
bool
|
||||
operator!=(const iterator &other) const {
|
||||
return !(_this() == other);
|
||||
}
|
||||
|
||||
bool operator<(const iterator &other) const {
|
||||
bool
|
||||
operator<(const iterator &other) const {
|
||||
return !(_this() >= other);
|
||||
}
|
||||
|
||||
bool operator>=(const iterator &other) const {
|
||||
bool
|
||||
operator>=(const iterator &other) const {
|
||||
return _this() == other || _this() > other;
|
||||
}
|
||||
|
||||
bool operator<=(const iterator &other) const {
|
||||
bool
|
||||
operator<=(const iterator &other) const {
|
||||
return _this() == other || _this() < other;
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const { return _this().eq(other); };
|
||||
bool operator>(const iterator &other) const { return _this().gt(other); }
|
||||
bool
|
||||
operator==(const iterator &other) const { return _this().eq(other); };
|
||||
bool
|
||||
operator>(const iterator &other) const { return _this().gt(other); }
|
||||
|
||||
private:
|
||||
iterator &_this() { return *static_cast<iterator *>(this); }
|
||||
const iterator &_this() const { return *static_cast<const iterator *>(this); }
|
||||
};
|
||||
private:
|
||||
iterator &
|
||||
_this() { return *static_cast<iterator *>(this); }
|
||||
const iterator &
|
||||
_this() const { return *static_cast<const iterator *>(this); }
|
||||
};
|
||||
|
||||
template<class V, class It>
|
||||
class round_robin_t : public it_wrap_t<V, round_robin_t<V, It>> {
|
||||
public:
|
||||
template <class V, class It>
|
||||
class round_robin_t: public it_wrap_t<V, round_robin_t<V, It>> {
|
||||
public:
|
||||
using iterator = It;
|
||||
using pointer = V *;
|
||||
|
||||
round_robin_t(iterator begin, iterator end) : _begin(begin), _end(end), _pos(begin) {}
|
||||
round_robin_t(iterator begin, iterator end):
|
||||
_begin(begin), _end(end), _pos(begin) {}
|
||||
|
||||
void inc() {
|
||||
void
|
||||
inc() {
|
||||
++_pos;
|
||||
|
||||
if(_pos == _end) {
|
||||
if (_pos == _end) {
|
||||
_pos = _begin;
|
||||
}
|
||||
}
|
||||
|
||||
void dec() {
|
||||
if(_pos == _begin) {
|
||||
void
|
||||
dec() {
|
||||
if (_pos == _begin) {
|
||||
_pos = _end;
|
||||
}
|
||||
|
||||
--_pos;
|
||||
}
|
||||
|
||||
bool eq(const round_robin_t &other) const {
|
||||
bool
|
||||
eq(const round_robin_t &other) const {
|
||||
return *_pos == *other._pos;
|
||||
}
|
||||
|
||||
pointer get() const {
|
||||
pointer
|
||||
get() const {
|
||||
return &*_pos;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
It _begin;
|
||||
It _end;
|
||||
|
||||
It _pos;
|
||||
};
|
||||
};
|
||||
|
||||
template<class V, class It>
|
||||
round_robin_t<V, It> make_round_robin(It begin, It end) {
|
||||
template <class V, class It>
|
||||
round_robin_t<V, It>
|
||||
make_round_robin(It begin, It end) {
|
||||
return round_robin_t<V, It>(begin, end);
|
||||
}
|
||||
}
|
||||
} // namespace round_robin_util
|
||||
|
||||
#endif
|
||||
|
|
|
|||
282
src/rtsp.cpp
282
src/rtsp.cpp
|
|
@ -30,28 +30,33 @@ using asio::ip::udp;
|
|||
using namespace std::literals;
|
||||
|
||||
namespace rtsp_stream {
|
||||
void free_msg(PRTSP_MESSAGE msg) {
|
||||
void
|
||||
free_msg(PRTSP_MESSAGE msg) {
|
||||
freeMessage(msg);
|
||||
|
||||
delete msg;
|
||||
}
|
||||
}
|
||||
|
||||
class rtsp_server_t;
|
||||
class rtsp_server_t;
|
||||
|
||||
using msg_t = util::safe_ptr<RTSP_MESSAGE, free_msg>;
|
||||
using cmd_func_t = std::function<void(rtsp_server_t *server, tcp::socket &, msg_t &&)>;
|
||||
using msg_t = util::safe_ptr<RTSP_MESSAGE, free_msg>;
|
||||
using cmd_func_t = std::function<void(rtsp_server_t *server, tcp::socket &, msg_t &&)>;
|
||||
|
||||
void print_msg(PRTSP_MESSAGE msg);
|
||||
void cmd_not_found(tcp::socket &sock, msg_t &&req);
|
||||
void respond(tcp::socket &sock, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload);
|
||||
void
|
||||
print_msg(PRTSP_MESSAGE msg);
|
||||
void
|
||||
cmd_not_found(tcp::socket &sock, msg_t &&req);
|
||||
void
|
||||
respond(tcp::socket &sock, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload);
|
||||
|
||||
class socket_t : public std::enable_shared_from_this<socket_t> {
|
||||
public:
|
||||
socket_t(boost::asio::io_service &ios, std::function<void(tcp::socket &sock, msg_t &&)> &&handle_data_fn)
|
||||
: handle_data_fn { std::move(handle_data_fn) }, sock { ios } {}
|
||||
class socket_t: public std::enable_shared_from_this<socket_t> {
|
||||
public:
|
||||
socket_t(boost::asio::io_service &ios, std::function<void(tcp::socket &sock, msg_t &&)> &&handle_data_fn):
|
||||
handle_data_fn { std::move(handle_data_fn) }, sock { ios } {}
|
||||
|
||||
void read() {
|
||||
if(begin == std::end(msg_buf)) {
|
||||
void
|
||||
read() {
|
||||
if (begin == std::end(msg_buf)) {
|
||||
BOOST_LOG(error) << "RTSP: read(): Exceeded maximum rtsp packet size: "sv << msg_buf.size();
|
||||
|
||||
respond(sock, nullptr, 400, "BAD REQUEST", 0, {});
|
||||
|
|
@ -69,8 +74,9 @@ public:
|
|||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void read_payload() {
|
||||
if(begin == std::end(msg_buf)) {
|
||||
void
|
||||
read_payload() {
|
||||
if (begin == std::end(msg_buf)) {
|
||||
BOOST_LOG(error) << "RTSP: read_payload(): Exceeded maximum rtsp packet size: "sv << msg_buf.size();
|
||||
|
||||
respond(sock, nullptr, 400, "BAD REQUEST", 0, {});
|
||||
|
|
@ -88,19 +94,20 @@ public:
|
|||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
static void handle_payload(std::shared_ptr<socket_t> &socket, const boost::system::error_code &ec, std::size_t bytes) {
|
||||
static void
|
||||
handle_payload(std::shared_ptr<socket_t> &socket, const boost::system::error_code &ec, std::size_t bytes) {
|
||||
BOOST_LOG(debug) << "handle_payload(): Handle read of size: "sv << bytes << " bytes"sv;
|
||||
|
||||
auto sock_close = util::fail_guard([&socket]() {
|
||||
boost::system::error_code ec;
|
||||
socket->sock.close(ec);
|
||||
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "RTSP: handle_payload(): Couldn't close tcp socket: "sv << ec.message();
|
||||
}
|
||||
});
|
||||
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "RTSP: handle_payload(): Couldn't read from tcp socket: "sv << ec.message();
|
||||
|
||||
return;
|
||||
|
|
@ -108,7 +115,7 @@ public:
|
|||
|
||||
auto end = socket->begin + bytes;
|
||||
msg_t req { new msg_t::element_type {} };
|
||||
if(auto status = parseRtspMessage(req.get(), socket->msg_buf.data(), (std::size_t)(end - socket->msg_buf.data()))) {
|
||||
if (auto status = parseRtspMessage(req.get(), socket->msg_buf.data(), (std::size_t)(end - socket->msg_buf.data()))) {
|
||||
BOOST_LOG(error) << "Malformed RTSP message: ["sv << status << ']';
|
||||
|
||||
respond(socket->sock, nullptr, 400, "BAD REQUEST", req->sequenceNumber, {});
|
||||
|
|
@ -122,22 +129,22 @@ public:
|
|||
});
|
||||
|
||||
auto content_length = 0;
|
||||
for(auto option = req->options; option != nullptr; option = option->next) {
|
||||
if("Content-length"sv == option->option) {
|
||||
for (auto option = req->options; option != nullptr; option = option->next) {
|
||||
if ("Content-length"sv == option->option) {
|
||||
BOOST_LOG(debug) << "Found Content-Length: "sv << option->content << " bytes"sv;
|
||||
|
||||
// If content_length > bytes read, then we need to store current data read,
|
||||
// to be appended by the next read.
|
||||
std::string_view content { option->content };
|
||||
auto begin = std::find_if(std::begin(content), std::end(content), [](auto ch) { return (bool)std::isdigit(ch); });
|
||||
auto begin = std::find_if(std::begin(content), std::end(content), [](auto ch) { return (bool) std::isdigit(ch); });
|
||||
|
||||
content_length = util::from_chars(begin, std::end(content));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(end - socket->crlf >= content_length) {
|
||||
if(end - socket->crlf > content_length) {
|
||||
if (end - socket->crlf >= content_length) {
|
||||
if (end - socket->crlf > content_length) {
|
||||
BOOST_LOG(warning) << "(end - socket->crlf) > content_length -- "sv << (std::size_t)(end - socket->crlf) << " > "sv << content_length;
|
||||
}
|
||||
|
||||
|
|
@ -150,16 +157,17 @@ public:
|
|||
socket->begin = end;
|
||||
}
|
||||
|
||||
static void handle_read(std::shared_ptr<socket_t> &socket, const boost::system::error_code &ec, std::size_t bytes) {
|
||||
static void
|
||||
handle_read(std::shared_ptr<socket_t> &socket, const boost::system::error_code &ec, std::size_t bytes) {
|
||||
BOOST_LOG(debug) << "handle_read(): Handle read of size: "sv << bytes << " bytes"sv;
|
||||
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "RTSP: handle_read(): Couldn't read from tcp socket: "sv << ec.message();
|
||||
|
||||
boost::system::error_code ec;
|
||||
socket->sock.close(ec);
|
||||
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "RTSP: handle_read(): Couldn't close tcp socket: "sv << ec.message();
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +185,7 @@ public:
|
|||
constexpr auto needle = "\r\n\r\n"sv;
|
||||
|
||||
auto it = std::search(begin, begin + buf_size, std::begin(needle), std::end(needle));
|
||||
if(it == end) {
|
||||
if (it == end) {
|
||||
socket->begin = end;
|
||||
|
||||
return;
|
||||
|
|
@ -192,7 +200,8 @@ public:
|
|||
handle_payload(socket, ec, buf_size);
|
||||
}
|
||||
|
||||
void handle_data(msg_t &&req) {
|
||||
void
|
||||
handle_data(msg_t &&req) {
|
||||
handle_data_fn(sock, std::move(req));
|
||||
}
|
||||
|
||||
|
|
@ -204,15 +213,16 @@ public:
|
|||
|
||||
char *crlf;
|
||||
char *begin = msg_buf.data();
|
||||
};
|
||||
};
|
||||
|
||||
class rtsp_server_t {
|
||||
public:
|
||||
class rtsp_server_t {
|
||||
public:
|
||||
~rtsp_server_t() {
|
||||
clear();
|
||||
}
|
||||
|
||||
int bind(std::uint16_t port, boost::system::error_code &ec) {
|
||||
int
|
||||
bind(std::uint16_t port, boost::system::error_code &ec) {
|
||||
{
|
||||
auto lg = _session_slots.lock();
|
||||
|
||||
|
|
@ -221,19 +231,19 @@ public:
|
|||
}
|
||||
|
||||
acceptor.open(tcp::v4(), ec);
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
acceptor.set_option(boost::asio::socket_base::reuse_address { true });
|
||||
|
||||
acceptor.bind(tcp::endpoint(tcp::v4(), port), ec);
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
acceptor.listen(4096, ec);
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -248,14 +258,16 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
template<class T, class X>
|
||||
void iterate(std::chrono::duration<T, X> timeout) {
|
||||
template <class T, class X>
|
||||
void
|
||||
iterate(std::chrono::duration<T, X> timeout) {
|
||||
ios.run_one_for(timeout);
|
||||
}
|
||||
|
||||
void handle_msg(tcp::socket &sock, msg_t &&req) {
|
||||
void
|
||||
handle_msg(tcp::socket &sock, msg_t &&req) {
|
||||
auto func = _map_cmd_cb.find(req->message.request.command);
|
||||
if(func != std::end(_map_cmd_cb)) {
|
||||
if (func != std::end(_map_cmd_cb)) {
|
||||
func->second(this, sock, std::move(req));
|
||||
}
|
||||
else {
|
||||
|
|
@ -265,8 +277,9 @@ public:
|
|||
sock.shutdown(boost::asio::socket_base::shutdown_type::shutdown_both);
|
||||
}
|
||||
|
||||
void handle_accept(const boost::system::error_code &ec) {
|
||||
if(ec) {
|
||||
void
|
||||
handle_accept(const boost::system::error_code &ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "Couldn't accept incoming connections: "sv << ec.message();
|
||||
|
||||
// Stop server
|
||||
|
|
@ -286,15 +299,17 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
void map(const std::string_view &type, cmd_func_t cb) {
|
||||
void
|
||||
map(const std::string_view &type, cmd_func_t cb) {
|
||||
_map_cmd_cb.emplace(type, std::move(cb));
|
||||
}
|
||||
|
||||
void session_raise(rtsp_stream::launch_session_t launch_session) {
|
||||
void
|
||||
session_raise(rtsp_stream::launch_session_t launch_session) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
// If a launch event is still pending, don't overwrite it.
|
||||
if(raised_timeout > now && launch_event.peek()) {
|
||||
if (raised_timeout > now && launch_event.peek()) {
|
||||
return;
|
||||
}
|
||||
raised_timeout = now + 10s;
|
||||
|
|
@ -303,25 +318,27 @@ public:
|
|||
launch_event.raise(launch_session);
|
||||
}
|
||||
|
||||
int session_count() const {
|
||||
int
|
||||
session_count() const {
|
||||
return config::stream.channels - _slot_count;
|
||||
}
|
||||
|
||||
safe::event_t<rtsp_stream::launch_session_t> launch_event;
|
||||
|
||||
void clear(bool all = true) {
|
||||
void
|
||||
clear(bool all = true) {
|
||||
// if a launch event timed out --> Remove it.
|
||||
if(raised_timeout < std::chrono::steady_clock::now()) {
|
||||
if (raised_timeout < std::chrono::steady_clock::now()) {
|
||||
auto discarded = launch_event.pop(0s);
|
||||
if(discarded) {
|
||||
if (discarded) {
|
||||
++_slot_count;
|
||||
}
|
||||
}
|
||||
|
||||
auto lg = _session_slots.lock();
|
||||
|
||||
for(auto &slot : *_session_slots) {
|
||||
if(slot && (all || stream::session::state(*slot) == stream::session::state_e::STOPPING)) {
|
||||
for (auto &slot : *_session_slots) {
|
||||
if (slot && (all || stream::session::state(*slot) == stream::session::state_e::STOPPING)) {
|
||||
stream::session::stop(*slot);
|
||||
stream::session::join(*slot);
|
||||
|
||||
|
|
@ -331,12 +348,13 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if(all && !ios.stopped()) {
|
||||
if (all && !ios.stopped()) {
|
||||
ios.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void clear(std::shared_ptr<stream::session_t> *session_p) {
|
||||
void
|
||||
clear(std::shared_ptr<stream::session_t> *session_p) {
|
||||
auto lg = _session_slots.lock();
|
||||
|
||||
session_p->reset();
|
||||
|
|
@ -344,11 +362,12 @@ public:
|
|||
++_slot_count;
|
||||
}
|
||||
|
||||
std::shared_ptr<stream::session_t> *accept(std::shared_ptr<stream::session_t> &session) {
|
||||
std::shared_ptr<stream::session_t> *
|
||||
accept(std::shared_ptr<stream::session_t> &session) {
|
||||
auto lg = _session_slots.lock();
|
||||
|
||||
for(auto &slot : *_session_slots) {
|
||||
if(!slot) {
|
||||
for (auto &slot : *_session_slots) {
|
||||
if (!slot) {
|
||||
slot = session;
|
||||
return &slot;
|
||||
}
|
||||
|
|
@ -357,7 +376,7 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
std::unordered_map<std::string_view, cmd_func_t> _map_cmd_cb;
|
||||
|
||||
sync_util::sync_t<std::vector<std::shared_ptr<stream::session_t>>> _session_slots;
|
||||
|
|
@ -369,38 +388,42 @@ private:
|
|||
tcp::acceptor acceptor { ios };
|
||||
|
||||
std::shared_ptr<socket_t> next_socket;
|
||||
};
|
||||
};
|
||||
|
||||
rtsp_server_t server {};
|
||||
rtsp_server_t server {};
|
||||
|
||||
void launch_session_raise(rtsp_stream::launch_session_t launch_session) {
|
||||
void
|
||||
launch_session_raise(rtsp_stream::launch_session_t launch_session) {
|
||||
server.session_raise(launch_session);
|
||||
}
|
||||
}
|
||||
|
||||
int session_count() {
|
||||
int
|
||||
session_count() {
|
||||
// Ensure session_count is up-to-date
|
||||
server.clear(false);
|
||||
|
||||
return server.session_count();
|
||||
}
|
||||
}
|
||||
|
||||
int send(tcp::socket &sock, const std::string_view &sv) {
|
||||
int
|
||||
send(tcp::socket &sock, const std::string_view &sv) {
|
||||
std::size_t bytes_send = 0;
|
||||
|
||||
while(bytes_send != sv.size()) {
|
||||
while (bytes_send != sv.size()) {
|
||||
boost::system::error_code ec;
|
||||
bytes_send += sock.send(boost::asio::buffer(sv.substr(bytes_send)), 0, ec);
|
||||
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "RTSP: Couldn't send data over tcp socket: "sv << ec.message();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void respond(tcp::socket &sock, msg_t &resp) {
|
||||
void
|
||||
respond(tcp::socket &sock, msg_t &resp) {
|
||||
auto payload = std::make_pair(resp->payload, resp->payloadLength);
|
||||
|
||||
// Restore response message for proper destruction
|
||||
|
|
@ -416,31 +439,34 @@ void respond(tcp::socket &sock, msg_t &resp) {
|
|||
util::c_ptr<char> raw_resp { serializeRtspMessage(resp.get(), &serialized_len) };
|
||||
BOOST_LOG(debug)
|
||||
<< "---Begin Response---"sv << std::endl
|
||||
<< std::string_view { raw_resp.get(), (std::size_t)serialized_len } << std::endl
|
||||
<< std::string_view { payload.first, (std::size_t)payload.second } << std::endl
|
||||
<< std::string_view { raw_resp.get(), (std::size_t) serialized_len } << std::endl
|
||||
<< std::string_view { payload.first, (std::size_t) payload.second } << std::endl
|
||||
<< "---End Response---"sv << std::endl;
|
||||
|
||||
std::string_view tmp_resp { raw_resp.get(), (size_t)serialized_len };
|
||||
std::string_view tmp_resp { raw_resp.get(), (size_t) serialized_len };
|
||||
|
||||
if(send(sock, tmp_resp)) {
|
||||
if (send(sock, tmp_resp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
send(sock, std::string_view { payload.first, (std::size_t)payload.second });
|
||||
}
|
||||
send(sock, std::string_view { payload.first, (std::size_t) payload.second });
|
||||
}
|
||||
|
||||
void respond(tcp::socket &sock, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload) {
|
||||
void
|
||||
respond(tcp::socket &sock, POPTION_ITEM options, int statuscode, const char *status_msg, int seqn, const std::string_view &payload) {
|
||||
msg_t resp { new msg_t::element_type };
|
||||
createRtspResponse(resp.get(), nullptr, 0, const_cast<char *>("RTSP/1.0"), statuscode, const_cast<char *>(status_msg), seqn, options, const_cast<char *>(payload.data()), (int)payload.size());
|
||||
createRtspResponse(resp.get(), nullptr, 0, const_cast<char *>("RTSP/1.0"), statuscode, const_cast<char *>(status_msg), seqn, options, const_cast<char *>(payload.data()), (int) payload.size());
|
||||
|
||||
respond(sock, resp);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_not_found(tcp::socket &sock, msg_t &&req) {
|
||||
void
|
||||
cmd_not_found(tcp::socket &sock, msg_t &&req) {
|
||||
respond(sock, nullptr, 404, "NOT FOUND", req->sequenceNumber, {});
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_option(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
void
|
||||
cmd_option(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
OPTION_ITEM option {};
|
||||
|
||||
// I know these string literals will not be modified
|
||||
|
|
@ -450,9 +476,10 @@ void cmd_option(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
option.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
respond(sock, &option, 200, "OK", req->sequenceNumber, {});
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_describe(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
void
|
||||
cmd_describe(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
OPTION_ITEM option {};
|
||||
|
||||
// I know these string literals will not be modified
|
||||
|
|
@ -462,11 +489,11 @@ void cmd_describe(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
option.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
std::stringstream ss;
|
||||
if(config::video.hevc_mode != 1) {
|
||||
if (config::video.hevc_mode != 1) {
|
||||
ss << "sprop-parameter-sets=AAAAAU"sv << std::endl;
|
||||
}
|
||||
|
||||
for(int x = 0; x < audio::MAX_STREAM_CONFIG; ++x) {
|
||||
for (int x = 0; x < audio::MAX_STREAM_CONFIG; ++x) {
|
||||
auto &stream_config = audio::stream_configs[x];
|
||||
std::uint8_t mapping[platf::speaker::MAX_SPEAKERS];
|
||||
|
||||
|
|
@ -477,7 +504,7 @@ void cmd_describe(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
* as a result, Moonlight rotates all channels from index '3' to the right
|
||||
* To work around this, rotate channels to the left from index '3'
|
||||
*/
|
||||
if(x == audio::SURROUND51 || x == audio::SURROUND71) {
|
||||
if (x == audio::SURROUND51 || x == audio::SURROUND71) {
|
||||
std::copy_n(mapping_p, stream_config.channelCount, mapping);
|
||||
std::rotate(mapping + 3, mapping + 4, mapping + audio::MAX_STREAM_CONFIG);
|
||||
|
||||
|
|
@ -487,16 +514,17 @@ void cmd_describe(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
ss << "a=fmtp:97 surround-params="sv << stream_config.channelCount << stream_config.streams << stream_config.coupledStreams;
|
||||
|
||||
std::for_each_n(mapping_p, stream_config.channelCount, [&ss](std::uint8_t digit) {
|
||||
ss << (char)(digit + '0');
|
||||
ss << (char) (digit + '0');
|
||||
});
|
||||
|
||||
ss << std::endl;
|
||||
}
|
||||
|
||||
respond(sock, &option, 200, "OK", req->sequenceNumber, ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_setup(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
void
|
||||
cmd_setup(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
OPTION_ITEM options[3] {};
|
||||
|
||||
auto &seqn = options[0];
|
||||
|
|
@ -511,16 +539,16 @@ void cmd_setup(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
std::string_view target { req->message.request.target };
|
||||
auto begin = std::find(std::begin(target), std::end(target), '=') + 1;
|
||||
auto end = std::find(begin, std::end(target), '/');
|
||||
std::string_view type { begin, (size_t)std::distance(begin, end) };
|
||||
std::string_view type { begin, (size_t) std::distance(begin, end) };
|
||||
|
||||
std::uint16_t port;
|
||||
if(type == "audio"sv) {
|
||||
if (type == "audio"sv) {
|
||||
port = map_port(stream::AUDIO_STREAM_PORT);
|
||||
}
|
||||
else if(type == "video"sv) {
|
||||
else if (type == "video"sv) {
|
||||
port = map_port(stream::VIDEO_STREAM_PORT);
|
||||
}
|
||||
else if(type == "control"sv) {
|
||||
else if (type == "control"sv) {
|
||||
port = map_port(stream::CONTROL_PORT);
|
||||
}
|
||||
else {
|
||||
|
|
@ -542,11 +570,11 @@ void cmd_setup(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
port_option.option = const_cast<char *>("Transport");
|
||||
port_option.content = port_value.data();
|
||||
|
||||
|
||||
respond(sock, &seqn, 200, "OK", req->sequenceNumber, {});
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
void
|
||||
cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
OPTION_ITEM option {};
|
||||
|
||||
// I know these string literals will not be modified
|
||||
|
|
@ -555,7 +583,7 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
auto seqn_str = std::to_string(req->sequenceNumber);
|
||||
option.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
if(!server->launch_event.peek()) {
|
||||
if (!server->launch_event.peek()) {
|
||||
// /launch has not been used
|
||||
|
||||
respond(sock, &option, 503, "Service Unavailable", req->sequenceNumber, {});
|
||||
|
|
@ -563,7 +591,7 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
}
|
||||
auto launch_session { server->launch_event.pop() };
|
||||
|
||||
std::string_view payload { req->payload, (size_t)req->payloadLength };
|
||||
std::string_view payload { req->payload, (size_t) req->payloadLength };
|
||||
|
||||
std::vector<std::string_view> lines;
|
||||
|
||||
|
|
@ -574,11 +602,11 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
{
|
||||
auto pos = std::begin(payload);
|
||||
auto begin = pos;
|
||||
while(pos != std::end(payload)) {
|
||||
if(whitespace(*pos++)) {
|
||||
while (pos != std::end(payload)) {
|
||||
if (whitespace(*pos++)) {
|
||||
lines.emplace_back(begin, pos - begin - 1);
|
||||
|
||||
while(pos != std::end(payload) && whitespace(*pos)) { ++pos; }
|
||||
while (pos != std::end(payload) && whitespace(*pos)) { ++pos; }
|
||||
begin = pos;
|
||||
}
|
||||
}
|
||||
|
|
@ -587,18 +615,18 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
std::string_view client;
|
||||
std::unordered_map<std::string_view, std::string_view> args;
|
||||
|
||||
for(auto line : lines) {
|
||||
for (auto line : lines) {
|
||||
auto type = line.substr(0, 2);
|
||||
if(type == "s="sv) {
|
||||
if (type == "s="sv) {
|
||||
client = line.substr(2);
|
||||
}
|
||||
else if(type == "a=") {
|
||||
else if (type == "a=") {
|
||||
auto pos = line.find(':');
|
||||
|
||||
auto name = line.substr(2, pos - 2);
|
||||
auto val = line.substr(pos + 1);
|
||||
|
||||
if(val[val.size() - 1] == ' ') {
|
||||
if (val[val.size() - 1] == ' ') {
|
||||
val = val.substr(0, val.size() - 1);
|
||||
}
|
||||
args.emplace(name, val);
|
||||
|
|
@ -644,8 +672,7 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
config.monitor.videoFormat = util::from_view(args.at("x-nv-vqos[0].bitStreamFormat"sv));
|
||||
config.monitor.dynamicRange = util::from_view(args.at("x-nv-video[0].dynamicRangeMode"sv));
|
||||
}
|
||||
catch(std::out_of_range &) {
|
||||
|
||||
catch (std::out_of_range &) {
|
||||
respond(sock, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
|
||||
return;
|
||||
}
|
||||
|
|
@ -653,9 +680,9 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
// When using stereo audio, the audio quality is (strangely) indicated by whether the Host field
|
||||
// in the RTSP message matches a local interface's IP address. Fortunately, Moonlight always sends
|
||||
// 0.0.0.0 when it wants low quality, so it is easy to check without enumerating interfaces.
|
||||
if(config.audio.channels == 2) {
|
||||
for(auto option = req->options; option != nullptr; option = option->next) {
|
||||
if("Host"sv == option->option) {
|
||||
if (config.audio.channels == 2) {
|
||||
for (auto option = req->options; option != nullptr; option = option->next) {
|
||||
if ("Host"sv == option->option) {
|
||||
std::string_view content { option->content };
|
||||
BOOST_LOG(debug) << "Found Host: "sv << content;
|
||||
config.audio.flags[audio::config_t::HIGH_QUALITY] = (content.find("0.0.0.0"sv) == std::string::npos);
|
||||
|
|
@ -663,7 +690,7 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
}
|
||||
}
|
||||
|
||||
if(config.monitor.videoFormat != 0 && config::video.hevc_mode == 1) {
|
||||
if (config.monitor.videoFormat != 0 && config::video.hevc_mode == 1) {
|
||||
BOOST_LOG(warning) << "HEVC is disabled, yet the client requested HEVC"sv;
|
||||
|
||||
respond(sock, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
|
||||
|
|
@ -673,14 +700,14 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
auto session = stream::session::alloc(config, launch_session->gcm_key, launch_session->iv);
|
||||
|
||||
auto slot = server->accept(session);
|
||||
if(!slot) {
|
||||
if (!slot) {
|
||||
BOOST_LOG(info) << "Ran out of slots for client from ["sv << ']';
|
||||
|
||||
respond(sock, &option, 503, "Service Unavailable", req->sequenceNumber, {});
|
||||
return;
|
||||
}
|
||||
|
||||
if(stream::session::start(*session, sock.remote_endpoint().address().to_string())) {
|
||||
if (stream::session::start(*session, sock.remote_endpoint().address().to_string())) {
|
||||
BOOST_LOG(error) << "Failed to start a streaming session"sv;
|
||||
|
||||
server->clear(slot);
|
||||
|
|
@ -689,9 +716,10 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
}
|
||||
|
||||
respond(sock, &option, 200, "OK", req->sequenceNumber, {});
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_play(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
void
|
||||
cmd_play(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
||||
OPTION_ITEM option {};
|
||||
|
||||
// I know these string literals will not be modified
|
||||
|
|
@ -701,9 +729,10 @@ void cmd_play(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) {
|
|||
option.content = const_cast<char *>(seqn_str.c_str());
|
||||
|
||||
respond(sock, &option, 200, "OK", req->sequenceNumber, {});
|
||||
}
|
||||
}
|
||||
|
||||
void rtpThread() {
|
||||
void
|
||||
rtpThread() {
|
||||
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
|
||||
auto broadcast_shutdown_event = mail::man->event<bool>(mail::broadcast_shutdown);
|
||||
|
||||
|
|
@ -715,17 +744,17 @@ void rtpThread() {
|
|||
server.map("PLAY"sv, &cmd_play);
|
||||
|
||||
boost::system::error_code ec;
|
||||
if(server.bind(map_port(rtsp_stream::RTSP_SETUP_PORT), ec)) {
|
||||
if (server.bind(map_port(rtsp_stream::RTSP_SETUP_PORT), ec)) {
|
||||
BOOST_LOG(fatal) << "Couldn't bind RTSP server to port ["sv << map_port(rtsp_stream::RTSP_SETUP_PORT) << "], " << ec.message();
|
||||
shutdown_event->raise(true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while(!shutdown_event->peek()) {
|
||||
while (!shutdown_event->peek()) {
|
||||
server.iterate(std::min(500ms, config::stream.ping_timeout));
|
||||
|
||||
if(broadcast_shutdown_event->peek()) {
|
||||
if (broadcast_shutdown_event->peek()) {
|
||||
server.clear();
|
||||
}
|
||||
else {
|
||||
|
|
@ -735,12 +764,13 @@ void rtpThread() {
|
|||
}
|
||||
|
||||
server.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void print_msg(PRTSP_MESSAGE msg) {
|
||||
void
|
||||
print_msg(PRTSP_MESSAGE msg) {
|
||||
std::string_view type = msg->type == TYPE_RESPONSE ? "RESPONSE"sv : "REQUEST"sv;
|
||||
|
||||
std::string_view payload { msg->payload, (size_t)msg->payloadLength };
|
||||
std::string_view payload { msg->payload, (size_t) msg->payloadLength };
|
||||
std::string_view protocol { msg->protocol };
|
||||
auto seqnm = msg->sequenceNumber;
|
||||
std::string_view messageBuffer { msg->messageBuffer };
|
||||
|
|
@ -750,7 +780,7 @@ void print_msg(PRTSP_MESSAGE msg) {
|
|||
BOOST_LOG(debug) << "protocol :: "sv << protocol;
|
||||
BOOST_LOG(debug) << "payload :: "sv << payload;
|
||||
|
||||
if(msg->type == TYPE_RESPONSE) {
|
||||
if (msg->type == TYPE_RESPONSE) {
|
||||
auto &resp = msg->message.response;
|
||||
|
||||
auto statuscode = resp.statusCode;
|
||||
|
|
@ -769,7 +799,7 @@ void print_msg(PRTSP_MESSAGE msg) {
|
|||
BOOST_LOG(debug) << "target :: "sv << target;
|
||||
}
|
||||
|
||||
for(auto option = msg->options; option != nullptr; option = option->next) {
|
||||
for (auto option = msg->options; option != nullptr; option = option->next) {
|
||||
std::string_view content { option->content };
|
||||
std::string_view name { option->option };
|
||||
|
||||
|
|
@ -779,5 +809,5 @@ void print_msg(PRTSP_MESSAGE msg) {
|
|||
BOOST_LOG(debug) << "---Begin MessageBuffer---"sv << std::endl
|
||||
<< messageBuffer << std::endl
|
||||
<< "---End MessageBuffer---"sv << std::endl;
|
||||
}
|
||||
}
|
||||
} // namespace rtsp_stream
|
||||
|
|
|
|||
15
src/rtsp.h
15
src/rtsp.h
|
|
@ -9,19 +9,22 @@
|
|||
#include "thread_safe.h"
|
||||
|
||||
namespace rtsp_stream {
|
||||
constexpr auto RTSP_SETUP_PORT = 21;
|
||||
constexpr auto RTSP_SETUP_PORT = 21;
|
||||
|
||||
struct launch_session_t {
|
||||
struct launch_session_t {
|
||||
crypto::aes_t gcm_key;
|
||||
crypto::aes_t iv;
|
||||
|
||||
bool host_audio;
|
||||
};
|
||||
};
|
||||
|
||||
void launch_session_raise(launch_session_t launch_session);
|
||||
int session_count();
|
||||
void
|
||||
launch_session_raise(launch_session_t launch_session);
|
||||
int
|
||||
session_count();
|
||||
|
||||
void rtpThread();
|
||||
void
|
||||
rtpThread();
|
||||
|
||||
} // namespace rtsp_stream
|
||||
|
||||
|
|
|
|||
576
src/stream.cpp
576
src/stream.cpp
File diff suppressed because it is too large
Load diff
35
src/stream.h
35
src/stream.h
|
|
@ -10,12 +10,12 @@
|
|||
#include "video.h"
|
||||
|
||||
namespace stream {
|
||||
constexpr auto VIDEO_STREAM_PORT = 9;
|
||||
constexpr auto CONTROL_PORT = 10;
|
||||
constexpr auto AUDIO_STREAM_PORT = 11;
|
||||
constexpr auto VIDEO_STREAM_PORT = 9;
|
||||
constexpr auto CONTROL_PORT = 10;
|
||||
constexpr auto AUDIO_STREAM_PORT = 11;
|
||||
|
||||
struct session_t;
|
||||
struct config_t {
|
||||
struct session_t;
|
||||
struct config_t {
|
||||
audio::config_t audio;
|
||||
video::config_t monitor;
|
||||
|
||||
|
|
@ -27,22 +27,27 @@ struct config_t {
|
|||
int videoQosType;
|
||||
|
||||
std::optional<int> gcmap;
|
||||
};
|
||||
};
|
||||
|
||||
namespace session {
|
||||
enum class state_e : int {
|
||||
namespace session {
|
||||
enum class state_e : int {
|
||||
STOPPED,
|
||||
STOPPING,
|
||||
STARTING,
|
||||
RUNNING,
|
||||
};
|
||||
};
|
||||
|
||||
std::shared_ptr<session_t> alloc(config_t &config, crypto::aes_t &gcm_key, crypto::aes_t &iv);
|
||||
int start(session_t &session, const std::string &addr_string);
|
||||
void stop(session_t &session);
|
||||
void join(session_t &session);
|
||||
state_e state(session_t &session);
|
||||
} // namespace session
|
||||
std::shared_ptr<session_t>
|
||||
alloc(config_t &config, crypto::aes_t &gcm_key, crypto::aes_t &iv);
|
||||
int
|
||||
start(session_t &session, const std::string &addr_string);
|
||||
void
|
||||
stop(session_t &session);
|
||||
void
|
||||
join(session_t &session);
|
||||
state_e
|
||||
state(session_t &session);
|
||||
} // namespace session
|
||||
} // namespace stream
|
||||
|
||||
#endif // SUNSHINE_STREAM_H
|
||||
|
|
|
|||
45
src/sync.h
45
src/sync.h
|
|
@ -9,20 +9,23 @@
|
|||
|
||||
namespace sync_util {
|
||||
|
||||
template<class T, class M = std::mutex>
|
||||
class sync_t {
|
||||
public:
|
||||
template <class T, class M = std::mutex>
|
||||
class sync_t {
|
||||
public:
|
||||
using value_t = T;
|
||||
using mutex_t = M;
|
||||
|
||||
std::lock_guard<mutex_t> lock() {
|
||||
std::lock_guard<mutex_t>
|
||||
lock() {
|
||||
return std::lock_guard { _lock };
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
sync_t(Args &&...args) : raw { std::forward<Args>(args)... } {}
|
||||
template <class... Args>
|
||||
sync_t(Args &&...args):
|
||||
raw { std::forward<Args>(args)... } {}
|
||||
|
||||
sync_t &operator=(sync_t &&other) noexcept {
|
||||
sync_t &
|
||||
operator=(sync_t &&other) noexcept {
|
||||
std::lock(_lock, other._lock);
|
||||
|
||||
raw = std::move(other.raw);
|
||||
|
|
@ -33,7 +36,8 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
sync_t &operator=(sync_t &other) noexcept {
|
||||
sync_t &
|
||||
operator=(sync_t &other) noexcept {
|
||||
std::lock(_lock, other._lock);
|
||||
|
||||
raw = other.raw;
|
||||
|
|
@ -44,8 +48,9 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<class V>
|
||||
sync_t &operator=(V &&val) {
|
||||
template <class V>
|
||||
sync_t &
|
||||
operator=(V &&val) {
|
||||
auto lg = lock();
|
||||
|
||||
raw = val;
|
||||
|
|
@ -53,7 +58,8 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
sync_t &operator=(const value_t &val) noexcept {
|
||||
sync_t &
|
||||
operator=(const value_t &val) noexcept {
|
||||
auto lg = lock();
|
||||
|
||||
raw = val;
|
||||
|
|
@ -61,7 +67,8 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
sync_t &operator=(value_t &&val) noexcept {
|
||||
sync_t &
|
||||
operator=(value_t &&val) noexcept {
|
||||
auto lg = lock();
|
||||
|
||||
raw = std::move(val);
|
||||
|
|
@ -69,25 +76,27 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
value_t *operator->() {
|
||||
value_t *
|
||||
operator->() {
|
||||
return &raw;
|
||||
}
|
||||
|
||||
value_t &operator*() {
|
||||
value_t &
|
||||
operator*() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
const value_t &operator*() const {
|
||||
const value_t &
|
||||
operator*() const {
|
||||
return raw;
|
||||
}
|
||||
|
||||
value_t raw;
|
||||
|
||||
private:
|
||||
private:
|
||||
mutex_t _lock;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace sync_util
|
||||
|
||||
|
||||
#endif // SUNSHINE_SYNC_H
|
||||
|
|
|
|||
|
|
@ -4,73 +4,75 @@
|
|||
// macros
|
||||
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define TRAY_ICON WEB_DIR "images/favicon.ico"
|
||||
#elif defined(__linux__) || defined(linux) || defined(__linux)
|
||||
#define TRAY_ICON "sunshine"
|
||||
#elif defined(__APPLE__) || defined(__MACH__)
|
||||
#define TRAY_ICON WEB_DIR "images/logo-sunshine-16.png"
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define TRAY_ICON WEB_DIR "images/favicon.ico"
|
||||
#elif defined(__linux__) || defined(linux) || defined(__linux)
|
||||
#define TRAY_ICON "sunshine"
|
||||
#elif defined(__APPLE__) || defined(__MACH__)
|
||||
#define TRAY_ICON WEB_DIR "images/logo-sunshine-16.png"
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
// standard includes
|
||||
#include <csignal>
|
||||
#include <string>
|
||||
// standard includes
|
||||
#include <csignal>
|
||||
#include <string>
|
||||
|
||||
// lib includes
|
||||
#include "tray/tray.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/process/environment.hpp>
|
||||
// lib includes
|
||||
#include "tray/tray.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/process/environment.hpp>
|
||||
|
||||
// local includes
|
||||
#include "confighttp.h"
|
||||
#include "main.h"
|
||||
#include "platform/common.h"
|
||||
#include "process.h"
|
||||
// local includes
|
||||
#include "confighttp.h"
|
||||
#include "main.h"
|
||||
#include "platform/common.h"
|
||||
#include "process.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
// system_tray namespace
|
||||
namespace system_tray {
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open a url in the default web browser.
|
||||
* @param url The url to open.
|
||||
*/
|
||||
void open_url(const std::string &url) {
|
||||
void
|
||||
open_url(const std::string &url) {
|
||||
boost::filesystem::path working_dir;
|
||||
|
||||
// if windows
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// set working dir to Windows system directory
|
||||
working_dir = boost::filesystem::path(std::getenv("SystemRoot"));
|
||||
|
||||
// this isn't ideal as it briefly shows a command window
|
||||
// but start a command built into cmd, not an executable
|
||||
std::string cmd = R"(cmd /C "start )" + url + R"(")";
|
||||
#else // if unix
|
||||
#else // if unix
|
||||
// set working dir to user home directory
|
||||
working_dir = boost::filesystem::path(std::getenv("HOME"));
|
||||
std::string cmd = R"(open ")" + url + R"(")";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
boost::process::environment _env = boost::this_process::environment();
|
||||
std::error_code ec;
|
||||
auto child = platf::run_unprivileged(cmd, working_dir, _env, nullptr, ec, nullptr);
|
||||
if(ec) {
|
||||
if (ec) {
|
||||
BOOST_LOG(warning) << "Couldn't open url ["sv << url << "]: System: "sv << ec.message();
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Opened url ["sv << url << "]"sv;
|
||||
child.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Callback for opening the UI from the system tray.
|
||||
* @param item The tray menu item.
|
||||
*/
|
||||
void tray_open_ui_cb(struct tray_menu *item) {
|
||||
void
|
||||
tray_open_ui_cb(struct tray_menu *item) {
|
||||
BOOST_LOG(info) << "Opening UI from system tray"sv;
|
||||
|
||||
// create the url with the port
|
||||
|
|
@ -78,56 +80,61 @@ void tray_open_ui_cb(struct tray_menu *item) {
|
|||
|
||||
// open the url in the default web browser
|
||||
open_url(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Callback for opening GitHub Sponsors from the system tray.
|
||||
* @param item The tray menu item.
|
||||
*/
|
||||
void tray_donate_github_cb(struct tray_menu *item) {
|
||||
void
|
||||
tray_donate_github_cb(struct tray_menu *item) {
|
||||
open_url("https://github.com/sponsors/LizardByte");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Callback for opening MEE6 donation from the system tray.
|
||||
* @param item The tray menu item.
|
||||
*/
|
||||
void tray_donate_mee6_cb(struct tray_menu *item) {
|
||||
void
|
||||
tray_donate_mee6_cb(struct tray_menu *item) {
|
||||
open_url("https://mee6.xyz/m/804382334370578482");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Callback for opening Patreon from the system tray.
|
||||
* @param item The tray menu item.
|
||||
*/
|
||||
void tray_donate_patreon_cb(struct tray_menu *item) {
|
||||
void
|
||||
tray_donate_patreon_cb(struct tray_menu *item) {
|
||||
open_url("https://www.patreon.com/LizardByte");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Callback for opening PayPal donation from the system tray.
|
||||
* @param item The tray menu item.
|
||||
*/
|
||||
void tray_donate_paypal_cb(struct tray_menu *item) {
|
||||
void
|
||||
tray_donate_paypal_cb(struct tray_menu *item) {
|
||||
open_url("https://www.paypal.com/paypalme/ReenigneArcher");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Callback for exiting Sunshine from the system tray.
|
||||
* @param item The tray menu item.
|
||||
*/
|
||||
void tray_quit_cb(struct tray_menu *item) {
|
||||
void
|
||||
tray_quit_cb(struct tray_menu *item) {
|
||||
BOOST_LOG(info) << "Quiting from system tray"sv;
|
||||
|
||||
std::raise(SIGINT);
|
||||
}
|
||||
}
|
||||
|
||||
// Tray menu
|
||||
static struct tray tray = {
|
||||
// Tray menu
|
||||
static struct tray tray = {
|
||||
.icon = TRAY_ICON,
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
.tooltip = const_cast<char *>("Sunshine"), // cast the string literal to a non-const char* pointer
|
||||
#endif
|
||||
#endif
|
||||
.menu =
|
||||
(struct tray_menu[]) {
|
||||
// todo - use boost/locale to translate menu strings
|
||||
|
|
@ -144,16 +151,16 @@ static struct tray tray = {
|
|||
{ .text = "-" },
|
||||
{ .text = "Quit", .cb = tray_quit_cb },
|
||||
{ .text = nullptr } },
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Create the system tray.
|
||||
* @details This function has an endless loop, so it should be run in a separate thread.
|
||||
* @return 1 if the system tray failed to create, otherwise 0 once the tray has been terminated.
|
||||
*/
|
||||
int system_tray() {
|
||||
if(tray_init(&tray) < 0) {
|
||||
int
|
||||
system_tray() {
|
||||
if (tray_init(&tray) < 0) {
|
||||
BOOST_LOG(warning) << "Failed to create system tray"sv;
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -161,20 +168,21 @@ int system_tray() {
|
|||
BOOST_LOG(info) << "System tray created"sv;
|
||||
}
|
||||
|
||||
while(tray_loop(1) == 0) {
|
||||
while (tray_loop(1) == 0) {
|
||||
BOOST_LOG(debug) << "System tray loop"sv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Run the system tray with platform specific options.
|
||||
* @note macOS requires that UI elements be created on the main thread, so the system tray is not implemented for macOS.
|
||||
*/
|
||||
void run_tray() {
|
||||
void
|
||||
run_tray() {
|
||||
// create the system tray
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
// macOS requires that UI elements be created on the main thread
|
||||
// creating tray using dispatch queue does not work, although the code doesn't actually throw any (visible) errors
|
||||
|
||||
|
|
@ -183,21 +191,22 @@ void run_tray() {
|
|||
// });
|
||||
|
||||
BOOST_LOG(info) << "system_tray() is not yet implemented for this platform."sv;
|
||||
#else // Windows, Linux
|
||||
#else // Windows, Linux
|
||||
// create tray in separate thread
|
||||
std::thread tray_thread(system_tray);
|
||||
tray_thread.detach();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Exit the system tray.
|
||||
* @return 0 after exiting the system tray.
|
||||
*/
|
||||
int end_tray() {
|
||||
int
|
||||
end_tray() {
|
||||
tray_exit();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace system_tray
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -7,17 +7,27 @@
|
|||
// system_tray namespace
|
||||
namespace system_tray {
|
||||
|
||||
void open_url(const std::string &url);
|
||||
void tray_open_ui_cb(struct tray_menu *item);
|
||||
void tray_donate_github_cb(struct tray_menu *item);
|
||||
void tray_donate_mee6_cb(struct tray_menu *item);
|
||||
void tray_donate_patreon_cb(struct tray_menu *item);
|
||||
void tray_donate_paypal_cb(struct tray_menu *item);
|
||||
void tray_quit_cb(struct tray_menu *item);
|
||||
void
|
||||
open_url(const std::string &url);
|
||||
void
|
||||
tray_open_ui_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_donate_github_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_donate_mee6_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_donate_patreon_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_donate_paypal_cb(struct tray_menu *item);
|
||||
void
|
||||
tray_quit_cb(struct tray_menu *item);
|
||||
|
||||
int system_tray();
|
||||
int run_tray();
|
||||
int end_tray();
|
||||
int
|
||||
system_tray();
|
||||
int
|
||||
run_tray();
|
||||
int
|
||||
end_tray();
|
||||
|
||||
} // namespace system_tray
|
||||
#endif
|
||||
|
|
|
|||
113
src/task_pool.h
113
src/task_pool.h
|
|
@ -15,62 +15,68 @@
|
|||
#include "utility.h"
|
||||
namespace task_pool_util {
|
||||
|
||||
class _ImplBase {
|
||||
public:
|
||||
class _ImplBase {
|
||||
public:
|
||||
// _unique_base_type _this_ptr;
|
||||
|
||||
inline virtual ~_ImplBase() = default;
|
||||
|
||||
virtual void run() = 0;
|
||||
};
|
||||
virtual void
|
||||
run() = 0;
|
||||
};
|
||||
|
||||
template<class Function>
|
||||
class _Impl : public _ImplBase {
|
||||
template <class Function>
|
||||
class _Impl: public _ImplBase {
|
||||
Function _func;
|
||||
|
||||
public:
|
||||
_Impl(Function &&f) : _func(std::forward<Function>(f)) {}
|
||||
public:
|
||||
_Impl(Function &&f):
|
||||
_func(std::forward<Function>(f)) {}
|
||||
|
||||
void run() override {
|
||||
void
|
||||
run() override {
|
||||
_func();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class TaskPool {
|
||||
public:
|
||||
class TaskPool {
|
||||
public:
|
||||
typedef std::unique_ptr<_ImplBase> __task;
|
||||
typedef _ImplBase *task_id_t;
|
||||
|
||||
|
||||
typedef std::chrono::steady_clock::time_point __time_point;
|
||||
|
||||
template<class R>
|
||||
template <class R>
|
||||
class timer_task_t {
|
||||
public:
|
||||
task_id_t task_id;
|
||||
std::future<R> future;
|
||||
|
||||
timer_task_t(task_id_t task_id, std::future<R> &future) : task_id { task_id }, future { std::move(future) } {}
|
||||
timer_task_t(task_id_t task_id, std::future<R> &future):
|
||||
task_id { task_id }, future { std::move(future) } {}
|
||||
};
|
||||
|
||||
protected:
|
||||
protected:
|
||||
std::deque<__task> _tasks;
|
||||
std::vector<std::pair<__time_point, __task>> _timer_tasks;
|
||||
std::mutex _task_mutex;
|
||||
|
||||
public:
|
||||
public:
|
||||
TaskPool() = default;
|
||||
TaskPool(TaskPool &&other) noexcept : _tasks { std::move(other._tasks) }, _timer_tasks { std::move(other._timer_tasks) } {}
|
||||
TaskPool(TaskPool &&other) noexcept:
|
||||
_tasks { std::move(other._tasks) }, _timer_tasks { std::move(other._timer_tasks) } {}
|
||||
|
||||
TaskPool &operator=(TaskPool &&other) noexcept {
|
||||
TaskPool &
|
||||
operator=(TaskPool &&other) noexcept {
|
||||
std::swap(_tasks, other._tasks);
|
||||
std::swap(_timer_tasks, other._timer_tasks);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Function, class... Args>
|
||||
auto push(Function &&newTask, Args &&...args) {
|
||||
template <class Function, class... Args>
|
||||
auto
|
||||
push(Function &&newTask, Args &&...args) {
|
||||
static_assert(std::is_invocable_v<Function, Args &&...>, "arguments don't match the function");
|
||||
|
||||
using __return = std::invoke_result_t<Function, Args &&...>;
|
||||
|
|
@ -90,12 +96,13 @@ public:
|
|||
return future;
|
||||
}
|
||||
|
||||
void pushDelayed(std::pair<__time_point, __task> &&task) {
|
||||
void
|
||||
pushDelayed(std::pair<__time_point, __task> &&task) {
|
||||
std::lock_guard lg(_task_mutex);
|
||||
|
||||
auto it = _timer_tasks.cbegin();
|
||||
for(; it < _timer_tasks.cend(); ++it) {
|
||||
if(std::get<0>(*it) < task.first) {
|
||||
for (; it < _timer_tasks.cend(); ++it) {
|
||||
if (std::get<0>(*it) < task.first) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -106,15 +113,16 @@ public:
|
|||
/**
|
||||
* @return an id to potentially delay the task
|
||||
*/
|
||||
template<class Function, class X, class Y, class... Args>
|
||||
auto pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&...args) {
|
||||
template <class Function, class X, class Y, class... Args>
|
||||
auto
|
||||
pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&...args) {
|
||||
static_assert(std::is_invocable_v<Function, Args &&...>, "arguments don't match the function");
|
||||
|
||||
using __return = std::invoke_result_t<Function, Args &&...>;
|
||||
using task_t = std::packaged_task<__return()>;
|
||||
|
||||
__time_point time_point;
|
||||
if constexpr(std::is_floating_point_v<X>) {
|
||||
if constexpr (std::is_floating_point_v<X>) {
|
||||
time_point = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
||||
}
|
||||
else {
|
||||
|
|
@ -140,29 +148,30 @@ public:
|
|||
/**
|
||||
* @param duration The delay before executing the task
|
||||
*/
|
||||
template<class X, class Y>
|
||||
void delay(task_id_t task_id, std::chrono::duration<X, Y> duration) {
|
||||
template <class X, class Y>
|
||||
void
|
||||
delay(task_id_t task_id, std::chrono::duration<X, Y> duration) {
|
||||
std::lock_guard<std::mutex> lg(_task_mutex);
|
||||
|
||||
auto it = _timer_tasks.begin();
|
||||
for(; it < _timer_tasks.cend(); ++it) {
|
||||
for (; it < _timer_tasks.cend(); ++it) {
|
||||
const __task &task = std::get<1>(*it);
|
||||
|
||||
if(&*task == task_id) {
|
||||
if (&*task == task_id) {
|
||||
std::get<0>(*it) = std::chrono::steady_clock::now() + duration;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(it == _timer_tasks.cend()) {
|
||||
if (it == _timer_tasks.cend()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// smaller time goes to the back
|
||||
auto prev = it - 1;
|
||||
while(it > _timer_tasks.cbegin()) {
|
||||
if(std::get<0>(*it) > std::get<0>(*prev)) {
|
||||
while (it > _timer_tasks.cbegin()) {
|
||||
if (std::get<0>(*it) > std::get<0>(*prev)) {
|
||||
std::swap(*it, *prev);
|
||||
}
|
||||
|
||||
|
|
@ -171,14 +180,15 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool cancel(task_id_t task_id) {
|
||||
bool
|
||||
cancel(task_id_t task_id) {
|
||||
std::lock_guard lg(_task_mutex);
|
||||
|
||||
auto it = _timer_tasks.begin();
|
||||
for(; it < _timer_tasks.cend(); ++it) {
|
||||
for (; it < _timer_tasks.cend(); ++it) {
|
||||
const __task &task = std::get<1>(*it);
|
||||
|
||||
if(&*task == task_id) {
|
||||
if (&*task == task_id) {
|
||||
_timer_tasks.erase(it);
|
||||
|
||||
return true;
|
||||
|
|
@ -188,28 +198,30 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::pair<__time_point, __task>> pop(task_id_t task_id) {
|
||||
std::optional<std::pair<__time_point, __task>>
|
||||
pop(task_id_t task_id) {
|
||||
std::lock_guard lg(_task_mutex);
|
||||
|
||||
auto pos = std::find_if(std::begin(_timer_tasks), std::end(_timer_tasks), [&task_id](const auto &t) { return t.second.get() == task_id; });
|
||||
|
||||
if(pos == std::end(_timer_tasks)) {
|
||||
if (pos == std::end(_timer_tasks)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::move(*pos);
|
||||
}
|
||||
|
||||
std::optional<__task> pop() {
|
||||
std::optional<__task>
|
||||
pop() {
|
||||
std::lock_guard lg(_task_mutex);
|
||||
|
||||
if(!_tasks.empty()) {
|
||||
if (!_tasks.empty()) {
|
||||
__task task = std::move(_tasks.front());
|
||||
_tasks.pop_front();
|
||||
return std::move(task);
|
||||
}
|
||||
|
||||
if(!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()) {
|
||||
if (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()) {
|
||||
__task task = std::move(std::get<1>(_timer_tasks.back()));
|
||||
_timer_tasks.pop_back();
|
||||
|
||||
|
|
@ -219,27 +231,30 @@ public:
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool ready() {
|
||||
bool
|
||||
ready() {
|
||||
std::lock_guard<std::mutex> lg(_task_mutex);
|
||||
|
||||
return !_tasks.empty() || (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now());
|
||||
}
|
||||
|
||||
std::optional<__time_point> next() {
|
||||
std::optional<__time_point>
|
||||
next() {
|
||||
std::lock_guard<std::mutex> lg(_task_mutex);
|
||||
|
||||
if(_timer_tasks.empty()) {
|
||||
if (_timer_tasks.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::get<0>(_timer_tasks.back());
|
||||
}
|
||||
|
||||
private:
|
||||
template<class Function>
|
||||
std::unique_ptr<_ImplBase> toRunnable(Function &&f) {
|
||||
private:
|
||||
template <class Function>
|
||||
std::unique_ptr<_ImplBase>
|
||||
toRunnable(Function &&f) {
|
||||
return std::make_unique<_Impl<Function>>(std::forward<Function &&>(f));
|
||||
}
|
||||
};
|
||||
};
|
||||
} // namespace task_pool_util
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
#include <thread>
|
||||
|
||||
namespace thread_pool_util {
|
||||
/*
|
||||
/*
|
||||
* Allow threads to execute unhindered
|
||||
* while keeping full control over the threads.
|
||||
*/
|
||||
class ThreadPool : public task_pool_util::TaskPool {
|
||||
public:
|
||||
class ThreadPool: public task_pool_util::TaskPool {
|
||||
public:
|
||||
typedef TaskPool::__task __task;
|
||||
|
||||
private:
|
||||
private:
|
||||
std::vector<std::thread> _thread;
|
||||
|
||||
std::condition_variable _cv;
|
||||
|
|
@ -21,24 +21,27 @@ private:
|
|||
|
||||
bool _continue;
|
||||
|
||||
public:
|
||||
ThreadPool() : _continue { false } {}
|
||||
public:
|
||||
ThreadPool():
|
||||
_continue { false } {}
|
||||
|
||||
explicit ThreadPool(int threads) : _thread(threads), _continue { true } {
|
||||
for(auto &t : _thread) {
|
||||
explicit ThreadPool(int threads):
|
||||
_thread(threads), _continue { true } {
|
||||
for (auto &t : _thread) {
|
||||
t = std::thread(&ThreadPool::_main, this);
|
||||
}
|
||||
}
|
||||
|
||||
~ThreadPool() noexcept {
|
||||
if(!_continue) return;
|
||||
if (!_continue) return;
|
||||
|
||||
stop();
|
||||
join();
|
||||
}
|
||||
|
||||
template<class Function, class... Args>
|
||||
auto push(Function &&newTask, Args &&...args) {
|
||||
template <class Function, class... Args>
|
||||
auto
|
||||
push(Function &&newTask, Args &&...args) {
|
||||
std::lock_guard lg(_lock);
|
||||
auto future = TaskPool::push(std::forward<Function>(newTask), std::forward<Args>(args)...);
|
||||
|
||||
|
|
@ -46,14 +49,16 @@ public:
|
|||
return future;
|
||||
}
|
||||
|
||||
void pushDelayed(std::pair<__time_point, __task> &&task) {
|
||||
void
|
||||
pushDelayed(std::pair<__time_point, __task> &&task) {
|
||||
std::lock_guard lg(_lock);
|
||||
|
||||
TaskPool::pushDelayed(std::move(task));
|
||||
}
|
||||
|
||||
template<class Function, class X, class Y, class... Args>
|
||||
auto pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&...args) {
|
||||
template <class Function, class X, class Y, class... Args>
|
||||
auto
|
||||
pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&...args) {
|
||||
std::lock_guard lg(_lock);
|
||||
auto future = TaskPool::pushDelayed(std::forward<Function>(newTask), duration, std::forward<Args>(args)...);
|
||||
|
||||
|
|
@ -62,47 +67,51 @@ public:
|
|||
return future;
|
||||
}
|
||||
|
||||
void start(int threads) {
|
||||
void
|
||||
start(int threads) {
|
||||
_continue = true;
|
||||
|
||||
_thread.resize(threads);
|
||||
|
||||
for(auto &t : _thread) {
|
||||
for (auto &t : _thread) {
|
||||
t = std::thread(&ThreadPool::_main, this);
|
||||
}
|
||||
}
|
||||
|
||||
void stop() {
|
||||
void
|
||||
stop() {
|
||||
std::lock_guard lg(_lock);
|
||||
|
||||
_continue = false;
|
||||
_cv.notify_all();
|
||||
}
|
||||
|
||||
void join() {
|
||||
for(auto &t : _thread) {
|
||||
void
|
||||
join() {
|
||||
for (auto &t : _thread) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void _main() {
|
||||
while(_continue) {
|
||||
if(auto task = this->pop()) {
|
||||
public:
|
||||
void
|
||||
_main() {
|
||||
while (_continue) {
|
||||
if (auto task = this->pop()) {
|
||||
(*task)->run();
|
||||
}
|
||||
else {
|
||||
std::unique_lock uniq_lock(_lock);
|
||||
|
||||
if(ready()) {
|
||||
if (ready()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(auto tp = next()) {
|
||||
if (auto tp = next()) {
|
||||
_cv.wait_until(uniq_lock, *tp);
|
||||
}
|
||||
else {
|
||||
|
|
@ -112,10 +121,10 @@ public:
|
|||
}
|
||||
|
||||
// Execute remaining tasks
|
||||
while(auto task = this->pop()) {
|
||||
while (auto task = this->pop()) {
|
||||
(*task)->run();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
} // namespace thread_pool_util
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,19 +13,20 @@
|
|||
#include "utility.h"
|
||||
|
||||
namespace safe {
|
||||
template<class T>
|
||||
class event_t {
|
||||
public:
|
||||
template <class T>
|
||||
class event_t {
|
||||
public:
|
||||
using status_t = util::optional_t<T>;
|
||||
|
||||
template<class... Args>
|
||||
void raise(Args &&...args) {
|
||||
template <class... Args>
|
||||
void
|
||||
raise(Args &&...args) {
|
||||
std::lock_guard lg { _lock };
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr(std::is_same_v<std::optional<T>, status_t>) {
|
||||
if constexpr (std::is_same_v<std::optional<T>, status_t>) {
|
||||
_status = std::make_optional<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
else {
|
||||
|
|
@ -36,17 +37,18 @@ public:
|
|||
}
|
||||
|
||||
// pop and view shoud not be used interchangeably
|
||||
status_t pop() {
|
||||
status_t
|
||||
pop() {
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while(!_status) {
|
||||
while (!_status) {
|
||||
_cv.wait(ul);
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
|
|
@ -57,16 +59,17 @@ public:
|
|||
}
|
||||
|
||||
// pop and view shoud not be used interchangeably
|
||||
template<class Rep, class Period>
|
||||
status_t pop(std::chrono::duration<Rep, Period> delay) {
|
||||
template <class Rep, class Period>
|
||||
status_t
|
||||
pop(std::chrono::duration<Rep, Period> delay) {
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while(!_status) {
|
||||
if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
||||
while (!_status) {
|
||||
if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
|
|
@ -77,17 +80,18 @@ public:
|
|||
}
|
||||
|
||||
// pop and view shoud not be used interchangeably
|
||||
const status_t &view() {
|
||||
const status_t &
|
||||
view() {
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while(!_status) {
|
||||
while (!_status) {
|
||||
_cv.wait(ul);
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
|
|
@ -95,11 +99,13 @@ public:
|
|||
return _status;
|
||||
}
|
||||
|
||||
bool peek() {
|
||||
return _continue && (bool)_status;
|
||||
bool
|
||||
peek() {
|
||||
return _continue && (bool) _status;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
void
|
||||
stop() {
|
||||
std::lock_guard lg { _lock };
|
||||
|
||||
_continue = false;
|
||||
|
|
@ -107,7 +113,8 @@ public:
|
|||
_cv.notify_all();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
void
|
||||
reset() {
|
||||
std::lock_guard lg { _lock };
|
||||
|
||||
_continue = true;
|
||||
|
|
@ -115,121 +122,137 @@ public:
|
|||
_status = util::false_v<status_t>;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool running() const {
|
||||
[[nodiscard]] bool
|
||||
running() const {
|
||||
return _continue;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
bool _continue { true };
|
||||
status_t _status { util::false_v<status_t> };
|
||||
|
||||
std::condition_variable _cv;
|
||||
std::mutex _lock;
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class alarm_raw_t {
|
||||
public:
|
||||
template <class T>
|
||||
class alarm_raw_t {
|
||||
public:
|
||||
using status_t = util::optional_t<T>;
|
||||
|
||||
alarm_raw_t() : _status { util::false_v<status_t> } {}
|
||||
alarm_raw_t():
|
||||
_status { util::false_v<status_t> } {}
|
||||
|
||||
void ring(const status_t &status) {
|
||||
void
|
||||
ring(const status_t &status) {
|
||||
std::lock_guard lg(_lock);
|
||||
|
||||
_status = status;
|
||||
_cv.notify_one();
|
||||
}
|
||||
|
||||
void ring(status_t &&status) {
|
||||
void
|
||||
ring(status_t &&status) {
|
||||
std::lock_guard lg(_lock);
|
||||
|
||||
_status = std::move(status);
|
||||
_cv.notify_one();
|
||||
}
|
||||
|
||||
template<class Rep, class Period>
|
||||
auto wait_for(const std::chrono::duration<Rep, Period> &rel_time) {
|
||||
template <class Rep, class Period>
|
||||
auto
|
||||
wait_for(const std::chrono::duration<Rep, Period> &rel_time) {
|
||||
std::unique_lock ul(_lock);
|
||||
|
||||
return _cv.wait_for(ul, rel_time, [this]() { return (bool)status(); });
|
||||
return _cv.wait_for(ul, rel_time, [this]() { return (bool) status(); });
|
||||
}
|
||||
|
||||
template<class Rep, class Period, class Pred>
|
||||
auto wait_for(const std::chrono::duration<Rep, Period> &rel_time, Pred &&pred) {
|
||||
template <class Rep, class Period, class Pred>
|
||||
auto
|
||||
wait_for(const std::chrono::duration<Rep, Period> &rel_time, Pred &&pred) {
|
||||
std::unique_lock ul(_lock);
|
||||
|
||||
return _cv.wait_for(ul, rel_time, [this, &pred]() { return (bool)status() || pred(); });
|
||||
return _cv.wait_for(ul, rel_time, [this, &pred]() { return (bool) status() || pred(); });
|
||||
}
|
||||
|
||||
template<class Rep, class Period>
|
||||
auto wait_until(const std::chrono::duration<Rep, Period> &rel_time) {
|
||||
template <class Rep, class Period>
|
||||
auto
|
||||
wait_until(const std::chrono::duration<Rep, Period> &rel_time) {
|
||||
std::unique_lock ul(_lock);
|
||||
|
||||
return _cv.wait_until(ul, rel_time, [this]() { return (bool)status(); });
|
||||
return _cv.wait_until(ul, rel_time, [this]() { return (bool) status(); });
|
||||
}
|
||||
|
||||
template<class Rep, class Period, class Pred>
|
||||
auto wait_until(const std::chrono::duration<Rep, Period> &rel_time, Pred &&pred) {
|
||||
template <class Rep, class Period, class Pred>
|
||||
auto
|
||||
wait_until(const std::chrono::duration<Rep, Period> &rel_time, Pred &&pred) {
|
||||
std::unique_lock ul(_lock);
|
||||
|
||||
return _cv.wait_until(ul, rel_time, [this, &pred]() { return (bool)status() || pred(); });
|
||||
return _cv.wait_until(ul, rel_time, [this, &pred]() { return (bool) status() || pred(); });
|
||||
}
|
||||
|
||||
auto wait() {
|
||||
auto
|
||||
wait() {
|
||||
std::unique_lock ul(_lock);
|
||||
_cv.wait(ul, [this]() { return (bool)status(); });
|
||||
_cv.wait(ul, [this]() { return (bool) status(); });
|
||||
}
|
||||
|
||||
template<class Pred>
|
||||
auto wait(Pred &&pred) {
|
||||
template <class Pred>
|
||||
auto
|
||||
wait(Pred &&pred) {
|
||||
std::unique_lock ul(_lock);
|
||||
_cv.wait(ul, [this, &pred]() { return (bool)status() || pred(); });
|
||||
_cv.wait(ul, [this, &pred]() { return (bool) status() || pred(); });
|
||||
}
|
||||
|
||||
const status_t &status() const {
|
||||
const status_t &
|
||||
status() const {
|
||||
return _status;
|
||||
}
|
||||
|
||||
status_t &status() {
|
||||
status_t &
|
||||
status() {
|
||||
return _status;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
void
|
||||
reset() {
|
||||
_status = status_t {};
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
std::mutex _lock;
|
||||
std::condition_variable _cv;
|
||||
|
||||
status_t _status;
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using alarm_t = std::shared_ptr<alarm_raw_t<T>>;
|
||||
template <class T>
|
||||
using alarm_t = std::shared_ptr<alarm_raw_t<T>>;
|
||||
|
||||
template<class T>
|
||||
alarm_t<T> make_alarm() {
|
||||
template <class T>
|
||||
alarm_t<T>
|
||||
make_alarm() {
|
||||
return std::make_shared<alarm_raw_t<T>>();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
class queue_t {
|
||||
public:
|
||||
template <class T>
|
||||
class queue_t {
|
||||
public:
|
||||
using status_t = util::optional_t<T>;
|
||||
|
||||
queue_t(std::uint32_t max_elements = 32) : _max_elements { max_elements } {}
|
||||
queue_t(std::uint32_t max_elements = 32):
|
||||
_max_elements { max_elements } {}
|
||||
|
||||
template<class... Args>
|
||||
void raise(Args &&...args) {
|
||||
template <class... Args>
|
||||
void
|
||||
raise(Args &&...args) {
|
||||
std::lock_guard ul { _lock };
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_queue.size() == _max_elements) {
|
||||
if (_queue.size() == _max_elements) {
|
||||
_queue.clear();
|
||||
}
|
||||
|
||||
|
|
@ -238,20 +261,22 @@ public:
|
|||
_cv.notify_all();
|
||||
}
|
||||
|
||||
bool peek() {
|
||||
bool
|
||||
peek() {
|
||||
return _continue && !_queue.empty();
|
||||
}
|
||||
|
||||
template<class Rep, class Period>
|
||||
status_t pop(std::chrono::duration<Rep, Period> delay) {
|
||||
template <class Rep, class Period>
|
||||
status_t
|
||||
pop(std::chrono::duration<Rep, Period> delay) {
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while(_queue.empty()) {
|
||||
if(!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
||||
while (_queue.empty()) {
|
||||
if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
|
|
@ -262,17 +287,18 @@ public:
|
|||
return val;
|
||||
}
|
||||
|
||||
status_t pop() {
|
||||
status_t
|
||||
pop() {
|
||||
std::unique_lock ul { _lock };
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
|
||||
while(_queue.empty()) {
|
||||
while (_queue.empty()) {
|
||||
_cv.wait(ul);
|
||||
|
||||
if(!_continue) {
|
||||
if (!_continue) {
|
||||
return util::false_v<status_t>;
|
||||
}
|
||||
}
|
||||
|
|
@ -283,11 +309,13 @@ public:
|
|||
return val;
|
||||
}
|
||||
|
||||
std::vector<T> &unsafe() {
|
||||
std::vector<T> &
|
||||
unsafe() {
|
||||
return _queue;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
void
|
||||
stop() {
|
||||
std::lock_guard lg { _lock };
|
||||
|
||||
_continue = false;
|
||||
|
|
@ -295,11 +323,12 @@ public:
|
|||
_cv.notify_all();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool running() const {
|
||||
[[nodiscard]] bool
|
||||
running() const {
|
||||
return _continue;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
bool _continue { true };
|
||||
std::uint32_t _max_elements;
|
||||
|
||||
|
|
@ -307,11 +336,11 @@ private:
|
|||
std::condition_variable _cv;
|
||||
|
||||
std::vector<T> _queue;
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class shared_t {
|
||||
public:
|
||||
template <class T>
|
||||
class shared_t {
|
||||
public:
|
||||
using element_type = T;
|
||||
|
||||
using construct_f = std::function<int(element_type &)>;
|
||||
|
|
@ -320,15 +349,19 @@ public:
|
|||
struct ptr_t {
|
||||
shared_t *owner;
|
||||
|
||||
ptr_t() : owner { nullptr } {}
|
||||
explicit ptr_t(shared_t *owner) : owner { owner } {}
|
||||
ptr_t():
|
||||
owner { nullptr } {}
|
||||
explicit ptr_t(shared_t *owner):
|
||||
owner { owner } {}
|
||||
|
||||
ptr_t(ptr_t &&ptr) noexcept : owner { ptr.owner } {
|
||||
ptr_t(ptr_t &&ptr) noexcept:
|
||||
owner { ptr.owner } {
|
||||
ptr.owner = nullptr;
|
||||
}
|
||||
|
||||
ptr_t(const ptr_t &ptr) noexcept : owner { ptr.owner } {
|
||||
if(!owner) {
|
||||
ptr_t(const ptr_t &ptr) noexcept:
|
||||
owner { ptr.owner } {
|
||||
if (!owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -336,8 +369,9 @@ public:
|
|||
tmp.owner = nullptr;
|
||||
}
|
||||
|
||||
ptr_t &operator=(const ptr_t &ptr) noexcept {
|
||||
if(!ptr.owner) {
|
||||
ptr_t &
|
||||
operator=(const ptr_t &ptr) noexcept {
|
||||
if (!ptr.owner) {
|
||||
release();
|
||||
|
||||
return *this;
|
||||
|
|
@ -346,8 +380,9 @@ public:
|
|||
return *this = std::move(*ptr.owner->ref());
|
||||
}
|
||||
|
||||
ptr_t &operator=(ptr_t &&ptr) noexcept {
|
||||
if(owner) {
|
||||
ptr_t &
|
||||
operator=(ptr_t &&ptr) noexcept {
|
||||
if (owner) {
|
||||
release();
|
||||
}
|
||||
|
||||
|
|
@ -357,7 +392,7 @@ public:
|
|||
}
|
||||
|
||||
~ptr_t() {
|
||||
if(owner) {
|
||||
if (owner) {
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
|
@ -366,10 +401,11 @@ public:
|
|||
return owner != nullptr;
|
||||
}
|
||||
|
||||
void release() {
|
||||
void
|
||||
release() {
|
||||
std::lock_guard lg { owner->_lock };
|
||||
|
||||
if(!--owner->_count) {
|
||||
if (!--owner->_count) {
|
||||
owner->_destruct(*get());
|
||||
(*this)->~element_type();
|
||||
}
|
||||
|
|
@ -377,23 +413,27 @@ public:
|
|||
owner = nullptr;
|
||||
}
|
||||
|
||||
element_type *get() const {
|
||||
element_type *
|
||||
get() const {
|
||||
return reinterpret_cast<element_type *>(owner->_object_buf.data());
|
||||
}
|
||||
|
||||
element_type *operator->() {
|
||||
element_type *
|
||||
operator->() {
|
||||
return reinterpret_cast<element_type *>(owner->_object_buf.data());
|
||||
}
|
||||
};
|
||||
|
||||
template<class FC, class FD>
|
||||
shared_t(FC &&fc, FD &&fd) : _construct { std::forward<FC>(fc) }, _destruct { std::forward<FD>(fd) } {}
|
||||
[[nodiscard]] ptr_t ref() {
|
||||
template <class FC, class FD>
|
||||
shared_t(FC &&fc, FD &&fd):
|
||||
_construct { std::forward<FC>(fc) }, _destruct { std::forward<FD>(fd) } {}
|
||||
[[nodiscard]] ptr_t
|
||||
ref() {
|
||||
std::lock_guard lg { _lock };
|
||||
|
||||
if(!_count) {
|
||||
new(_object_buf.data()) element_type;
|
||||
if(_construct(*reinterpret_cast<element_type *>(_object_buf.data()))) {
|
||||
if (!_count) {
|
||||
new (_object_buf.data()) element_type;
|
||||
if (_construct(*reinterpret_cast<element_type *>(_object_buf.data()))) {
|
||||
return ptr_t { nullptr };
|
||||
}
|
||||
}
|
||||
|
|
@ -403,7 +443,7 @@ public:
|
|||
return ptr_t { this };
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
construct_f _construct;
|
||||
destruct_f _destruct;
|
||||
|
||||
|
|
@ -411,53 +451,58 @@ private:
|
|||
|
||||
std::uint32_t _count;
|
||||
std::mutex _lock;
|
||||
};
|
||||
};
|
||||
|
||||
template<class T, class F_Construct, class F_Destruct>
|
||||
auto make_shared(F_Construct &&fc, F_Destruct &&fd) {
|
||||
template <class T, class F_Construct, class F_Destruct>
|
||||
auto
|
||||
make_shared(F_Construct &&fc, F_Destruct &&fd) {
|
||||
return shared_t<T> {
|
||||
std::forward<F_Construct>(fc), std::forward<F_Destruct>(fd)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using signal_t = event_t<bool>;
|
||||
using signal_t = event_t<bool>;
|
||||
|
||||
class mail_raw_t;
|
||||
using mail_t = std::shared_ptr<mail_raw_t>;
|
||||
class mail_raw_t;
|
||||
using mail_t = std::shared_ptr<mail_raw_t>;
|
||||
|
||||
void cleanup(mail_raw_t *);
|
||||
template<class T>
|
||||
class post_t : public T {
|
||||
public:
|
||||
template<class... Args>
|
||||
post_t(mail_t mail, Args &&...args) : T(std::forward<Args>(args)...), mail { std::move(mail) } {}
|
||||
void
|
||||
cleanup(mail_raw_t *);
|
||||
template <class T>
|
||||
class post_t: public T {
|
||||
public:
|
||||
template <class... Args>
|
||||
post_t(mail_t mail, Args &&...args):
|
||||
T(std::forward<Args>(args)...), mail { std::move(mail) } {}
|
||||
|
||||
mail_t mail;
|
||||
|
||||
~post_t() {
|
||||
cleanup(mail.get());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline auto lock(const std::weak_ptr<void> &wp) {
|
||||
template <class T>
|
||||
inline auto
|
||||
lock(const std::weak_ptr<void> &wp) {
|
||||
return std::reinterpret_pointer_cast<typename T::element_type>(wp.lock());
|
||||
}
|
||||
}
|
||||
|
||||
class mail_raw_t : public std::enable_shared_from_this<mail_raw_t> {
|
||||
public:
|
||||
template<class T>
|
||||
class mail_raw_t: public std::enable_shared_from_this<mail_raw_t> {
|
||||
public:
|
||||
template <class T>
|
||||
using event_t = std::shared_ptr<post_t<event_t<T>>>;
|
||||
|
||||
template<class T>
|
||||
template <class T>
|
||||
using queue_t = std::shared_ptr<post_t<queue_t<T>>>;
|
||||
|
||||
template<class T>
|
||||
event_t<T> event(const std::string_view &id) {
|
||||
template <class T>
|
||||
event_t<T>
|
||||
event(const std::string_view &id) {
|
||||
std::lock_guard lg { mutex };
|
||||
|
||||
auto it = id_to_post.find(id);
|
||||
if(it != std::end(id_to_post)) {
|
||||
if (it != std::end(id_to_post)) {
|
||||
return lock<event_t<T>>(it->second);
|
||||
}
|
||||
|
||||
|
|
@ -467,12 +512,13 @@ public:
|
|||
return post;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
queue_t<T> queue(const std::string_view &id) {
|
||||
template <class T>
|
||||
queue_t<T>
|
||||
queue(const std::string_view &id) {
|
||||
std::lock_guard lg { mutex };
|
||||
|
||||
auto it = id_to_post.find(id);
|
||||
if(it != std::end(id_to_post)) {
|
||||
if (it != std::end(id_to_post)) {
|
||||
return lock<queue_t<T>>(it->second);
|
||||
}
|
||||
|
||||
|
|
@ -482,13 +528,14 @@ public:
|
|||
return post;
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
void
|
||||
cleanup() {
|
||||
std::lock_guard lg { mutex };
|
||||
|
||||
for(auto it = std::begin(id_to_post); it != std::end(id_to_post); ++it) {
|
||||
for (auto it = std::begin(id_to_post); it != std::end(id_to_post); ++it) {
|
||||
auto &weak = it->second;
|
||||
|
||||
if(weak.expired()) {
|
||||
if (weak.expired()) {
|
||||
id_to_post.erase(it);
|
||||
|
||||
return;
|
||||
|
|
@ -499,11 +546,12 @@ public:
|
|||
std::mutex mutex;
|
||||
|
||||
std::map<std::string, std::weak_ptr<void>, std::less<>> id_to_post;
|
||||
};
|
||||
};
|
||||
|
||||
inline void cleanup(mail_raw_t *mail) {
|
||||
inline void
|
||||
cleanup(mail_raw_t *mail) {
|
||||
mail->cleanup();
|
||||
}
|
||||
}
|
||||
} // namespace safe
|
||||
|
||||
#endif // SUNSHINE_THREAD_SAFE_H
|
||||
|
|
|
|||
68
src/upnp.cpp
68
src/upnp.cpp
|
|
@ -14,18 +14,18 @@
|
|||
using namespace std::literals;
|
||||
|
||||
namespace upnp {
|
||||
constexpr auto INET6_ADDRESS_STRLEN = 46;
|
||||
constexpr auto INET6_ADDRESS_STRLEN = 46;
|
||||
|
||||
constexpr auto IPv4 = 0;
|
||||
constexpr auto IPv6 = 1;
|
||||
constexpr auto IPv4 = 0;
|
||||
constexpr auto IPv6 = 1;
|
||||
|
||||
using device_t = util::safe_ptr<UPNPDev, freeUPNPDevlist>;
|
||||
using device_t = util::safe_ptr<UPNPDev, freeUPNPDevlist>;
|
||||
|
||||
KITTY_USING_MOVE_T(urls_t, UPNPUrls, , {
|
||||
KITTY_USING_MOVE_T(urls_t, UPNPUrls, , {
|
||||
FreeUPNPUrls(&el);
|
||||
});
|
||||
});
|
||||
|
||||
struct mapping_t {
|
||||
struct mapping_t {
|
||||
struct {
|
||||
std::string wan;
|
||||
std::string lan;
|
||||
|
|
@ -33,17 +33,17 @@ struct mapping_t {
|
|||
|
||||
std::string description;
|
||||
bool tcp;
|
||||
};
|
||||
};
|
||||
|
||||
void unmap(
|
||||
void
|
||||
unmap(
|
||||
const urls_t &urls,
|
||||
const IGDdatas &data,
|
||||
std::vector<mapping_t>::const_reverse_iterator begin,
|
||||
std::vector<mapping_t>::const_reverse_iterator end) {
|
||||
|
||||
BOOST_LOG(debug) << "Unmapping UPNP ports"sv;
|
||||
|
||||
for(auto it = begin; it != end; ++it) {
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
auto status = UPNP_DeletePortMapping(
|
||||
urls->controlURL,
|
||||
data.first.servicetype,
|
||||
|
|
@ -51,18 +51,18 @@ void unmap(
|
|||
it->tcp ? "TCP" : "UDP",
|
||||
nullptr);
|
||||
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(warning) << "Failed to unmap port ["sv << it->port.wan << "] to port ["sv << it->port.lan << "]: error code ["sv << status << ']';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class deinit_t : public platf::deinit_t {
|
||||
public:
|
||||
class deinit_t: public platf::deinit_t {
|
||||
public:
|
||||
using iter_t = std::vector<mapping_t>::const_reverse_iterator;
|
||||
deinit_t(urls_t &&urls, IGDdatas data, std::vector<mapping_t> &&mapping)
|
||||
: urls { std::move(urls) }, data { data }, mapping { std::move(mapping) } {}
|
||||
deinit_t(urls_t &&urls, IGDdatas data, std::vector<mapping_t> &&mapping):
|
||||
urls { std::move(urls) }, data { data }, mapping { std::move(mapping) } {}
|
||||
|
||||
~deinit_t() {
|
||||
BOOST_LOG(info) << "Unmapping UPNP ports..."sv;
|
||||
|
|
@ -73,10 +73,11 @@ public:
|
|||
IGDdatas data;
|
||||
|
||||
std::vector<mapping_t> mapping;
|
||||
};
|
||||
};
|
||||
|
||||
static std::string_view status_string(int status) {
|
||||
switch(status) {
|
||||
static std::string_view
|
||||
status_string(int status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return "No IGD device found"sv;
|
||||
case 1:
|
||||
|
|
@ -88,23 +89,24 @@ static std::string_view status_string(int status) {
|
|||
}
|
||||
|
||||
return "Unknown status"sv;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<platf::deinit_t> start() {
|
||||
if(!config::sunshine.flags[config::flag::UPNP]) {
|
||||
std::unique_ptr<platf::deinit_t>
|
||||
start() {
|
||||
if (!config::sunshine.flags[config::flag::UPNP]) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int err {};
|
||||
|
||||
device_t device { upnpDiscover(2000, nullptr, nullptr, 0, IPv4, 2, &err) };
|
||||
if(!device || err) {
|
||||
if (!device || err) {
|
||||
BOOST_LOG(error) << "Couldn't discover any UPNP devices"sv;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for(auto dev = device.get(); dev != nullptr; dev = dev->pNext) {
|
||||
for (auto dev = device.get(); dev != nullptr; dev = dev->pNext) {
|
||||
BOOST_LOG(debug) << "Found device: "sv << dev->descURL;
|
||||
}
|
||||
|
||||
|
|
@ -115,19 +117,19 @@ std::unique_ptr<platf::deinit_t> start() {
|
|||
IGDdatas data;
|
||||
|
||||
auto status = UPNP_GetValidIGD(device.get(), &urls.el, &data, lan_addr.data(), lan_addr.size());
|
||||
if(status != 1 && status != 2) {
|
||||
if (status != 1 && status != 2) {
|
||||
BOOST_LOG(error) << status_string(status);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << "Found valid IGD device: "sv << urls->rootdescURL;
|
||||
|
||||
if(UPNP_GetExternalIPAddress(urls->controlURL, data.first.servicetype, wan_addr.data())) {
|
||||
if (UPNP_GetExternalIPAddress(urls->controlURL, data.first.servicetype, wan_addr.data())) {
|
||||
BOOST_LOG(warning) << "Could not get external ip"sv;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(debug) << "Found external ip: "sv << wan_addr.data();
|
||||
if(config::nvhttp.external_ip.empty()) {
|
||||
if (config::nvhttp.external_ip.empty()) {
|
||||
config::nvhttp.external_ip = wan_addr.data();
|
||||
}
|
||||
}
|
||||
|
|
@ -150,14 +152,14 @@ std::unique_ptr<platf::deinit_t> start() {
|
|||
};
|
||||
|
||||
// Only map port for the Web Manager if it is configured to accept connection from WAN
|
||||
if(net::from_enum_string(config::nvhttp.origin_web_ui_allowed) > net::LAN) {
|
||||
if (net::from_enum_string(config::nvhttp.origin_web_ui_allowed) > net::LAN) {
|
||||
mappings.emplace_back(mapping_t { wm_http, wm_http, "Sunshine Web UI port"s, true });
|
||||
}
|
||||
|
||||
auto it = std::begin(mappings);
|
||||
|
||||
status = 0;
|
||||
for(; it != std::end(mappings); ++it) {
|
||||
for (; it != std::end(mappings); ++it) {
|
||||
status = UPNP_AddPortMapping(
|
||||
urls->controlURL,
|
||||
data.first.servicetype,
|
||||
|
|
@ -169,18 +171,18 @@ std::unique_ptr<platf::deinit_t> start() {
|
|||
nullptr,
|
||||
"86400");
|
||||
|
||||
if(status) {
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to map port ["sv << it->port.wan << "] to port ["sv << it->port.lan << "]: error code ["sv << status << ']';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(status) {
|
||||
if (status) {
|
||||
unmap(urls, data, std::make_reverse_iterator(it), std::rend(mappings));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<deinit_t>(std::move(urls), data, std::move(mappings));
|
||||
}
|
||||
}
|
||||
} // namespace upnp
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
#include "platform/common.h"
|
||||
|
||||
namespace upnp {
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t> start();
|
||||
[[nodiscard]] std::unique_ptr<platf::deinit_t>
|
||||
start();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
757
src/utility.h
757
src/utility.h
File diff suppressed because it is too large
Load diff
30
src/uuid.h
30
src/uuid.h
|
|
@ -6,27 +6,29 @@
|
|||
#include <random>
|
||||
|
||||
namespace uuid_util {
|
||||
union uuid_t {
|
||||
union uuid_t {
|
||||
std::uint8_t b8[16];
|
||||
std::uint16_t b16[8];
|
||||
std::uint32_t b32[4];
|
||||
std::uint64_t b64[2];
|
||||
|
||||
static uuid_t generate(std::default_random_engine &engine) {
|
||||
static uuid_t
|
||||
generate(std::default_random_engine &engine) {
|
||||
std::uniform_int_distribution<std::uint8_t> dist(0, std::numeric_limits<std::uint8_t>::max());
|
||||
|
||||
uuid_t buf;
|
||||
for(auto &el : buf.b8) {
|
||||
for (auto &el : buf.b8) {
|
||||
el = dist(engine);
|
||||
}
|
||||
|
||||
buf.b8[7] &= (std::uint8_t)0b00101111;
|
||||
buf.b8[9] &= (std::uint8_t)0b10011111;
|
||||
buf.b8[7] &= (std::uint8_t) 0b00101111;
|
||||
buf.b8[9] &= (std::uint8_t) 0b10011111;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static uuid_t generate() {
|
||||
static uuid_t
|
||||
generate() {
|
||||
std::random_device r;
|
||||
|
||||
std::default_random_engine engine { r() };
|
||||
|
|
@ -34,7 +36,8 @@ union uuid_t {
|
|||
return generate(engine);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string string() const {
|
||||
[[nodiscard]] std::string
|
||||
string() const {
|
||||
std::string result;
|
||||
|
||||
result.reserve(sizeof(uuid_t) * 2 + 4);
|
||||
|
|
@ -50,7 +53,7 @@ union uuid_t {
|
|||
};
|
||||
auto last_slice = hex_view.substr(20, 12);
|
||||
|
||||
for(auto &slice : slices) {
|
||||
for (auto &slice : slices) {
|
||||
std::copy(std::begin(slice), std::end(slice), std::back_inserter(result));
|
||||
|
||||
result.push_back('-');
|
||||
|
|
@ -61,17 +64,20 @@ union uuid_t {
|
|||
return result;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const uuid_t &other) const {
|
||||
constexpr bool
|
||||
operator==(const uuid_t &other) const {
|
||||
return b64[0] == other.b64[0] && b64[1] == other.b64[1];
|
||||
}
|
||||
|
||||
constexpr bool operator<(const uuid_t &other) const {
|
||||
constexpr bool
|
||||
operator<(const uuid_t &other) const {
|
||||
return (b64[0] < other.b64[0] || (b64[0] == other.b64[0] && b64[1] < other.b64[1]));
|
||||
}
|
||||
|
||||
constexpr bool operator>(const uuid_t &other) const {
|
||||
constexpr bool
|
||||
operator>(const uuid_t &other) const {
|
||||
return (b64[0] > other.b64[0] || (b64[0] == other.b64[0] && b64[1] > other.b64[1]));
|
||||
}
|
||||
};
|
||||
};
|
||||
} // namespace uuid_util
|
||||
#endif // T_MAN_UUID_H
|
||||
|
|
|
|||
747
src/video.cpp
747
src/video.cpp
File diff suppressed because it is too large
Load diff
54
src/video.h
54
src/video.h
|
|
@ -14,17 +14,20 @@ extern "C" {
|
|||
struct AVPacket;
|
||||
namespace video {
|
||||
|
||||
struct packet_raw_t {
|
||||
void init_packet() {
|
||||
struct packet_raw_t {
|
||||
void
|
||||
init_packet() {
|
||||
this->av_packet = av_packet_alloc();
|
||||
}
|
||||
|
||||
template<class P>
|
||||
explicit packet_raw_t(P *user_data) : channel_data { user_data } {
|
||||
template <class P>
|
||||
explicit packet_raw_t(P *user_data):
|
||||
channel_data { user_data } {
|
||||
init_packet();
|
||||
}
|
||||
|
||||
explicit packet_raw_t(std::nullptr_t) : channel_data { nullptr } {
|
||||
explicit packet_raw_t(std::nullptr_t):
|
||||
channel_data { nullptr } {
|
||||
init_packet();
|
||||
}
|
||||
|
||||
|
|
@ -38,27 +41,30 @@ struct packet_raw_t {
|
|||
|
||||
KITTY_DEFAULT_CONSTR_MOVE(replace_t)
|
||||
|
||||
replace_t(std::string_view old, std::string_view _new) noexcept : old { std::move(old) }, _new { std::move(_new) } {}
|
||||
replace_t(std::string_view old, std::string_view _new) noexcept:
|
||||
old { std::move(old) }, _new { std::move(_new) } {}
|
||||
};
|
||||
|
||||
AVPacket *av_packet;
|
||||
std::vector<replace_t> *replacements;
|
||||
void *channel_data;
|
||||
};
|
||||
};
|
||||
|
||||
using packet_t = std::unique_ptr<packet_raw_t>;
|
||||
using packet_t = std::unique_ptr<packet_raw_t>;
|
||||
|
||||
struct hdr_info_raw_t {
|
||||
explicit hdr_info_raw_t(bool enabled) : enabled { enabled }, metadata {} {};
|
||||
explicit hdr_info_raw_t(bool enabled, const SS_HDR_METADATA &metadata) : enabled { enabled }, metadata { metadata } {};
|
||||
struct hdr_info_raw_t {
|
||||
explicit hdr_info_raw_t(bool enabled):
|
||||
enabled { enabled }, metadata {} {};
|
||||
explicit hdr_info_raw_t(bool enabled, const SS_HDR_METADATA &metadata):
|
||||
enabled { enabled }, metadata { metadata } {};
|
||||
|
||||
bool enabled;
|
||||
SS_HDR_METADATA metadata;
|
||||
};
|
||||
};
|
||||
|
||||
using hdr_info_t = std::unique_ptr<hdr_info_raw_t>;
|
||||
using hdr_info_t = std::unique_ptr<hdr_info_raw_t>;
|
||||
|
||||
struct config_t {
|
||||
struct config_t {
|
||||
int width;
|
||||
int height;
|
||||
int framerate;
|
||||
|
|
@ -68,28 +74,30 @@ struct config_t {
|
|||
int encoderCscMode;
|
||||
int videoFormat;
|
||||
int dynamicRange;
|
||||
};
|
||||
};
|
||||
|
||||
using float4 = float[4];
|
||||
using float3 = float[3];
|
||||
using float2 = float[2];
|
||||
using float4 = float[4];
|
||||
using float3 = float[3];
|
||||
using float2 = float[2];
|
||||
|
||||
struct alignas(16) color_t {
|
||||
struct alignas(16) color_t {
|
||||
float4 color_vec_y;
|
||||
float4 color_vec_u;
|
||||
float4 color_vec_v;
|
||||
float2 range_y;
|
||||
float2 range_uv;
|
||||
};
|
||||
};
|
||||
|
||||
extern color_t colors[6];
|
||||
extern color_t colors[6];
|
||||
|
||||
void capture(
|
||||
void
|
||||
capture(
|
||||
safe::mail_t mail,
|
||||
config_t config,
|
||||
void *channel_data);
|
||||
|
||||
int init();
|
||||
int
|
||||
init();
|
||||
} // namespace video
|
||||
|
||||
#endif // SUNSHINE_VIDEO_H
|
||||
|
|
|
|||
2
third-party/ViGEmClient
vendored
2
third-party/ViGEmClient
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 726404ef5590ea4bfcc7d9fa40cadcb2638a5b82
|
||||
Subproject commit 9e842ba1c3a6efbb90d9b7e9346a55b1a3d10494
|
||||
2
third-party/ffmpeg-linux-aarch64
vendored
2
third-party/ffmpeg-linux-aarch64
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 4a29f4eeaf7d207f171b5afb06327a2b6912b14c
|
||||
Subproject commit 22034b2bafd873308a1857da749eabe9b537b33b
|
||||
2
third-party/ffmpeg-linux-x86_64
vendored
2
third-party/ffmpeg-linux-x86_64
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 05eff5066370a9c223b56ae8c5d0684e0d06a0fe
|
||||
Subproject commit d8f29a064caabdeb78f263a5017a5dbdaa454eb6
|
||||
2
third-party/ffmpeg-macos-aarch64
vendored
2
third-party/ffmpeg-macos-aarch64
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 645dcc56665a5a3c4f7a5284a1e42abc76cb5278
|
||||
Subproject commit 5876c4b765670deaeb6927c1785085bbf1a98b96
|
||||
2
third-party/ffmpeg-macos-x86_64
vendored
2
third-party/ffmpeg-macos-x86_64
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 759f5fc2165ef070bd300edcc691c48ccb8f84f8
|
||||
Subproject commit d75ce5ffeef371cedb6fbbb23ac5603e34c2994b
|
||||
2
third-party/ffmpeg-windows-x86_64
vendored
2
third-party/ffmpeg-windows-x86_64
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit b05f94a92ee0a3de4742f6b4897556b6b84f5dd5
|
||||
Subproject commit c4d6360a59d149b1ea097a5658ab1193e20db643
|
||||
28
third-party/glad/include/EGL/eglplatform.h
vendored
28
third-party/glad/include/EGL/eglplatform.h
vendored
|
|
@ -28,11 +28,11 @@
|
|||
*/
|
||||
|
||||
#ifndef EGLAPI
|
||||
#define EGLAPI KHRONOS_APICALL
|
||||
#define EGLAPI KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
#ifndef EGLAPIENTRY
|
||||
#define EGLAPIENTRY KHRONOS_APIENTRY
|
||||
#define EGLAPIENTRY KHRONOS_APIENTRY
|
||||
#endif
|
||||
#define EGLAPIENTRYP EGLAPIENTRY *
|
||||
|
||||
|
|
@ -55,10 +55,10 @@ typedef void *EGLNativePixmapType;
|
|||
typedef void *EGLNativeWindowType;
|
||||
|
||||
#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
typedef HDC EGLNativeDisplayType;
|
||||
typedef HBITMAP EGLNativePixmapType;
|
||||
|
|
@ -111,9 +111,9 @@ typedef khronos_uintptr_t EGLNativeWindowType;
|
|||
|
||||
#elif defined(__unix__) || defined(USE_X11)
|
||||
|
||||
/* X11 (tentative) */
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
/* X11 (tentative) */
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
typedef Display *EGLNativeDisplayType;
|
||||
typedef Pixmap EGLNativePixmapType;
|
||||
|
|
@ -127,7 +127,7 @@ typedef void *EGLNativeWindowType;
|
|||
|
||||
#elif defined(__HAIKU__)
|
||||
|
||||
#include <kernel/image.h>
|
||||
#include <kernel/image.h>
|
||||
|
||||
typedef void *EGLNativeDisplayType;
|
||||
typedef khronos_uintptr_t EGLNativePixmapType;
|
||||
|
|
@ -140,7 +140,7 @@ typedef khronos_uintptr_t EGLNativePixmapType;
|
|||
typedef khronos_uintptr_t EGLNativeWindowType;
|
||||
|
||||
#else
|
||||
#error "Platform not recognized"
|
||||
#error "Platform not recognized"
|
||||
#endif
|
||||
|
||||
/* EGL 1.2 types, renamed for consistency in EGL 1.3 */
|
||||
|
|
@ -148,7 +148,6 @@ typedef EGLNativeDisplayType NativeDisplayType;
|
|||
typedef EGLNativePixmapType NativePixmapType;
|
||||
typedef EGLNativeWindowType NativeWindowType;
|
||||
|
||||
|
||||
/* Define EGLint. This must be a signed integral type large enough to contain
|
||||
* all legal attribute names and values passed into and out of EGL, whether
|
||||
* their type is boolean, bitmask, enumerant (symbolic constant), integer,
|
||||
|
|
@ -158,12 +157,11 @@ typedef EGLNativeWindowType NativeWindowType;
|
|||
*/
|
||||
typedef khronos_int32_t EGLint;
|
||||
|
||||
|
||||
/* C++ / C typecast macros for special EGL handle values */
|
||||
#if defined(__cplusplus)
|
||||
#define EGL_CAST(type, value) (static_cast<type>(value))
|
||||
#define EGL_CAST(type, value) (static_cast<type>(value))
|
||||
#else
|
||||
#define EGL_CAST(type, value) ((type)(value))
|
||||
#define EGL_CAST(type, value) ((type) (value))
|
||||
#endif
|
||||
|
||||
#endif /* __eglplatform_h */
|
||||
72
third-party/glad/include/KHR/khrplatform.h
vendored
72
third-party/glad/include/KHR/khrplatform.h
vendored
|
|
@ -91,7 +91,7 @@
|
|||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
#define KHRONOS_STATIC 1
|
||||
#define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
|
|
@ -100,17 +100,17 @@
|
|||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
#define KHRONOS_APICALL
|
||||
#define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
#define KHRONOS_APICALL __declspec(dllimport)
|
||||
#define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined(__SYMBIAN32__)
|
||||
#define KHRONOS_APICALL IMPORT_C
|
||||
#define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
#define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
#define KHRONOS_APICALL
|
||||
#define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
|
|
@ -120,10 +120,10 @@
|
|||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
#define KHRONOS_APIENTRY __stdcall
|
||||
/* Win32 but not WinCE */
|
||||
#define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
#define KHRONOS_APIENTRY
|
||||
#define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
|
|
@ -132,40 +132,39 @@
|
|||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined(__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__VMS) || defined(__sgi)
|
||||
|
||||
/*
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
|
|
@ -176,8 +175,8 @@ typedef __int32 khronos_int32_t;
|
|||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
|
|
@ -186,15 +185,15 @@ typedef unsigned __int64 khronos_uint64_t;
|
|||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
|
|
@ -203,25 +202,24 @@ typedef unsigned long long int khronos_uint64_t;
|
|||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
|
|
@ -272,7 +270,7 @@ typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
|||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
|||
197
third-party/glad/include/glad/egl.h
vendored
197
third-party/glad/include/glad/egl.h
vendored
|
|
@ -28,7 +28,6 @@
|
|||
#ifndef GLAD_EGL_H_
|
||||
#define GLAD_EGL_H_
|
||||
|
||||
|
||||
#define GLAD_EGL
|
||||
#define GLAD_OPTION_EGL_LOADER
|
||||
|
||||
|
|
@ -37,108 +36,108 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_H_
|
||||
#define GLAD_PLATFORM_H_
|
||||
#define GLAD_PLATFORM_H_
|
||||
|
||||
#ifndef GLAD_PLATFORM_WIN32
|
||||
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)
|
||||
#define GLAD_PLATFORM_WIN32 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_WIN32 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_WIN32
|
||||
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)
|
||||
#define GLAD_PLATFORM_WIN32 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_WIN32 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_APPLE
|
||||
#ifdef __APPLE__
|
||||
#define GLAD_PLATFORM_APPLE 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_APPLE 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_APPLE
|
||||
#ifdef __APPLE__
|
||||
#define GLAD_PLATFORM_APPLE 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_APPLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_EMSCRIPTEN
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define GLAD_PLATFORM_EMSCRIPTEN 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_EMSCRIPTEN 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_EMSCRIPTEN
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define GLAD_PLATFORM_EMSCRIPTEN 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_EMSCRIPTEN 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_UWP
|
||||
#if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY)
|
||||
#ifdef __has_include
|
||||
#if __has_include(<winapifamily.h>)
|
||||
#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1
|
||||
#endif
|
||||
#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_
|
||||
#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_UWP
|
||||
#if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY)
|
||||
#ifdef __has_include
|
||||
#if __has_include(<winapifamily.h>)
|
||||
#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1
|
||||
#endif
|
||||
#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_
|
||||
#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY
|
||||
#include <winapifamily.h>
|
||||
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
|
||||
#define GLAD_PLATFORM_UWP 1
|
||||
#endif
|
||||
#endif
|
||||
#ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY
|
||||
#include <winapifamily.h>
|
||||
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
|
||||
#define GLAD_PLATFORM_UWP 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_UWP
|
||||
#define GLAD_PLATFORM_UWP 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_UWP
|
||||
#define GLAD_PLATFORM_UWP 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define GLAD_GNUC_EXTENSION __extension__
|
||||
#else
|
||||
#define GLAD_GNUC_EXTENSION
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
#define GLAD_GNUC_EXTENSION __extension__
|
||||
#else
|
||||
#define GLAD_GNUC_EXTENSION
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_API_CALL
|
||||
#if defined(GLAD_API_CALL_EXPORT)
|
||||
#if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__)
|
||||
#if defined(GLAD_API_CALL_EXPORT_BUILD)
|
||||
#if defined(__GNUC__)
|
||||
#define GLAD_API_CALL __attribute__((dllexport)) extern
|
||||
#else
|
||||
#define GLAD_API_CALL __declspec(dllexport) extern
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define GLAD_API_CALL __attribute__((dllimport)) extern
|
||||
#else
|
||||
#define GLAD_API_CALL __declspec(dllimport) extern
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD)
|
||||
#define GLAD_API_CALL __attribute__((visibility("default"))) extern
|
||||
#else
|
||||
#define GLAD_API_CALL extern
|
||||
#endif
|
||||
#else
|
||||
#define GLAD_API_CALL extern
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_API_CALL
|
||||
#if defined(GLAD_API_CALL_EXPORT)
|
||||
#if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__)
|
||||
#if defined(GLAD_API_CALL_EXPORT_BUILD)
|
||||
#if defined(__GNUC__)
|
||||
#define GLAD_API_CALL __attribute__((dllexport)) extern
|
||||
#else
|
||||
#define GLAD_API_CALL __declspec(dllexport) extern
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define GLAD_API_CALL __attribute__((dllimport)) extern
|
||||
#else
|
||||
#define GLAD_API_CALL __declspec(dllimport) extern
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD)
|
||||
#define GLAD_API_CALL __attribute__((visibility("default"))) extern
|
||||
#else
|
||||
#define GLAD_API_CALL extern
|
||||
#endif
|
||||
#else
|
||||
#define GLAD_API_CALL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef APIENTRY
|
||||
#define GLAD_API_PTR APIENTRY
|
||||
#elif GLAD_PLATFORM_WIN32
|
||||
#define GLAD_API_PTR __stdcall
|
||||
#else
|
||||
#define GLAD_API_PTR
|
||||
#endif
|
||||
#ifdef APIENTRY
|
||||
#define GLAD_API_PTR APIENTRY
|
||||
#elif GLAD_PLATFORM_WIN32
|
||||
#define GLAD_API_PTR __stdcall
|
||||
#else
|
||||
#define GLAD_API_PTR
|
||||
#endif
|
||||
|
||||
#ifndef GLAPI
|
||||
#define GLAPI GLAD_API_CALL
|
||||
#endif
|
||||
#ifndef GLAPI
|
||||
#define GLAPI GLAD_API_CALL
|
||||
#endif
|
||||
|
||||
#ifndef GLAPIENTRY
|
||||
#define GLAPIENTRY GLAD_API_PTR
|
||||
#endif
|
||||
#ifndef GLAPIENTRY
|
||||
#define GLAPIENTRY GLAD_API_PTR
|
||||
#endif
|
||||
|
||||
#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor)
|
||||
#define GLAD_VERSION_MAJOR(version) (version / 10000)
|
||||
#define GLAD_VERSION_MINOR(version) (version % 10000)
|
||||
#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor)
|
||||
#define GLAD_VERSION_MAJOR(version) (version / 10000)
|
||||
#define GLAD_VERSION_MINOR(version) (version % 10000)
|
||||
|
||||
#define GLAD_GENERATOR_VERSION "2.0.0-beta"
|
||||
#define GLAD_GENERATOR_VERSION "2.0.0-beta"
|
||||
|
||||
typedef void (*GLADapiproc)(void);
|
||||
|
||||
|
|
@ -316,12 +315,10 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
|
|||
#define EGL_WIDTH 0x3057
|
||||
#define EGL_WINDOW_BIT 0x0004
|
||||
|
||||
|
||||
#include <KHR/khrplatform.h>
|
||||
|
||||
#include <EGL/eglplatform.h>
|
||||
|
||||
|
||||
struct AHardwareBuffer;
|
||||
|
||||
struct wl_buffer;
|
||||
|
|
@ -330,7 +327,6 @@ struct wl_display;
|
|||
|
||||
struct wl_resource;
|
||||
|
||||
|
||||
typedef unsigned int EGLBoolean;
|
||||
|
||||
typedef unsigned int EGLenum;
|
||||
|
|
@ -410,7 +406,6 @@ typedef void(GLAD_API_PTR *EGLDEBUGPROCKHR)(EGLenum error, const char *command,
|
|||
|
||||
#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC
|
||||
|
||||
|
||||
#define EGL_VERSION_1_0 1
|
||||
GLAD_API_CALL int GLAD_EGL_VERSION_1_0;
|
||||
#define EGL_VERSION_1_1 1
|
||||
|
|
@ -424,7 +419,6 @@ GLAD_API_CALL int GLAD_EGL_VERSION_1_4;
|
|||
#define EGL_VERSION_1_5 1
|
||||
GLAD_API_CALL int GLAD_EGL_VERSION_1_5;
|
||||
|
||||
|
||||
typedef EGLBoolean(GLAD_API_PTR *PFNEGLBINDAPIPROC)(EGLenum api);
|
||||
typedef EGLBoolean(GLAD_API_PTR *PFNEGLBINDTEXIMAGEPROC)(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
|
||||
typedef EGLBoolean(GLAD_API_PTR *PFNEGLCHOOSECONFIGPROC)(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
|
||||
|
|
@ -567,15 +561,18 @@ GLAD_API_CALL PFNEGLWAITNATIVEPROC glad_eglWaitNative;
|
|||
GLAD_API_CALL PFNEGLWAITSYNCPROC glad_eglWaitSync;
|
||||
#define eglWaitSync glad_eglWaitSync
|
||||
|
||||
|
||||
GLAD_API_CALL int gladLoadEGLUserPtr(EGLDisplay display, GLADuserptrloadfunc load, void *userptr);
|
||||
GLAD_API_CALL int gladLoadEGL(EGLDisplay display, GLADloadfunc load);
|
||||
GLAD_API_CALL int
|
||||
gladLoadEGLUserPtr(EGLDisplay display, GLADuserptrloadfunc load, void *userptr);
|
||||
GLAD_API_CALL int
|
||||
gladLoadEGL(EGLDisplay display, GLADloadfunc load);
|
||||
|
||||
#ifdef GLAD_EGL
|
||||
|
||||
GLAD_API_CALL int gladLoaderLoadEGL(EGLDisplay display);
|
||||
GLAD_API_CALL int
|
||||
gladLoaderLoadEGL(EGLDisplay display);
|
||||
|
||||
GLAD_API_CALL void gladLoaderUnloadEGL(void);
|
||||
GLAD_API_CALL void
|
||||
gladLoaderUnloadEGL(void);
|
||||
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
208
third-party/glad/include/glad/gl.h
vendored
208
third-party/glad/include/glad/gl.h
vendored
|
|
@ -29,27 +29,27 @@
|
|||
#define GLAD_GL_H_
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreserved-id-macro"
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreserved-id-macro"
|
||||
#endif
|
||||
#ifdef __gl_h_
|
||||
#error OpenGL (gl.h) header already included (API: gl), remove previous include!
|
||||
#error OpenGL (gl.h) header already included (API: gl), remove previous include!
|
||||
#endif
|
||||
#define __gl_h_ 1
|
||||
#ifdef __gl3_h_
|
||||
#error OpenGL (gl3.h) header already included (API: gl), remove previous include!
|
||||
#error OpenGL (gl3.h) header already included (API: gl), remove previous include!
|
||||
#endif
|
||||
#define __gl3_h_ 1
|
||||
#ifdef __glext_h_
|
||||
#error OpenGL (glext.h) header already included (API: gl), remove previous include!
|
||||
#error OpenGL (glext.h) header already included (API: gl), remove previous include!
|
||||
#endif
|
||||
#define __glext_h_ 1
|
||||
#ifdef __gl3ext_h_
|
||||
#error OpenGL (gl3ext.h) header already included (API: gl), remove previous include!
|
||||
#error OpenGL (gl3ext.h) header already included (API: gl), remove previous include!
|
||||
#endif
|
||||
#define __gl3ext_h_ 1
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#define GLAD_GL
|
||||
|
|
@ -61,108 +61,108 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_H_
|
||||
#define GLAD_PLATFORM_H_
|
||||
#define GLAD_PLATFORM_H_
|
||||
|
||||
#ifndef GLAD_PLATFORM_WIN32
|
||||
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)
|
||||
#define GLAD_PLATFORM_WIN32 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_WIN32 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_WIN32
|
||||
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)
|
||||
#define GLAD_PLATFORM_WIN32 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_WIN32 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_APPLE
|
||||
#ifdef __APPLE__
|
||||
#define GLAD_PLATFORM_APPLE 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_APPLE 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_APPLE
|
||||
#ifdef __APPLE__
|
||||
#define GLAD_PLATFORM_APPLE 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_APPLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_EMSCRIPTEN
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define GLAD_PLATFORM_EMSCRIPTEN 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_EMSCRIPTEN 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_EMSCRIPTEN
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define GLAD_PLATFORM_EMSCRIPTEN 1
|
||||
#else
|
||||
#define GLAD_PLATFORM_EMSCRIPTEN 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_UWP
|
||||
#if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY)
|
||||
#ifdef __has_include
|
||||
#if __has_include(<winapifamily.h>)
|
||||
#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1
|
||||
#endif
|
||||
#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_
|
||||
#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_UWP
|
||||
#if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY)
|
||||
#ifdef __has_include
|
||||
#if __has_include(<winapifamily.h>)
|
||||
#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1
|
||||
#endif
|
||||
#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_
|
||||
#define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY
|
||||
#include <winapifamily.h>
|
||||
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
|
||||
#define GLAD_PLATFORM_UWP 1
|
||||
#endif
|
||||
#endif
|
||||
#ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY
|
||||
#include <winapifamily.h>
|
||||
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
|
||||
#define GLAD_PLATFORM_UWP 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_PLATFORM_UWP
|
||||
#define GLAD_PLATFORM_UWP 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_PLATFORM_UWP
|
||||
#define GLAD_PLATFORM_UWP 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define GLAD_GNUC_EXTENSION __extension__
|
||||
#else
|
||||
#define GLAD_GNUC_EXTENSION
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
#define GLAD_GNUC_EXTENSION __extension__
|
||||
#else
|
||||
#define GLAD_GNUC_EXTENSION
|
||||
#endif
|
||||
|
||||
#ifndef GLAD_API_CALL
|
||||
#if defined(GLAD_API_CALL_EXPORT)
|
||||
#if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__)
|
||||
#if defined(GLAD_API_CALL_EXPORT_BUILD)
|
||||
#if defined(__GNUC__)
|
||||
#define GLAD_API_CALL __attribute__((dllexport)) extern
|
||||
#else
|
||||
#define GLAD_API_CALL __declspec(dllexport) extern
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define GLAD_API_CALL __attribute__((dllimport)) extern
|
||||
#else
|
||||
#define GLAD_API_CALL __declspec(dllimport) extern
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD)
|
||||
#define GLAD_API_CALL __attribute__((visibility("default"))) extern
|
||||
#else
|
||||
#define GLAD_API_CALL extern
|
||||
#endif
|
||||
#else
|
||||
#define GLAD_API_CALL extern
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GLAD_API_CALL
|
||||
#if defined(GLAD_API_CALL_EXPORT)
|
||||
#if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__)
|
||||
#if defined(GLAD_API_CALL_EXPORT_BUILD)
|
||||
#if defined(__GNUC__)
|
||||
#define GLAD_API_CALL __attribute__((dllexport)) extern
|
||||
#else
|
||||
#define GLAD_API_CALL __declspec(dllexport) extern
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__)
|
||||
#define GLAD_API_CALL __attribute__((dllimport)) extern
|
||||
#else
|
||||
#define GLAD_API_CALL __declspec(dllimport) extern
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD)
|
||||
#define GLAD_API_CALL __attribute__((visibility("default"))) extern
|
||||
#else
|
||||
#define GLAD_API_CALL extern
|
||||
#endif
|
||||
#else
|
||||
#define GLAD_API_CALL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef APIENTRY
|
||||
#define GLAD_API_PTR APIENTRY
|
||||
#elif GLAD_PLATFORM_WIN32
|
||||
#define GLAD_API_PTR __stdcall
|
||||
#else
|
||||
#define GLAD_API_PTR
|
||||
#endif
|
||||
#ifdef APIENTRY
|
||||
#define GLAD_API_PTR APIENTRY
|
||||
#elif GLAD_PLATFORM_WIN32
|
||||
#define GLAD_API_PTR __stdcall
|
||||
#else
|
||||
#define GLAD_API_PTR
|
||||
#endif
|
||||
|
||||
#ifndef GLAPI
|
||||
#define GLAPI GLAD_API_CALL
|
||||
#endif
|
||||
#ifndef GLAPI
|
||||
#define GLAPI GLAD_API_CALL
|
||||
#endif
|
||||
|
||||
#ifndef GLAPIENTRY
|
||||
#define GLAPIENTRY GLAD_API_PTR
|
||||
#endif
|
||||
#ifndef GLAPIENTRY
|
||||
#define GLAPIENTRY GLAD_API_PTR
|
||||
#endif
|
||||
|
||||
#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor)
|
||||
#define GLAD_VERSION_MAJOR(version) (version / 10000)
|
||||
#define GLAD_VERSION_MINOR(version) (version % 10000)
|
||||
#define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor)
|
||||
#define GLAD_VERSION_MAJOR(version) (version / 10000)
|
||||
#define GLAD_VERSION_MINOR(version) (version % 10000)
|
||||
|
||||
#define GLAD_GENERATOR_VERSION "2.0.0-beta"
|
||||
#define GLAD_GENERATOR_VERSION "2.0.0-beta"
|
||||
|
||||
typedef void (*GLADapiproc)(void);
|
||||
|
||||
|
|
@ -1983,7 +1983,6 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
|
|||
#define GL_ZOOM_X 0x0D16
|
||||
#define GL_ZOOM_Y 0x0D17
|
||||
|
||||
|
||||
#include <KHR/khrplatform.h>
|
||||
|
||||
typedef unsigned int GLenum;
|
||||
|
|
@ -2090,7 +2089,6 @@ typedef GLintptr GLvdpauSurfaceNV;
|
|||
|
||||
typedef void(GLAD_API_PTR *GLVULKANPROCNV)(void);
|
||||
|
||||
|
||||
#define GL_VERSION_1_0 1
|
||||
#define GL_VERSION_1_1 1
|
||||
#define GL_VERSION_1_2 1
|
||||
|
|
@ -4235,15 +4233,17 @@ typedef struct GladGLContext {
|
|||
PFNGLWINDOWPOS3SVPROC WindowPos3sv;
|
||||
} GladGLContext;
|
||||
|
||||
|
||||
GLAD_API_CALL int gladLoadGLContextUserPtr(GladGLContext *context, GLADuserptrloadfunc load, void *userptr);
|
||||
GLAD_API_CALL int gladLoadGLContext(GladGLContext *context, GLADloadfunc load);
|
||||
|
||||
GLAD_API_CALL int
|
||||
gladLoadGLContextUserPtr(GladGLContext *context, GLADuserptrloadfunc load, void *userptr);
|
||||
GLAD_API_CALL int
|
||||
gladLoadGLContext(GladGLContext *context, GLADloadfunc load);
|
||||
|
||||
#ifdef GLAD_GL
|
||||
|
||||
GLAD_API_CALL int gladLoaderLoadGLContext(GladGLContext *context);
|
||||
GLAD_API_CALL void gladLoaderUnloadGL(void);
|
||||
GLAD_API_CALL int
|
||||
gladLoaderLoadGLContext(GladGLContext *context);
|
||||
GLAD_API_CALL void
|
||||
gladLoaderUnloadGL(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
2
third-party/miniupnp
vendored
2
third-party/miniupnp
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit e439318cf782e30066d430f27a1365e013a5ab94
|
||||
Subproject commit 014c9df8ee7a36e5bf85aa619062a2d4b95ec8f6
|
||||
2
third-party/moonlight-common-c
vendored
2
third-party/moonlight-common-c
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit c9426a6a71c4162e65dde8c0c71a25f1dbca46ba
|
||||
Subproject commit d3cb8131d12832898af31c4f2484ec1bd6bed0f4
|
||||
2
third-party/nanors
vendored
2
third-party/nanors
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit e9e242e98e27037830490b2a752895ca68f75f8b
|
||||
Subproject commit 395e5ada44dd8d5974eaf6bb6b17f23406e3ca72
|
||||
45
third-party/nvfbc/NvFBC.h
vendored
45
third-party/nvfbc/NvFBC.h
vendored
|
|
@ -1525,7 +1525,8 @@ typedef struct _NVFBC_TOGL_GRAB_FRAME_PARAMS {
|
|||
* A NULL terminated error message, or an empty string. Its maximum length
|
||||
* is NVFBC_ERROR_STR_LEN.
|
||||
*/
|
||||
const char *NVFBCAPI NvFBCGetLastErrorStr(const NVFBC_SESSION_HANDLE sessionHandle);
|
||||
const char *NVFBCAPI
|
||||
NvFBCGetLastErrorStr(const NVFBC_SESSION_HANDLE sessionHandle);
|
||||
|
||||
/*!
|
||||
* \brief Allocates a new handle for an NvFBC client.
|
||||
|
|
@ -1551,7 +1552,8 @@ const char *NVFBCAPI NvFBCGetLastErrorStr(const NVFBC_SESSION_HANDLE sessionHand
|
|||
* ::NVFBC_ERR_GL
|
||||
*
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCCreateHandle(NVFBC_SESSION_HANDLE *pSessionHandle, NVFBC_CREATE_HANDLE_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCCreateHandle(NVFBC_SESSION_HANDLE *pSessionHandle, NVFBC_CREATE_HANDLE_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Destroys the handle of an NvFBC client.
|
||||
|
|
@ -1577,7 +1579,8 @@ NVFBCSTATUS NVFBCAPI NvFBCCreateHandle(NVFBC_SESSION_HANDLE *pSessionHandle, NVF
|
|||
* ::NVFBC_ERR_CONTEXT \n
|
||||
* ::NVFBC_ERR_X
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCDestroyHandle(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_HANDLE_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCDestroyHandle(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_HANDLE_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Gets the current status of the display driver.
|
||||
|
|
@ -1596,7 +1599,8 @@ NVFBCSTATUS NVFBCAPI NvFBCDestroyHandle(const NVFBC_SESSION_HANDLE sessionHandle
|
|||
* ::NVFBC_ERR_INTERNAL \n
|
||||
* ::NVFBC_ERR_X
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCGetStatus(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_GET_STATUS_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCGetStatus(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_GET_STATUS_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Binds the FBC context to the calling thread.
|
||||
|
|
@ -1630,7 +1634,8 @@ NVFBCSTATUS NVFBCAPI NvFBCGetStatus(const NVFBC_SESSION_HANDLE sessionHandle, NV
|
|||
* ::NVFBC_ERR_INTERNAL \n
|
||||
* ::NVFBC_ERR_X
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCBindContext(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_BIND_CONTEXT_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCBindContext(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_BIND_CONTEXT_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Releases the FBC context from the calling thread.
|
||||
|
|
@ -1651,7 +1656,8 @@ NVFBCSTATUS NVFBCAPI NvFBCBindContext(const NVFBC_SESSION_HANDLE sessionHandle,
|
|||
* ::NVFBC_ERR_INTERNAL \n
|
||||
* ::NVFBC_ERR_X
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCReleaseContext(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_RELEASE_CONTEXT_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCReleaseContext(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_RELEASE_CONTEXT_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Creates a capture session for an FBC client.
|
||||
|
|
@ -1686,7 +1692,8 @@ NVFBCSTATUS NVFBCAPI NvFBCReleaseContext(const NVFBC_SESSION_HANDLE sessionHandl
|
|||
* ::NVFBC_ERR_MUST_RECREATE \n
|
||||
* ::NVFBC_ERR_INTERNAL
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCCreateCaptureSession(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_CREATE_CAPTURE_SESSION_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCCreateCaptureSession(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_CREATE_CAPTURE_SESSION_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Destroys a capture session for an FBC client.
|
||||
|
|
@ -1710,7 +1717,8 @@ NVFBCSTATUS NVFBCAPI NvFBCCreateCaptureSession(const NVFBC_SESSION_HANDLE sessio
|
|||
* ::NVFBC_ERR_INTERNAL \n
|
||||
* ::NVFBC_ERR_X
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCDestroyCaptureSession(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_CAPTURE_SESSION_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCDestroyCaptureSession(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_CAPTURE_SESSION_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Sets up a capture to system memory session.
|
||||
|
|
@ -1742,7 +1750,8 @@ NVFBCSTATUS NVFBCAPI NvFBCDestroyCaptureSession(const NVFBC_SESSION_HANDLE sessi
|
|||
* ::NVFBC_ERR_OUT_OF_MEMORY \n
|
||||
* ::NVFBC_ERR_X
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCToSysSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_SETUP_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCToSysSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_SETUP_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Captures a frame to a buffer in system memory.
|
||||
|
|
@ -1782,7 +1791,8 @@ NVFBCSTATUS NVFBCAPI NvFBCToSysSetUp(const NVFBC_SESSION_HANDLE sessionHandle, N
|
|||
* \see NvFBCCreateCaptureSession \n
|
||||
* \see NvFBCToSysSetUp
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCToSysGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_GRAB_FRAME_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCToSysGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_GRAB_FRAME_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Sets up a capture to video memory session.
|
||||
|
|
@ -1809,7 +1819,8 @@ NVFBCSTATUS NVFBCAPI NvFBCToSysGrabFrame(const NVFBC_SESSION_HANDLE sessionHandl
|
|||
* ::NVFBC_ERR_GL \n
|
||||
* ::NVFBC_ERR_X
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCToCudaSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_SETUP_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCToCudaSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_SETUP_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Captures a frame to a CUDA device in video memory.
|
||||
|
|
@ -1838,7 +1849,8 @@ NVFBCSTATUS NVFBCAPI NvFBCToCudaSetUp(const NVFBC_SESSION_HANDLE sessionHandle,
|
|||
* \see NvFBCCreateCaptureSession \n
|
||||
* \see NvFBCToCudaSetUp
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCToCudaGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_GRAB_FRAME_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCToCudaGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_GRAB_FRAME_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Sets up a capture to OpenGL buffer in video memory session.
|
||||
|
|
@ -1865,7 +1877,8 @@ NVFBCSTATUS NVFBCAPI NvFBCToCudaGrabFrame(const NVFBC_SESSION_HANDLE sessionHand
|
|||
* ::NVFBC_ERR_GL \n
|
||||
* ::NVFBC_ERR_X
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCToGLSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_SETUP_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCToGLSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_SETUP_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \brief Captures a frame to an OpenGL buffer in video memory.
|
||||
|
|
@ -1893,7 +1906,8 @@ NVFBCSTATUS NVFBCAPI NvFBCToGLSetUp(const NVFBC_SESSION_HANDLE sessionHandle, NV
|
|||
* \see NvFBCCreateCaptureSession \n
|
||||
* \see NvFBCToCudaSetUp
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCToGLGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_GRAB_FRAME_PARAMS *pParams);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCToGLGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_GRAB_FRAME_PARAMS *pParams);
|
||||
|
||||
/*!
|
||||
* \cond FBC_PFN
|
||||
|
|
@ -1966,7 +1980,8 @@ typedef struct
|
|||
* ::NVFBC_ERR_INVALID_PTR \n
|
||||
* ::NVFBC_ERR_API_VERSION
|
||||
*/
|
||||
NVFBCSTATUS NVFBCAPI NvFBCCreateInstance(NVFBC_API_FUNCTION_LIST *pFunctionList);
|
||||
NVFBCSTATUS NVFBCAPI
|
||||
NvFBCCreateInstance(NVFBC_API_FUNCTION_LIST *pFunctionList);
|
||||
/*!
|
||||
* \ingroup FBC_FUNC
|
||||
*
|
||||
|
|
|
|||
849
third-party/nvfbc/helper_math.h
vendored
849
third-party/nvfbc/helper_math.h
vendored
File diff suppressed because it is too large
Load diff
128
tools/audio.cpp
128
tools/audio.cpp
|
|
@ -30,30 +30,32 @@ constexpr auto SAMPLE_RATE = 48000;
|
|||
int device_state_filter = DEVICE_STATE_ACTIVE;
|
||||
|
||||
namespace audio {
|
||||
template<class T>
|
||||
void Release(T *p) {
|
||||
template <class T>
|
||||
void
|
||||
Release(T *p) {
|
||||
p->Release();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void co_task_free(T *p) {
|
||||
CoTaskMemFree((LPVOID)p);
|
||||
}
|
||||
template <class T>
|
||||
void
|
||||
co_task_free(T *p) {
|
||||
CoTaskMemFree((LPVOID) p);
|
||||
}
|
||||
|
||||
using device_enum_t = util::safe_ptr<IMMDeviceEnumerator, Release<IMMDeviceEnumerator>>;
|
||||
using collection_t = util::safe_ptr<IMMDeviceCollection, Release<IMMDeviceCollection>>;
|
||||
using prop_t = util::safe_ptr<IPropertyStore, Release<IPropertyStore>>;
|
||||
using device_t = util::safe_ptr<IMMDevice, Release<IMMDevice>>;
|
||||
using audio_client_t = util::safe_ptr<IAudioClient, Release<IAudioClient>>;
|
||||
using audio_capture_t = util::safe_ptr<IAudioCaptureClient, Release<IAudioCaptureClient>>;
|
||||
using wave_format_t = util::safe_ptr<WAVEFORMATEX, co_task_free<WAVEFORMATEX>>;
|
||||
using device_enum_t = util::safe_ptr<IMMDeviceEnumerator, Release<IMMDeviceEnumerator>>;
|
||||
using collection_t = util::safe_ptr<IMMDeviceCollection, Release<IMMDeviceCollection>>;
|
||||
using prop_t = util::safe_ptr<IPropertyStore, Release<IPropertyStore>>;
|
||||
using device_t = util::safe_ptr<IMMDevice, Release<IMMDevice>>;
|
||||
using audio_client_t = util::safe_ptr<IAudioClient, Release<IAudioClient>>;
|
||||
using audio_capture_t = util::safe_ptr<IAudioCaptureClient, Release<IAudioCaptureClient>>;
|
||||
using wave_format_t = util::safe_ptr<WAVEFORMATEX, co_task_free<WAVEFORMATEX>>;
|
||||
|
||||
using wstring_t = util::safe_ptr<WCHAR, co_task_free<WCHAR>>;
|
||||
using wstring_t = util::safe_ptr<WCHAR, co_task_free<WCHAR>>;
|
||||
|
||||
using handle_t = util::safe_ptr_v2<void, BOOL, CloseHandle>;
|
||||
using handle_t = util::safe_ptr_v2<void, BOOL, CloseHandle>;
|
||||
|
||||
class prop_var_t {
|
||||
public:
|
||||
class prop_var_t {
|
||||
public:
|
||||
prop_var_t() {
|
||||
PropVariantInit(&prop);
|
||||
}
|
||||
|
|
@ -63,17 +65,18 @@ public:
|
|||
}
|
||||
|
||||
PROPVARIANT prop;
|
||||
};
|
||||
};
|
||||
|
||||
const wchar_t *no_null(const wchar_t *str) {
|
||||
const wchar_t *
|
||||
no_null(const wchar_t *str) {
|
||||
return str ? str : L"Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
struct format_t {
|
||||
struct format_t {
|
||||
std::string_view name;
|
||||
int channels;
|
||||
int channel_mask;
|
||||
} formats[] {
|
||||
} formats[] {
|
||||
{ "Mono"sv,
|
||||
1,
|
||||
SPEAKER_FRONT_CENTER },
|
||||
|
|
@ -98,27 +101,29 @@ struct format_t {
|
|||
SPEAKER_BACK_RIGHT |
|
||||
SPEAKER_SIDE_LEFT |
|
||||
SPEAKER_SIDE_RIGHT }
|
||||
};
|
||||
};
|
||||
|
||||
void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) {
|
||||
void
|
||||
set_wave_format(audio::wave_format_t &wave_format, const format_t &format) {
|
||||
wave_format->nChannels = format.channels;
|
||||
wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8;
|
||||
wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign;
|
||||
|
||||
if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask;
|
||||
if (wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
((PWAVEFORMATEXTENSIBLE) wave_format.get())->dwChannelMask = format.channel_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
||||
audio_client_t
|
||||
make_audio_client(device_t &device, const format_t &format) {
|
||||
audio_client_t audio_client;
|
||||
auto status = device->Activate(
|
||||
IID_IAudioClient,
|
||||
CLSCTX_ALL,
|
||||
nullptr,
|
||||
(void **)&audio_client);
|
||||
(void **) &audio_client);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
std::cout << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
|
||||
return nullptr;
|
||||
|
|
@ -127,7 +132,7 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
|||
wave_format_t wave_format;
|
||||
status = audio_client->GetMixFormat(&wave_format);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
std::cout << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
|
||||
return nullptr;
|
||||
|
|
@ -135,14 +140,14 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
|||
|
||||
wave_format->wBitsPerSample = 16;
|
||||
wave_format->nSamplesPerSec = SAMPLE_RATE;
|
||||
switch(wave_format->wFormatTag) {
|
||||
switch (wave_format->wFormatTag) {
|
||||
case WAVE_FORMAT_PCM:
|
||||
break;
|
||||
case WAVE_FORMAT_IEEE_FLOAT:
|
||||
break;
|
||||
case WAVE_FORMAT_EXTENSIBLE: {
|
||||
auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get();
|
||||
if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) {
|
||||
auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get();
|
||||
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) {
|
||||
wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wave_ex->Samples.wValidBitsPerSample = 16;
|
||||
break;
|
||||
|
|
@ -164,14 +169,15 @@ audio_client_t make_audio_client(device_t &device, const format_t &format) {
|
|||
wave_format.get(),
|
||||
nullptr);
|
||||
|
||||
if(status) {
|
||||
if (status) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audio_client;
|
||||
}
|
||||
}
|
||||
|
||||
void print_device(device_t &device) {
|
||||
void
|
||||
print_device(device_t &device) {
|
||||
audio::wstring_t wstring;
|
||||
DWORD device_state;
|
||||
|
||||
|
|
@ -189,12 +195,12 @@ void print_device(device_t &device) {
|
|||
prop->GetValue(PKEY_DeviceInterface_FriendlyName, &adapter_friendly_name.prop);
|
||||
prop->GetValue(PKEY_Device_DeviceDesc, &device_desc.prop);
|
||||
|
||||
if(!(device_state & device_state_filter)) {
|
||||
if (!(device_state & device_state_filter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::wstring device_state_string = L"Unknown"s;
|
||||
switch(device_state) {
|
||||
switch (device_state) {
|
||||
case DEVICE_STATE_ACTIVE:
|
||||
device_state_string = L"Active"s;
|
||||
break;
|
||||
|
|
@ -212,46 +218,48 @@ void print_device(device_t &device) {
|
|||
std::wcout
|
||||
<< L"===== Device ====="sv << std::endl
|
||||
<< L"Device ID : "sv << wstring.get() << std::endl
|
||||
<< L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl
|
||||
<< L"Adapter name : "sv << no_null((LPWSTR)adapter_friendly_name.prop.pszVal) << std::endl
|
||||
<< L"Device description : "sv << no_null((LPWSTR)device_desc.prop.pszVal) << std::endl
|
||||
<< L"Device name : "sv << no_null((LPWSTR) device_friendly_name.prop.pszVal) << std::endl
|
||||
<< L"Adapter name : "sv << no_null((LPWSTR) adapter_friendly_name.prop.pszVal) << std::endl
|
||||
<< L"Device description : "sv << no_null((LPWSTR) device_desc.prop.pszVal) << std::endl
|
||||
<< L"Device state : "sv << device_state_string << std::endl
|
||||
<< std::endl;
|
||||
|
||||
if(device_state != DEVICE_STATE_ACTIVE) {
|
||||
if (device_state != DEVICE_STATE_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(const auto &format : formats) {
|
||||
for (const auto &format : formats) {
|
||||
// Ensure WaveFromat is compatible
|
||||
auto audio_client = make_audio_client(device, format);
|
||||
|
||||
std::cout << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace audio
|
||||
|
||||
void print_help() {
|
||||
void
|
||||
print_help() {
|
||||
std::cout
|
||||
<< "==== Help ===="sv << std::endl
|
||||
<< "Usage:"sv << std::endl
|
||||
<< " audio-info [Active|Disabled|Unplugged|Not-Present]" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY);
|
||||
|
||||
auto fg = util::fail_guard([]() {
|
||||
CoUninitialize();
|
||||
});
|
||||
|
||||
if(argc > 1) {
|
||||
if (argc > 1) {
|
||||
device_state_filter = 0;
|
||||
}
|
||||
|
||||
for(auto x = 1; x < argc; ++x) {
|
||||
for(auto p = argv[x]; *p != '\0'; ++p) {
|
||||
if(*p == ' ') {
|
||||
for (auto x = 1; x < argc; ++x) {
|
||||
for (auto p = argv[x]; *p != '\0'; ++p) {
|
||||
if (*p == ' ') {
|
||||
*p = '-';
|
||||
|
||||
continue;
|
||||
|
|
@ -260,16 +268,16 @@ int main(int argc, char *argv[]) {
|
|||
*p = std::tolower(*p);
|
||||
}
|
||||
|
||||
if(argv[x] == "active"sv) {
|
||||
if (argv[x] == "active"sv) {
|
||||
device_state_filter |= DEVICE_STATE_ACTIVE;
|
||||
}
|
||||
else if(argv[x] == "disabled"sv) {
|
||||
else if (argv[x] == "disabled"sv) {
|
||||
device_state_filter |= DEVICE_STATE_DISABLED;
|
||||
}
|
||||
else if(argv[x] == "unplugged"sv) {
|
||||
else if (argv[x] == "unplugged"sv) {
|
||||
device_state_filter |= DEVICE_STATE_UNPLUGGED;
|
||||
}
|
||||
else if(argv[x] == "not-present"sv) {
|
||||
else if (argv[x] == "not-present"sv) {
|
||||
device_state_filter |= DEVICE_STATE_NOTPRESENT;
|
||||
}
|
||||
else {
|
||||
|
|
@ -286,9 +294,9 @@ int main(int argc, char *argv[]) {
|
|||
nullptr,
|
||||
CLSCTX_ALL,
|
||||
IID_IMMDeviceEnumerator,
|
||||
(void **)&device_enum);
|
||||
(void **) &device_enum);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
std::cout << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
|
||||
return -1;
|
||||
|
|
@ -297,7 +305,7 @@ int main(int argc, char *argv[]) {
|
|||
audio::collection_t collection;
|
||||
status = device_enum->EnumAudioEndpoints(eRender, device_state_filter, &collection);
|
||||
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
std::cout << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
|
||||
return -1;
|
||||
|
|
@ -307,7 +315,7 @@ int main(int argc, char *argv[]) {
|
|||
collection->GetCount(&count);
|
||||
|
||||
std::cout << "====== Found "sv << count << " audio devices ======"sv << std::endl;
|
||||
for(auto x = 0; x < count; ++x) {
|
||||
for (auto x = 0; x < count; ++x) {
|
||||
audio::device_t device;
|
||||
collection->Item(x, &device);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,21 +10,23 @@
|
|||
|
||||
using namespace std::literals;
|
||||
namespace dxgi {
|
||||
template<class T>
|
||||
void Release(T *dxgi) {
|
||||
template <class T>
|
||||
void
|
||||
Release(T *dxgi) {
|
||||
dxgi->Release();
|
||||
}
|
||||
}
|
||||
|
||||
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
using output1_t = util::safe_ptr<IDXGIOutput1, Release<IDXGIOutput1>>;
|
||||
using device_t = util::safe_ptr<ID3D11Device, Release<ID3D11Device>>;
|
||||
using dup_t = util::safe_ptr<IDXGIOutputDuplication, Release<IDXGIOutputDuplication>>;
|
||||
using factory1_t = util::safe_ptr<IDXGIFactory1, Release<IDXGIFactory1>>;
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
using output1_t = util::safe_ptr<IDXGIOutput1, Release<IDXGIOutput1>>;
|
||||
using device_t = util::safe_ptr<ID3D11Device, Release<ID3D11Device>>;
|
||||
using dup_t = util::safe_ptr<IDXGIOutputDuplication, Release<IDXGIOutputDuplication>>;
|
||||
|
||||
} // namespace dxgi
|
||||
|
||||
LSTATUS set_gpu_preference(int preference) {
|
||||
LSTATUS
|
||||
set_gpu_preference(int preference) {
|
||||
// The GPU preferences key uses app path as the value name.
|
||||
WCHAR executable_path[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, executable_path, ARRAYSIZE(executable_path));
|
||||
|
|
@ -38,7 +40,7 @@ LSTATUS set_gpu_preference(int preference) {
|
|||
REG_SZ,
|
||||
value_data,
|
||||
(wcslen(value_data) + 1) * sizeof(WCHAR));
|
||||
if(status != ERROR_SUCCESS) {
|
||||
if (status != ERROR_SUCCESS) {
|
||||
std::cout << "Failed to set GPU preference: "sv << status << std::endl;
|
||||
return status;
|
||||
}
|
||||
|
|
@ -46,7 +48,8 @@ LSTATUS set_gpu_preference(int preference) {
|
|||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
HRESULT test_dxgi_duplication(dxgi::adapter_t &adapter, dxgi::output_t &output) {
|
||||
HRESULT
|
||||
test_dxgi_duplication(dxgi::adapter_t &adapter, dxgi::output_t &output) {
|
||||
D3D_FEATURE_LEVEL featureLevels[] {
|
||||
D3D_FEATURE_LEVEL_11_1,
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
|
|
@ -68,41 +71,42 @@ HRESULT test_dxgi_duplication(dxgi::adapter_t &adapter, dxgi::output_t &output)
|
|||
&device,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if(FAILED(status)) {
|
||||
if (FAILED(status)) {
|
||||
std::cout << "Failed to create D3D11 device for DD test [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
return status;
|
||||
}
|
||||
|
||||
dxgi::output1_t output1;
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1);
|
||||
if(FAILED(status)) {
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **) &output1);
|
||||
if (FAILED(status)) {
|
||||
std::cout << "Failed to query IDXGIOutput1 from the output"sv << std::endl;
|
||||
return status;
|
||||
}
|
||||
|
||||
// Return the result of DuplicateOutput() to Sunshine
|
||||
dxgi::dup_t dup;
|
||||
return output1->DuplicateOutput((IUnknown *)device.get(), &dup);
|
||||
return output1->DuplicateOutput((IUnknown *) device.get(), &dup);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
HRESULT status;
|
||||
|
||||
// Display name may be omitted
|
||||
if(argc != 2 && argc != 3) {
|
||||
if (argc != 2 && argc != 3) {
|
||||
std::cout << "ddprobe.exe [GPU preference value] [display name]"sv << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::wstring display_name;
|
||||
if(argc == 3) {
|
||||
if (argc == 3) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
|
||||
display_name = converter.from_bytes(argv[2]);
|
||||
}
|
||||
|
||||
// We must set the GPU preference before making any DXGI/D3D calls
|
||||
status = set_gpu_preference(atoi(argv[1]));
|
||||
if(status != ERROR_SUCCESS) {
|
||||
if (status != ERROR_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
@ -117,30 +121,30 @@ int main(int argc, char *argv[]) {
|
|||
});
|
||||
|
||||
dxgi::factory1_t factory;
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory);
|
||||
if(FAILED(status)) {
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory);
|
||||
if (FAILED(status)) {
|
||||
std::cout << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']' << std::endl;
|
||||
return status;
|
||||
}
|
||||
|
||||
dxgi::adapter_t::pointer adapter_p {};
|
||||
for(int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) {
|
||||
for (int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) {
|
||||
dxgi::adapter_t adapter { adapter_p };
|
||||
|
||||
dxgi::output_t::pointer output_p {};
|
||||
for(int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
for (int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
dxgi::output_t output { output_p };
|
||||
|
||||
DXGI_OUTPUT_DESC desc;
|
||||
output->GetDesc(&desc);
|
||||
|
||||
// If a display name was specified and this one doesn't match, skip it
|
||||
if(!display_name.empty() && desc.DeviceName != display_name) {
|
||||
if (!display_name.empty() && desc.DeviceName != display_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this display is not part of the desktop, we definitely can't capture it
|
||||
if(!desc.AttachedToDesktop) {
|
||||
if (!desc.AttachedToDesktop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue