style: adjust clang-format rules (#2186)
Co-authored-by: Vithorio Polten <reach@vithor.io>
This commit is contained in:
parent
f57aee9025
commit
c2420427b1
158 changed files with 8754 additions and 9994 deletions
|
|
@ -2,20 +2,21 @@
|
|||
* @file src/platform/linux/audio.cpp
|
||||
* @brief Definitions for audio control on Linux.
|
||||
*/
|
||||
// standard includes
|
||||
#include <bitset>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
// lib includes
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <pulse/error.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <pulse/simple.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
// local includes
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/thread_safe.h"
|
||||
|
||||
namespace platf {
|
||||
|
|
@ -32,8 +33,7 @@ namespace platf {
|
|||
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=float channels="sv << channels << " channel_map="sv;
|
||||
|
|
@ -53,8 +53,7 @@ namespace platf {
|
|||
struct mic_attr_t: public mic_t {
|
||||
util::safe_ptr<pa_simple, pa_simple_free> mic;
|
||||
|
||||
capture_e
|
||||
sample(std::vector<float> &sample_buf) override {
|
||||
capture_e sample(std::vector<float> &sample_buf) override {
|
||||
auto sample_size = sample_buf.size();
|
||||
|
||||
auto buf = sample_buf.data();
|
||||
|
|
@ -69,11 +68,10 @@ namespace platf {
|
|||
}
|
||||
};
|
||||
|
||||
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_FLOAT32, sample_rate, (std::uint8_t) channels };
|
||||
pa_sample_spec ss {PA_SAMPLE_FLOAT32, sample_rate, (std::uint8_t) channels};
|
||||
pa_channel_map pa_map;
|
||||
|
||||
pa_map.channels = channels;
|
||||
|
|
@ -92,9 +90,8 @@ namespace platf {
|
|||
int status;
|
||||
|
||||
mic->mic.reset(
|
||||
pa_simple_new(nullptr, "sunshine",
|
||||
pa_stream_direction_t::PA_STREAM_RECORD, source_name.c_str(),
|
||||
"sunshine-record", &ss, &pa_map, &pa_attr, &status));
|
||||
pa_simple_new(nullptr, "sunshine", pa_stream_direction_t::PA_STREAM_RECORD, source_name.c_str(), "sunshine-record", &ss, &pa_map, &pa_attr, &status)
|
||||
);
|
||||
|
||||
if (!mic->mic) {
|
||||
auto err_str = pa_strerror(status);
|
||||
|
|
@ -106,38 +103,37 @@ namespace platf {
|
|||
}
|
||||
|
||||
namespace pa {
|
||||
template <bool B, class T>
|
||||
template<bool B, class T>
|
||||
struct add_const_helper;
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
struct add_const_helper<true, T> {
|
||||
using type = const std::remove_pointer_t<T> *;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
struct add_const_helper<false, T> {
|
||||
using type = const T *;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
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>>;
|
||||
|
||||
template <class T>
|
||||
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) {
|
||||
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
|
||||
|
|
@ -145,12 +141,11 @@ namespace platf {
|
|||
f(ctx, i);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
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) {
|
||||
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
|
||||
|
|
@ -161,22 +156,19 @@ namespace platf {
|
|||
f(ctx, i, eol);
|
||||
}
|
||||
|
||||
void
|
||||
cb_i(ctx_t::pointer ctx, std::uint32_t i, void *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) {
|
||||
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;
|
||||
|
|
@ -205,8 +197,8 @@ namespace platf {
|
|||
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"));
|
||||
|
|
@ -262,8 +254,7 @@ namespace platf {
|
|||
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 {
|
||||
|
|
@ -272,15 +263,15 @@ namespace platf {
|
|||
"module-null-sink",
|
||||
to_string(name, channel_mapping, channels).c_str(),
|
||||
cb_i,
|
||||
alarm.get()),
|
||||
alarm.get()
|
||||
),
|
||||
};
|
||||
|
||||
alarm->wait();
|
||||
return *alarm->status();
|
||||
}
|
||||
|
||||
int
|
||||
unload_null(std::uint32_t i) {
|
||||
int unload_null(std::uint32_t i) {
|
||||
if (i == PA_INVALID_INDEX) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -301,8 +292,7 @@ namespace platf {
|
|||
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";
|
||||
|
|
@ -331,20 +321,18 @@ namespace platf {
|
|||
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;
|
||||
}
|
||||
};
|
||||
|
||||
op_t op { pa_context_get_sink_info_list(ctx.get(), cb<pa_sink_info *>, &f) };
|
||||
op_t op {pa_context_get_sink_info_list(ctx.get(), cb<pa_sink_info *>, &f)};
|
||||
|
||||
if (!op) {
|
||||
BOOST_LOG(error) << "Couldn't create card info operation: "sv << pa_strerror(pa_context_errno(ctx.get()));
|
||||
|
|
@ -365,8 +353,7 @@ namespace platf {
|
|||
index.stereo = load_null(stereo, speaker::map_stereo, sizeof(speaker::map_stereo));
|
||||
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 {
|
||||
} else {
|
||||
++nullcount;
|
||||
}
|
||||
}
|
||||
|
|
@ -375,8 +362,7 @@ namespace platf {
|
|||
index.surround51 = load_null(surround51, speaker::map_surround51, sizeof(speaker::map_surround51));
|
||||
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 {
|
||||
} else {
|
||||
++nullcount;
|
||||
}
|
||||
}
|
||||
|
|
@ -385,8 +371,7 @@ namespace platf {
|
|||
index.surround71 = load_null(surround71, speaker::map_surround71, sizeof(speaker::map_surround71));
|
||||
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 {
|
||||
} else {
|
||||
++nullcount;
|
||||
}
|
||||
}
|
||||
|
|
@ -396,14 +381,13 @@ namespace platf {
|
|||
}
|
||||
|
||||
if (nullcount == 3) {
|
||||
sink.null = std::make_optional(sink_t::null_t { stereo, surround51, surround71 });
|
||||
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>();
|
||||
|
||||
|
|
@ -419,14 +403,13 @@ namespace platf {
|
|||
alarm->ring(0);
|
||||
};
|
||||
|
||||
op_t server_op { pa_context_get_server_info(ctx.get(), cb<pa_server_info *>, &server_f) };
|
||||
op_t server_op {pa_context_get_server_info(ctx.get(), cb<pa_server_info *>, &server_f)};
|
||||
alarm->wait();
|
||||
// No need to check status. If it failed just return default name.
|
||||
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>();
|
||||
|
||||
|
|
@ -449,7 +432,7 @@ namespace platf {
|
|||
monitor_name = sink_info->monitor_source_name;
|
||||
};
|
||||
|
||||
op_t sink_op { pa_context_get_sink_info_by_name(ctx.get(), sink_name.c_str(), cb<pa_sink_info *>, &sink_f) };
|
||||
op_t sink_op {pa_context_get_sink_info_by_name(ctx.get(), sink_name.c_str(), cb<pa_sink_info *>, &sink_f)};
|
||||
|
||||
alarm->wait();
|
||||
// No need to check status. If it failed just return default name.
|
||||
|
|
@ -457,8 +440,7 @@ namespace platf {
|
|||
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)
|
||||
|
|
@ -467,26 +449,32 @@ namespace platf {
|
|||
// 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));
|
||||
}
|
||||
|
||||
bool
|
||||
is_sink_available(const std::string &sink) override {
|
||||
bool is_sink_available(const std::string &sink) override {
|
||||
BOOST_LOG(warning) << "audio_control_t::is_sink_available() unimplemented: "sv << sink;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
op_t op {
|
||||
pa_context_set_default_sink(
|
||||
ctx.get(), sink.c_str(), success_cb, alarm.get()),
|
||||
ctx.get(),
|
||||
sink.c_str(),
|
||||
success_cb,
|
||||
alarm.get()
|
||||
),
|
||||
};
|
||||
|
||||
if (!op) {
|
||||
|
|
@ -525,8 +513,7 @@ namespace platf {
|
|||
};
|
||||
} // 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()) {
|
||||
|
|
@ -535,4 +522,4 @@ namespace platf {
|
|||
|
||||
return audio;
|
||||
}
|
||||
} // namespace platf
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
* @file src/platform/linux/cuda.cpp
|
||||
* @brief Definitions for CUDA encoding.
|
||||
*/
|
||||
// standard includes
|
||||
#include <bitset>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
#include <NvFBC.h>
|
||||
// lib includes
|
||||
#include <ffnvcodec/dynlink_loader.h>
|
||||
#include <NvFBC.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
|
@ -16,6 +18,7 @@ extern "C" {
|
|||
#include <libavutil/imgutils.h>
|
||||
}
|
||||
|
||||
// local includes
|
||||
#include "cuda.h"
|
||||
#include "graphics.h"
|
||||
#include "src/logging.h"
|
||||
|
|
@ -27,7 +30,8 @@ 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 ": "))
|
||||
|
|
@ -35,17 +39,16 @@ extern "C" {
|
|||
namespace fs = std::filesystem;
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace cuda {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -53,8 +56,7 @@ namespace cuda {
|
|||
|
||||
static cdf_t cdf;
|
||||
|
||||
inline static int
|
||||
check(CUresult result, const std::string_view &sv) {
|
||||
inline static int check(CUresult result, const std::string_view &sv) {
|
||||
if (result != CUDA_SUCCESS) {
|
||||
const char *name;
|
||||
const char *description;
|
||||
|
|
@ -69,13 +71,11 @@ namespace cuda {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
freeStream(CUstream stream) {
|
||||
void freeStream(CUstream stream) {
|
||||
CU_CHECK_IGNORE(cdf->cuStreamDestroy(stream), "Couldn't destroy cuda stream");
|
||||
}
|
||||
|
||||
void
|
||||
unregisterResource(CUgraphicsResource resource) {
|
||||
void unregisterResource(CUgraphicsResource resource) {
|
||||
CU_CHECK_IGNORE(cdf->cuGraphicsUnregisterResource(resource), "Couldn't unregister resource");
|
||||
}
|
||||
|
||||
|
|
@ -86,8 +86,7 @@ namespace cuda {
|
|||
tex_t tex;
|
||||
};
|
||||
|
||||
int
|
||||
init() {
|
||||
int init() {
|
||||
auto status = cuda_load_functions(&cdf, nullptr);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't load cuda: "sv << status;
|
||||
|
|
@ -102,8 +101,7 @@ namespace cuda {
|
|||
|
||||
class cuda_t: public platf::avcodec_encode_device_t {
|
||||
public:
|
||||
int
|
||||
init(int in_width, int in_height) {
|
||||
int init(int in_width, int in_height) {
|
||||
if (!cdf) {
|
||||
BOOST_LOG(warning) << "cuda not initialized"sv;
|
||||
return -1;
|
||||
|
|
@ -117,8 +115,7 @@ namespace cuda {
|
|||
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;
|
||||
|
||||
|
|
@ -156,8 +153,7 @@ namespace cuda {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
apply_colorspace() override {
|
||||
void apply_colorspace() override {
|
||||
sws.apply_colorspace(colorspace);
|
||||
|
||||
auto tex = tex_t::make(height, width * 4);
|
||||
|
|
@ -182,11 +178,10 @@ namespace cuda {
|
|||
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 });
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -203,13 +198,11 @@ namespace cuda {
|
|||
|
||||
class cuda_ram_t: public cuda_t {
|
||||
public:
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
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) {
|
||||
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
||||
if (cuda_t::set_frame(frame, hw_frames_ctx)) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -229,8 +222,7 @@ namespace cuda {
|
|||
|
||||
class cuda_vram_t: public cuda_t {
|
||||
public:
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
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());
|
||||
}
|
||||
};
|
||||
|
|
@ -240,8 +232,7 @@ namespace cuda {
|
|||
* @param index CUDA device index to open.
|
||||
* @return File descriptor or -1 on failure.
|
||||
*/
|
||||
file_t
|
||||
open_drm_fd_for_cuda_device(int index) {
|
||||
file_t open_drm_fd_for_cuda_device(int index) {
|
||||
CUdevice device;
|
||||
CU_CHECK(cdf->cuDeviceGet(&device, index), "Couldn't get CUDA device");
|
||||
|
||||
|
|
@ -252,29 +243,29 @@ namespace cuda {
|
|||
BOOST_LOG(debug) << "Found CUDA device with PCI bus ID: "sv << pci_bus_id.data();
|
||||
|
||||
// Linux uses lowercase hexadecimal while CUDA uses uppercase
|
||||
std::transform(pci_bus_id.begin(), pci_bus_id.end(), pci_bus_id.begin(),
|
||||
[](char c) { return std::tolower(c); });
|
||||
std::transform(pci_bus_id.begin(), pci_bus_id.end(), pci_bus_id.begin(), [](char c) {
|
||||
return std::tolower(c);
|
||||
});
|
||||
|
||||
// Look for the name of the primary node in sysfs
|
||||
try {
|
||||
char sysfs_path[PATH_MAX];
|
||||
std::snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/pci/devices/%s/drm", pci_bus_id.data());
|
||||
fs::path sysfs_dir { sysfs_path };
|
||||
for (auto &entry : fs::directory_iterator { sysfs_dir }) {
|
||||
fs::path sysfs_dir {sysfs_path};
|
||||
for (auto &entry : fs::directory_iterator {sysfs_dir}) {
|
||||
auto file = entry.path().filename();
|
||||
auto filestring = file.generic_string();
|
||||
if (std::string_view { filestring }.substr(0, 4) != "card"sv) {
|
||||
if (std::string_view {filestring}.substr(0, 4) != "card"sv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << "Found DRM primary node: "sv << filestring;
|
||||
|
||||
fs::path dri_path { "/dev/dri"sv };
|
||||
fs::path dri_path {"/dev/dri"sv};
|
||||
auto device_path = dri_path / file;
|
||||
return open(device_path.c_str(), O_RDWR);
|
||||
}
|
||||
}
|
||||
catch (const std::filesystem::filesystem_error &err) {
|
||||
} catch (const std::filesystem::filesystem_error &err) {
|
||||
BOOST_LOG(error) << "Failed to read sysfs: "sv << err.what();
|
||||
}
|
||||
|
||||
|
|
@ -292,8 +283,7 @@ namespace cuda {
|
|||
* @param offset_y Offset of content in captured frame.
|
||||
* @return 0 on success or -1 on failure.
|
||||
*/
|
||||
int
|
||||
init(int in_width, int in_height, int offset_x, int offset_y) {
|
||||
int init(int in_width, int in_height, int offset_x, int offset_y) {
|
||||
// This must be non-zero to tell the video core that it's a hardware encoding device.
|
||||
data = (void *) 0x1;
|
||||
|
||||
|
|
@ -340,8 +330,7 @@ namespace cuda {
|
|||
* @param hw_frames_ctx_buf FFmpeg hardware frame context.
|
||||
* @return 0 on success or -1 on failure.
|
||||
*/
|
||||
int
|
||||
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx_buf) override {
|
||||
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx_buf) override {
|
||||
this->hwframe.reset(frame);
|
||||
this->frame = frame;
|
||||
|
||||
|
|
@ -377,10 +366,8 @@ namespace cuda {
|
|||
|
||||
cuda_ctx->stream = stream.get();
|
||||
|
||||
CU_CHECK(cdf->cuGraphicsGLRegisterImage(&y_res, nv12->tex[0], GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY),
|
||||
"Couldn't register Y plane texture");
|
||||
CU_CHECK(cdf->cuGraphicsGLRegisterImage(&uv_res, nv12->tex[1], GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY),
|
||||
"Couldn't register UV plane texture");
|
||||
CU_CHECK(cdf->cuGraphicsGLRegisterImage(&y_res, nv12->tex[0], GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY), "Couldn't register Y plane texture");
|
||||
CU_CHECK(cdf->cuGraphicsGLRegisterImage(&uv_res, nv12->tex[1], GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY), "Couldn't register UV plane texture");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -390,15 +377,13 @@ namespace cuda {
|
|||
* @param img Captured screen image.
|
||||
* @return 0 on success or -1 on failure.
|
||||
*/
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
int convert(platf::img_t &img) override {
|
||||
auto &descriptor = (egl::img_descriptor_t &) img;
|
||||
|
||||
if (descriptor.sequence == 0) {
|
||||
// For dummy images, use a blank RGB texture instead of importing a DMA-BUF
|
||||
rgb = egl::create_blank(img);
|
||||
}
|
||||
else if (descriptor.sequence > sequence) {
|
||||
} else if (descriptor.sequence > sequence) {
|
||||
sequence = descriptor.sequence;
|
||||
|
||||
rgb = egl::rgb_t {};
|
||||
|
|
@ -419,7 +404,7 @@ namespace cuda {
|
|||
auto fmt_desc = av_pix_fmt_desc_get(sw_format);
|
||||
|
||||
// Map the GL textures to read for CUDA
|
||||
CUgraphicsResource resources[2] = { y_res.get(), uv_res.get() };
|
||||
CUgraphicsResource resources[2] = {y_res.get(), uv_res.get()};
|
||||
CU_CHECK(cdf->cuGraphicsMapResources(2, resources, stream.get()), "Couldn't map GL textures in CUDA");
|
||||
|
||||
// Copy from the GL textures to the target CUDA frame
|
||||
|
|
@ -445,8 +430,7 @@ namespace cuda {
|
|||
/**
|
||||
* @brief Configures shader parameters for the specified colorspace.
|
||||
*/
|
||||
void
|
||||
apply_colorspace() override {
|
||||
void apply_colorspace() override {
|
||||
sws.apply_colorspace(colorspace);
|
||||
}
|
||||
|
||||
|
|
@ -474,8 +458,7 @@ namespace cuda {
|
|||
int offset_x, offset_y;
|
||||
};
|
||||
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(int width, int height, bool vram) {
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(int width, int height, bool vram) {
|
||||
if (init()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -484,8 +467,7 @@ namespace cuda {
|
|||
|
||||
if (vram) {
|
||||
cuda = std::make_unique<cuda_vram_t>();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
cuda = std::make_unique<cuda_ram_t>();
|
||||
}
|
||||
|
||||
|
|
@ -504,8 +486,7 @@ namespace cuda {
|
|||
* @param offset_y Offset of content in captured frame.
|
||||
* @return FFmpeg encoding device context.
|
||||
*/
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_gl_encode_device(int width, int height, int offset_x, int offset_y) {
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_gl_encode_device(int width, int height, int offset_x, int offset_y) {
|
||||
if (init()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -521,29 +502,30 @@ namespace cuda {
|
|||
|
||||
namespace nvfbc {
|
||||
static PNVFBCCREATEINSTANCE createInstance {};
|
||||
static NVFBC_API_FUNCTION_LIST func { NVFBC_VERSION };
|
||||
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) {
|
||||
handle = dyn::handle({ "libnvidia-fbc.so.1", "libnvidia-fbc.so" });
|
||||
handle = dyn::handle({"libnvidia-fbc.so.1", "libnvidia-fbc.so"});
|
||||
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)) {
|
||||
|
|
@ -569,7 +551,7 @@ namespace cuda {
|
|||
class ctx_t {
|
||||
public:
|
||||
ctx_t(NVFBC_SESSION_HANDLE handle) {
|
||||
NVFBC_BIND_CONTEXT_PARAMS params { NVFBC_BIND_CONTEXT_PARAMS_VER };
|
||||
NVFBC_BIND_CONTEXT_PARAMS params {NVFBC_BIND_CONTEXT_PARAMS_VER};
|
||||
|
||||
if (func.nvFBCBindContext(handle, ¶ms)) {
|
||||
BOOST_LOG(error) << "Couldn't bind NvFBC context to current thread: " << func.nvFBCGetLastErrorStr(handle);
|
||||
|
|
@ -579,7 +561,7 @@ namespace cuda {
|
|||
}
|
||||
|
||||
~ctx_t() {
|
||||
NVFBC_RELEASE_CONTEXT_PARAMS params { NVFBC_RELEASE_CONTEXT_PARAMS_VER };
|
||||
NVFBC_RELEASE_CONTEXT_PARAMS params {NVFBC_RELEASE_CONTEXT_PARAMS_VER};
|
||||
if (func.nvFBCReleaseContext(handle, ¶ms)) {
|
||||
BOOST_LOG(error) << "Couldn't release NvFBC context from current thread: " << func.nvFBCGetLastErrorStr(handle);
|
||||
}
|
||||
|
|
@ -597,26 +579,26 @@ namespace cuda {
|
|||
|
||||
public:
|
||||
handle_t() = default;
|
||||
|
||||
handle_t(handle_t &&other):
|
||||
handle_flags { other.handle_flags }, handle { other.handle } {
|
||||
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() {
|
||||
NVFBC_CREATE_HANDLE_PARAMS params { NVFBC_CREATE_HANDLE_PARAMS_VER };
|
||||
static std::optional<handle_t> make() {
|
||||
NVFBC_CREATE_HANDLE_PARAMS params {NVFBC_CREATE_HANDLE_PARAMS_VER};
|
||||
|
||||
// Set privateData to allow NvFBC on consumer NVIDIA GPUs.
|
||||
// Based on https://github.com/keylase/nvidia-patch/blob/3193b4b1cea91527bf09ea9b8db5aade6a3f3c0a/win/nvfbcwrp/nvfbcwrp_main.cpp#L23-L25 .
|
||||
const unsigned int MAGIC_PRIVATE_DATA[4] = { 0xAEF57AC5, 0x401D1A39, 0x1B856BBE, 0x9ED0CEBA };
|
||||
const unsigned int MAGIC_PRIVATE_DATA[4] = {0xAEF57AC5, 0x401D1A39, 0x1B856BBE, 0x9ED0CEBA};
|
||||
params.privateData = MAGIC_PRIVATE_DATA;
|
||||
params.privateDataSize = sizeof(MAGIC_PRIVATE_DATA);
|
||||
|
||||
|
|
@ -633,14 +615,12 @@ namespace cuda {
|
|||
return handle;
|
||||
}
|
||||
|
||||
const char *
|
||||
last_error() {
|
||||
const char *last_error() {
|
||||
return func.nvFBCGetLastErrorStr(handle);
|
||||
}
|
||||
|
||||
std::optional<NVFBC_GET_STATUS_PARAMS>
|
||||
status() {
|
||||
NVFBC_GET_STATUS_PARAMS params { NVFBC_GET_STATUS_PARAMS_VER };
|
||||
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) {
|
||||
|
|
@ -652,8 +632,7 @@ namespace cuda {
|
|||
return params;
|
||||
}
|
||||
|
||||
int
|
||||
capture(NVFBC_CREATE_CAPTURE_SESSION_PARAMS &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;
|
||||
|
|
@ -673,13 +652,12 @@ namespace cuda {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
stop() {
|
||||
int stop() {
|
||||
if (!handle_flags[SESSION_CAPTURE]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NVFBC_DESTROY_CAPTURE_SESSION_PARAMS params { NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER };
|
||||
NVFBC_DESTROY_CAPTURE_SESSION_PARAMS params {NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER};
|
||||
|
||||
if (func.nvFBCDestroyCaptureSession(handle, ¶ms)) {
|
||||
BOOST_LOG(error) << "Couldn't destroy capture session: "sv << last_error();
|
||||
|
|
@ -692,17 +670,16 @@ namespace cuda {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
reset() {
|
||||
int reset() {
|
||||
if (!handle_flags[SESSION_HANDLE]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
NVFBC_DESTROY_HANDLE_PARAMS params { NVFBC_DESTROY_HANDLE_PARAMS_VER };
|
||||
NVFBC_DESTROY_HANDLE_PARAMS params {NVFBC_DESTROY_HANDLE_PARAMS_VER};
|
||||
|
||||
ctx_t ctx { handle };
|
||||
ctx_t ctx {handle};
|
||||
if (func.nvFBCDestroyHandle(handle, ¶ms)) {
|
||||
BOOST_LOG(error) << "Couldn't destroy session handle: "sv << func.nvFBCGetLastErrorStr(handle);
|
||||
}
|
||||
|
|
@ -723,14 +700,13 @@ namespace cuda {
|
|||
|
||||
class display_t: public platf::display_t {
|
||||
public:
|
||||
int
|
||||
init(const std::string_view &display_name, const ::video::config_t &config) {
|
||||
int init(const std::string_view &display_name, const ::video::config_t &config) {
|
||||
auto handle = handle_t::make();
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx_t ctx { handle->handle };
|
||||
ctx_t ctx {handle->handle};
|
||||
|
||||
auto status_params = handle->status();
|
||||
if (!status_params) {
|
||||
|
|
@ -744,19 +720,17 @@ namespace cuda {
|
|||
|
||||
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 {
|
||||
} else {
|
||||
streamedMonitor = monitor_nr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(warning) << "XrandR not available, streaming entire virtual desktop"sv;
|
||||
}
|
||||
}
|
||||
|
||||
delay = std::chrono::nanoseconds { 1s } / config.framerate;
|
||||
delay = std::chrono::nanoseconds {1s} / config.framerate;
|
||||
|
||||
capture_params = NVFBC_CREATE_CAPTURE_SESSION_PARAMS { NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER };
|
||||
capture_params = NVFBC_CREATE_CAPTURE_SESSION_PARAMS {NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER};
|
||||
|
||||
capture_params.eCaptureType = NVFBC_CAPTURE_SHARED_CUDA;
|
||||
capture_params.bDisableAutoModesetRecovery = nv_bool(true);
|
||||
|
|
@ -773,8 +747,7 @@ namespace cuda {
|
|||
|
||||
capture_params.eTrackingType = NVFBC_TRACKING_OUTPUT;
|
||||
capture_params.dwOutputId = output.dwId;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
capture_params.eTrackingType = NVFBC_TRACKING_SCREEN;
|
||||
|
||||
width = status_params->screenSize.w;
|
||||
|
|
@ -788,8 +761,7 @@ namespace cuda {
|
|||
return 0;
|
||||
}
|
||||
|
||||
platf::capture_e
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
platf::capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
{
|
||||
|
|
@ -802,7 +774,7 @@ namespace cuda {
|
|||
// Force display_t::capture to initialize handle_t::capture
|
||||
cursor_visible = !*cursor;
|
||||
|
||||
ctx_t ctx { handle.handle };
|
||||
ctx_t ctx {handle.handle};
|
||||
auto fg = util::fail_guard([&]() {
|
||||
handle.reset();
|
||||
});
|
||||
|
|
@ -849,8 +821,7 @@ namespace cuda {
|
|||
}
|
||||
|
||||
// Reinitialize the capture session.
|
||||
platf::capture_e
|
||||
reinit(bool cursor) {
|
||||
platf::capture_e reinit(bool cursor) {
|
||||
if (handle.stop()) {
|
||||
return platf::capture_e::error;
|
||||
}
|
||||
|
|
@ -860,8 +831,7 @@ namespace cuda {
|
|||
capture_params.bPushModel = nv_bool(false);
|
||||
capture_params.bWithCursor = nv_bool(true);
|
||||
capture_params.bAllowDirectCapture = nv_bool(false);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
capture_params.bPushModel = nv_bool(true);
|
||||
capture_params.bWithCursor = nv_bool(false);
|
||||
capture_params.bAllowDirectCapture = nv_bool(true);
|
||||
|
|
@ -919,8 +889,7 @@ namespace cuda {
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
platf::capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
if (cursor != cursor_visible) {
|
||||
auto status = reinit(cursor);
|
||||
if (status != platf::capture_e::ok) {
|
||||
|
|
@ -960,13 +929,11 @@ namespace cuda {
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) {
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) {
|
||||
return ::cuda::make_avcodec_encode_device(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;
|
||||
|
|
@ -985,8 +952,7 @@ namespace cuda {
|
|||
return img;
|
||||
};
|
||||
|
||||
int
|
||||
dummy_img(platf::img_t *) override {
|
||||
int dummy_img(platf::img_t *) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1001,8 +967,7 @@ namespace cuda {
|
|||
} // 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) {
|
||||
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;
|
||||
|
|
@ -1017,8 +982,7 @@ namespace platf {
|
|||
return display;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
nvfbc_display_names() {
|
||||
std::vector<std::string> nvfbc_display_names() {
|
||||
if (cuda::init() || cuda::nvfbc::init()) {
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@
|
|||
* @file src/platform/linux/cuda.cu
|
||||
* @brief CUDA implementation for Linux.
|
||||
*/
|
||||
// #include <algorithm>
|
||||
#include <helper_math.h>
|
||||
// standard includes
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
// platform includes
|
||||
#include <helper_math.h>
|
||||
|
||||
// local includes
|
||||
#include "cuda.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
|
@ -18,16 +21,20 @@ using namespace std::literals;
|
|||
#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_VOID(x, y) \
|
||||
if(check((x), SUNSHINE_STRINGVIEW(y ": "))) return;
|
||||
if (check((x), SUNSHINE_STRINGVIEW(y ": "))) \
|
||||
return;
|
||||
|
||||
#define CU_CHECK_PTR(x, y) \
|
||||
if(check((x), SUNSHINE_STRINGVIEW(y ": "))) return nullptr;
|
||||
if (check((x), SUNSHINE_STRINGVIEW(y ": "))) \
|
||||
return nullptr;
|
||||
|
||||
#define CU_CHECK_OPT(x, y) \
|
||||
if(check((x), SUNSHINE_STRINGVIEW(y ": "))) return std::nullopt;
|
||||
if (check((x), SUNSHINE_STRINGVIEW(y ": "))) \
|
||||
return std::nullopt;
|
||||
|
||||
#define CU_CHECK_IGNORE(x, y) \
|
||||
check((x), SUNSHINE_STRINGVIEW(y ": "))
|
||||
|
|
@ -42,277 +49,293 @@ using namespace std::literals;
|
|||
* Not pretty and extremely error-prone, fix at earliest convenience.
|
||||
*/
|
||||
namespace platf {
|
||||
struct img_t: std::enable_shared_from_this<img_t> {
|
||||
public:
|
||||
std::uint8_t *data {};
|
||||
std::int32_t width {};
|
||||
std::int32_t height {};
|
||||
std::int32_t pixel_pitch {};
|
||||
std::int32_t row_pitch {};
|
||||
struct img_t: std::enable_shared_from_this<img_t> {
|
||||
public:
|
||||
std::uint8_t *data {};
|
||||
std::int32_t width {};
|
||||
std::int32_t height {};
|
||||
std::int32_t pixel_pitch {};
|
||||
std::int32_t row_pitch {};
|
||||
|
||||
std::optional<std::chrono::steady_clock::time_point> frame_timestamp;
|
||||
std::optional<std::chrono::steady_clock::time_point> frame_timestamp;
|
||||
|
||||
virtual ~img_t() = default;
|
||||
};
|
||||
} // namespace platf
|
||||
virtual ~img_t() = default;
|
||||
};
|
||||
} // namespace platf
|
||||
|
||||
// End special declarations
|
||||
|
||||
namespace cuda {
|
||||
|
||||
struct alignas(16) cuda_color_t {
|
||||
float4 color_vec_y;
|
||||
float4 color_vec_u;
|
||||
float4 color_vec_v;
|
||||
float2 range_y;
|
||||
float2 range_uv;
|
||||
};
|
||||
struct alignas(16) cuda_color_t {
|
||||
float4 color_vec_y;
|
||||
float4 color_vec_u;
|
||||
float4 color_vec_v;
|
||||
float2 range_y;
|
||||
float2 range_uv;
|
||||
};
|
||||
|
||||
static_assert(sizeof(video::color_t) == sizeof(cuda::cuda_color_t), "color matrix struct mismatch");
|
||||
static_assert(sizeof(video::color_t) == sizeof(cuda::cuda_color_t), "color matrix struct mismatch");
|
||||
|
||||
auto constexpr INVALID_TEXTURE = std::numeric_limits<cudaTextureObject_t>::max();
|
||||
auto constexpr INVALID_TEXTURE = std::numeric_limits<cudaTextureObject_t>::max();
|
||||
|
||||
template<class T>
|
||||
inline T div_align(T l, T r) {
|
||||
return (l + r - 1) / r;
|
||||
}
|
||||
|
||||
void pass_error(const std::string_view &sv, const char *name, const char *description);
|
||||
inline static int check(cudaError_t result, const std::string_view &sv) {
|
||||
if(result) {
|
||||
auto name = cudaGetErrorName(result);
|
||||
auto description = cudaGetErrorString(result);
|
||||
|
||||
pass_error(sv, name, description);
|
||||
return -1;
|
||||
template<class T>
|
||||
inline T div_align(T l, T r) {
|
||||
return (l + r - 1) / r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
void pass_error(const std::string_view &sv, const char *name, const char *description);
|
||||
|
||||
template<class T>
|
||||
ptr_t make_ptr() {
|
||||
void *p;
|
||||
CU_CHECK_PTR(cudaMalloc(&p, sizeof(T)), "Couldn't allocate color matrix");
|
||||
inline static int check(cudaError_t result, const std::string_view &sv) {
|
||||
if (result) {
|
||||
auto name = cudaGetErrorName(result);
|
||||
auto description = cudaGetErrorString(result);
|
||||
|
||||
ptr_t ptr { p };
|
||||
pass_error(sv, name, description);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void freeCudaPtr_t::operator()(void *ptr) {
|
||||
CU_CHECK_IGNORE(cudaFree(ptr), "Couldn't free cuda device pointer");
|
||||
}
|
||||
|
||||
void freeCudaStream_t::operator()(cudaStream_t ptr) {
|
||||
CU_CHECK_IGNORE(cudaStreamDestroy(ptr), "Couldn't free cuda stream");
|
||||
}
|
||||
|
||||
stream_t make_stream(int flags) {
|
||||
cudaStream_t stream;
|
||||
|
||||
if(!flags) {
|
||||
CU_CHECK_PTR(cudaStreamCreate(&stream), "Couldn't create cuda stream");
|
||||
}
|
||||
else {
|
||||
CU_CHECK_PTR(cudaStreamCreateWithFlags(&stream, flags), "Couldn't create cuda stream with flags");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return stream_t { stream };
|
||||
}
|
||||
template<class T>
|
||||
ptr_t make_ptr() {
|
||||
void *p;
|
||||
CU_CHECK_PTR(cudaMalloc(&p, sizeof(T)), "Couldn't allocate color matrix");
|
||||
|
||||
inline __device__ float3 bgra_to_rgb(uchar4 vec) {
|
||||
return make_float3((float)vec.z, (float)vec.y, (float)vec.x);
|
||||
}
|
||||
ptr_t ptr {p};
|
||||
|
||||
inline __device__ float3 bgra_to_rgb(float4 vec) {
|
||||
return make_float3(vec.z, vec.y, vec.x);
|
||||
}
|
||||
|
||||
inline __device__ float2 calcUV(float3 pixel, const cuda_color_t *const color_matrix) {
|
||||
float4 vec_u = color_matrix->color_vec_u;
|
||||
float4 vec_v = color_matrix->color_vec_v;
|
||||
|
||||
float u = dot(pixel, make_float3(vec_u)) + vec_u.w;
|
||||
float v = dot(pixel, make_float3(vec_v)) + vec_v.w;
|
||||
|
||||
u = u * color_matrix->range_uv.x + color_matrix->range_uv.y;
|
||||
v = v * color_matrix->range_uv.x + color_matrix->range_uv.y;
|
||||
|
||||
return make_float2(u, v);
|
||||
}
|
||||
|
||||
inline __device__ float calcY(float3 pixel, const cuda_color_t *const color_matrix) {
|
||||
float4 vec_y = color_matrix->color_vec_y;
|
||||
|
||||
return (dot(pixel, make_float3(vec_y)) + vec_y.w) * color_matrix->range_y.x + color_matrix->range_y.y;
|
||||
}
|
||||
|
||||
__global__ void RGBA_to_NV12(
|
||||
cudaTextureObject_t srcImage, std::uint8_t *dstY, std::uint8_t *dstUV,
|
||||
std::uint32_t dstPitchY, std::uint32_t dstPitchUV,
|
||||
float scale, const viewport_t viewport, const cuda_color_t *const color_matrix) {
|
||||
|
||||
int idX = (threadIdx.x + blockDim.x * blockIdx.x) * 2;
|
||||
int idY = (threadIdx.y + blockDim.y * blockIdx.y) * 2;
|
||||
|
||||
if(idX >= viewport.width) return;
|
||||
if(idY >= viewport.height) return;
|
||||
|
||||
float x = idX * scale;
|
||||
float y = idY * scale;
|
||||
|
||||
idX += viewport.offsetX;
|
||||
idY += viewport.offsetY;
|
||||
|
||||
uint8_t *dstY0 = dstY + idX + idY * dstPitchY;
|
||||
uint8_t *dstY1 = dstY + idX + (idY + 1) * dstPitchY;
|
||||
dstUV = dstUV + idX + (idY / 2 * dstPitchUV);
|
||||
|
||||
float3 rgb_lt = bgra_to_rgb(tex2D<float4>(srcImage, x, y));
|
||||
float3 rgb_rt = bgra_to_rgb(tex2D<float4>(srcImage, x + scale, y));
|
||||
float3 rgb_lb = bgra_to_rgb(tex2D<float4>(srcImage, x, y + scale));
|
||||
float3 rgb_rb = bgra_to_rgb(tex2D<float4>(srcImage, x + scale, y + scale));
|
||||
|
||||
float2 uv_lt = calcUV(rgb_lt, color_matrix) * 256.0f;
|
||||
float2 uv_rt = calcUV(rgb_rt, color_matrix) * 256.0f;
|
||||
float2 uv_lb = calcUV(rgb_lb, color_matrix) * 256.0f;
|
||||
float2 uv_rb = calcUV(rgb_rb, color_matrix) * 256.0f;
|
||||
|
||||
float2 uv = (uv_lt + uv_lb + uv_rt + uv_rb) * 0.25f;
|
||||
|
||||
dstUV[0] = uv.x;
|
||||
dstUV[1] = uv.y;
|
||||
dstY0[0] = calcY(rgb_lt, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible
|
||||
dstY0[1] = calcY(rgb_rt, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible
|
||||
dstY1[0] = calcY(rgb_lb, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible
|
||||
dstY1[1] = calcY(rgb_rb, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible
|
||||
}
|
||||
|
||||
int tex_t::copy(std::uint8_t *src, int height, int pitch) {
|
||||
CU_CHECK(cudaMemcpy2DToArray(array, 0, 0, src, pitch, pitch, height, cudaMemcpyDeviceToDevice), "Couldn't copy to cuda array from deviceptr");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<tex_t> tex_t::make(int height, int pitch) {
|
||||
tex_t tex;
|
||||
|
||||
auto format = cudaCreateChannelDesc<uchar4>();
|
||||
CU_CHECK_OPT(cudaMallocArray(&tex.array, &format, pitch, height, cudaArrayDefault), "Couldn't allocate cuda array");
|
||||
|
||||
cudaResourceDesc res {};
|
||||
res.resType = cudaResourceTypeArray;
|
||||
res.res.array.array = tex.array;
|
||||
|
||||
cudaTextureDesc desc {};
|
||||
|
||||
desc.readMode = cudaReadModeNormalizedFloat;
|
||||
desc.filterMode = cudaFilterModePoint;
|
||||
desc.normalizedCoords = false;
|
||||
|
||||
std::fill_n(std::begin(desc.addressMode), 2, cudaAddressModeClamp);
|
||||
|
||||
CU_CHECK_OPT(cudaCreateTextureObject(&tex.texture.point, &res, &desc, nullptr), "Couldn't create cuda texture that uses point interpolation");
|
||||
|
||||
desc.filterMode = cudaFilterModeLinear;
|
||||
|
||||
CU_CHECK_OPT(cudaCreateTextureObject(&tex.texture.linear, &res, &desc, nullptr), "Couldn't create cuda texture that uses linear interpolation");
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
tex_t::tex_t() : array {}, texture { INVALID_TEXTURE, INVALID_TEXTURE } {}
|
||||
tex_t::tex_t(tex_t &&other) : array { other.array }, texture { other.texture } {
|
||||
other.array = 0;
|
||||
other.texture.point = INVALID_TEXTURE;
|
||||
other.texture.linear = INVALID_TEXTURE;
|
||||
}
|
||||
|
||||
tex_t &tex_t::operator=(tex_t &&other) {
|
||||
std::swap(array, other.array);
|
||||
std::swap(texture, other.texture);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
tex_t::~tex_t() {
|
||||
if(texture.point != INVALID_TEXTURE) {
|
||||
CU_CHECK_IGNORE(cudaDestroyTextureObject(texture.point), "Couldn't deallocate cuda texture that uses point interpolation");
|
||||
|
||||
texture.point = INVALID_TEXTURE;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
if(texture.linear != INVALID_TEXTURE) {
|
||||
CU_CHECK_IGNORE(cudaDestroyTextureObject(texture.linear), "Couldn't deallocate cuda texture that uses linear interpolation");
|
||||
|
||||
texture.linear = INVALID_TEXTURE;
|
||||
void freeCudaPtr_t::operator()(void *ptr) {
|
||||
CU_CHECK_IGNORE(cudaFree(ptr), "Couldn't free cuda device pointer");
|
||||
}
|
||||
|
||||
if(array) {
|
||||
CU_CHECK_IGNORE(cudaFreeArray(array), "Couldn't deallocate cuda array");
|
||||
|
||||
array = cudaArray_t {};
|
||||
}
|
||||
}
|
||||
|
||||
sws_t::sws_t(int in_width, int in_height, int out_width, int out_height, int pitch, int threadsPerBlock, ptr_t &&color_matrix)
|
||||
: threadsPerBlock { threadsPerBlock }, color_matrix { std::move(color_matrix) } {
|
||||
// Ensure aspect ratio is maintained
|
||||
auto scalar = std::fminf(out_width / (float)in_width, out_height / (float)in_height);
|
||||
auto out_width_f = in_width * scalar;
|
||||
auto out_height_f = in_height * scalar;
|
||||
|
||||
// result is always positive
|
||||
auto offsetX_f = (out_width - out_width_f) / 2;
|
||||
auto offsetY_f = (out_height - out_height_f) / 2;
|
||||
|
||||
viewport.width = out_width_f;
|
||||
viewport.height = out_height_f;
|
||||
|
||||
viewport.offsetX = offsetX_f;
|
||||
viewport.offsetY = offsetY_f;
|
||||
|
||||
scale = 1.0f / scalar;
|
||||
}
|
||||
|
||||
std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int out_height, int pitch) {
|
||||
cudaDeviceProp props;
|
||||
int device;
|
||||
CU_CHECK_OPT(cudaGetDevice(&device), "Couldn't get cuda device");
|
||||
CU_CHECK_OPT(cudaGetDeviceProperties(&props, device), "Couldn't get cuda device properties");
|
||||
|
||||
auto ptr = make_ptr<cuda_color_t>();
|
||||
if(!ptr) {
|
||||
return std::nullopt;
|
||||
void freeCudaStream_t::operator()(cudaStream_t ptr) {
|
||||
CU_CHECK_IGNORE(cudaStreamDestroy(ptr), "Couldn't free cuda stream");
|
||||
}
|
||||
|
||||
return std::make_optional<sws_t>(in_width, in_height, out_width, out_height, pitch, props.maxThreadsPerMultiProcessor / props.maxBlocksPerMultiProcessor, std::move(ptr));
|
||||
}
|
||||
stream_t make_stream(int flags) {
|
||||
cudaStream_t stream;
|
||||
|
||||
int sws_t::convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream) {
|
||||
return convert(Y, UV, pitchY, pitchUV, texture, stream, viewport);
|
||||
}
|
||||
if (!flags) {
|
||||
CU_CHECK_PTR(cudaStreamCreate(&stream), "Couldn't create cuda stream");
|
||||
} else {
|
||||
CU_CHECK_PTR(cudaStreamCreateWithFlags(&stream, flags), "Couldn't create cuda stream with flags");
|
||||
}
|
||||
|
||||
int sws_t::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 threadsX = viewport.width / 2;
|
||||
int threadsY = viewport.height / 2;
|
||||
return stream_t {stream};
|
||||
}
|
||||
|
||||
dim3 block(threadsPerBlock);
|
||||
dim3 grid(div_align(threadsX, threadsPerBlock), threadsY);
|
||||
inline __device__ float3 bgra_to_rgb(uchar4 vec) {
|
||||
return make_float3((float) vec.z, (float) vec.y, (float) vec.x);
|
||||
}
|
||||
|
||||
RGBA_to_NV12<<<grid, block, 0, stream>>>(texture, Y, UV, pitchY, pitchUV, scale, viewport, (cuda_color_t *)color_matrix.get());
|
||||
inline __device__ float3 bgra_to_rgb(float4 vec) {
|
||||
return make_float3(vec.z, vec.y, vec.x);
|
||||
}
|
||||
|
||||
return CU_CHECK_IGNORE(cudaGetLastError(), "RGBA_to_NV12 failed");
|
||||
}
|
||||
inline __device__ float2 calcUV(float3 pixel, const cuda_color_t *const color_matrix) {
|
||||
float4 vec_u = color_matrix->color_vec_u;
|
||||
float4 vec_v = color_matrix->color_vec_v;
|
||||
|
||||
void sws_t::apply_colorspace(const video::sunshine_colorspace_t& colorspace) {
|
||||
auto color_p = video::color_vectors_from_colorspace(colorspace);
|
||||
CU_CHECK_IGNORE(cudaMemcpy(color_matrix.get(), color_p, sizeof(video::color_t), cudaMemcpyHostToDevice), "Couldn't copy color matrix to cuda");
|
||||
}
|
||||
float u = dot(pixel, make_float3(vec_u)) + vec_u.w;
|
||||
float v = dot(pixel, make_float3(vec_v)) + vec_v.w;
|
||||
|
||||
int sws_t::load_ram(platf::img_t &img, cudaArray_t array) {
|
||||
return CU_CHECK_IGNORE(cudaMemcpy2DToArray(array, 0, 0, img.data, img.row_pitch, img.width * img.pixel_pitch, img.height, cudaMemcpyHostToDevice), "Couldn't copy to cuda array");
|
||||
}
|
||||
u = u * color_matrix->range_uv.x + color_matrix->range_uv.y;
|
||||
v = v * color_matrix->range_uv.x + color_matrix->range_uv.y;
|
||||
|
||||
} // namespace cuda
|
||||
return make_float2(u, v);
|
||||
}
|
||||
|
||||
inline __device__ float calcY(float3 pixel, const cuda_color_t *const color_matrix) {
|
||||
float4 vec_y = color_matrix->color_vec_y;
|
||||
|
||||
return (dot(pixel, make_float3(vec_y)) + vec_y.w) * color_matrix->range_y.x + color_matrix->range_y.y;
|
||||
}
|
||||
|
||||
__global__ void RGBA_to_NV12(
|
||||
cudaTextureObject_t srcImage,
|
||||
std::uint8_t *dstY,
|
||||
std::uint8_t *dstUV,
|
||||
std::uint32_t dstPitchY,
|
||||
std::uint32_t dstPitchUV,
|
||||
float scale,
|
||||
const viewport_t viewport,
|
||||
const cuda_color_t *const color_matrix
|
||||
) {
|
||||
int idX = (threadIdx.x + blockDim.x * blockIdx.x) * 2;
|
||||
int idY = (threadIdx.y + blockDim.y * blockIdx.y) * 2;
|
||||
|
||||
if (idX >= viewport.width) {
|
||||
return;
|
||||
}
|
||||
if (idY >= viewport.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
float x = idX * scale;
|
||||
float y = idY * scale;
|
||||
|
||||
idX += viewport.offsetX;
|
||||
idY += viewport.offsetY;
|
||||
|
||||
uint8_t *dstY0 = dstY + idX + idY * dstPitchY;
|
||||
uint8_t *dstY1 = dstY + idX + (idY + 1) * dstPitchY;
|
||||
dstUV = dstUV + idX + (idY / 2 * dstPitchUV);
|
||||
|
||||
float3 rgb_lt = bgra_to_rgb(tex2D<float4>(srcImage, x, y));
|
||||
float3 rgb_rt = bgra_to_rgb(tex2D<float4>(srcImage, x + scale, y));
|
||||
float3 rgb_lb = bgra_to_rgb(tex2D<float4>(srcImage, x, y + scale));
|
||||
float3 rgb_rb = bgra_to_rgb(tex2D<float4>(srcImage, x + scale, y + scale));
|
||||
|
||||
float2 uv_lt = calcUV(rgb_lt, color_matrix) * 256.0f;
|
||||
float2 uv_rt = calcUV(rgb_rt, color_matrix) * 256.0f;
|
||||
float2 uv_lb = calcUV(rgb_lb, color_matrix) * 256.0f;
|
||||
float2 uv_rb = calcUV(rgb_rb, color_matrix) * 256.0f;
|
||||
|
||||
float2 uv = (uv_lt + uv_lb + uv_rt + uv_rb) * 0.25f;
|
||||
|
||||
dstUV[0] = uv.x;
|
||||
dstUV[1] = uv.y;
|
||||
dstY0[0] = calcY(rgb_lt, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible
|
||||
dstY0[1] = calcY(rgb_rt, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible
|
||||
dstY1[0] = calcY(rgb_lb, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible
|
||||
dstY1[1] = calcY(rgb_rb, color_matrix) * 245.0f; // 245.0f is a magic number to ensure slight changes in luminosity are more visible
|
||||
}
|
||||
|
||||
int tex_t::copy(std::uint8_t *src, int height, int pitch) {
|
||||
CU_CHECK(cudaMemcpy2DToArray(array, 0, 0, src, pitch, pitch, height, cudaMemcpyDeviceToDevice), "Couldn't copy to cuda array from deviceptr");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<tex_t> tex_t::make(int height, int pitch) {
|
||||
tex_t tex;
|
||||
|
||||
auto format = cudaCreateChannelDesc<uchar4>();
|
||||
CU_CHECK_OPT(cudaMallocArray(&tex.array, &format, pitch, height, cudaArrayDefault), "Couldn't allocate cuda array");
|
||||
|
||||
cudaResourceDesc res {};
|
||||
res.resType = cudaResourceTypeArray;
|
||||
res.res.array.array = tex.array;
|
||||
|
||||
cudaTextureDesc desc {};
|
||||
|
||||
desc.readMode = cudaReadModeNormalizedFloat;
|
||||
desc.filterMode = cudaFilterModePoint;
|
||||
desc.normalizedCoords = false;
|
||||
|
||||
std::fill_n(std::begin(desc.addressMode), 2, cudaAddressModeClamp);
|
||||
|
||||
CU_CHECK_OPT(cudaCreateTextureObject(&tex.texture.point, &res, &desc, nullptr), "Couldn't create cuda texture that uses point interpolation");
|
||||
|
||||
desc.filterMode = cudaFilterModeLinear;
|
||||
|
||||
CU_CHECK_OPT(cudaCreateTextureObject(&tex.texture.linear, &res, &desc, nullptr), "Couldn't create cuda texture that uses linear interpolation");
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
tex_t::tex_t():
|
||||
array {},
|
||||
texture {INVALID_TEXTURE, INVALID_TEXTURE} {
|
||||
}
|
||||
|
||||
tex_t::tex_t(tex_t &&other):
|
||||
array {other.array},
|
||||
texture {other.texture} {
|
||||
other.array = 0;
|
||||
other.texture.point = INVALID_TEXTURE;
|
||||
other.texture.linear = INVALID_TEXTURE;
|
||||
}
|
||||
|
||||
tex_t &tex_t::operator=(tex_t &&other) {
|
||||
std::swap(array, other.array);
|
||||
std::swap(texture, other.texture);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
tex_t::~tex_t() {
|
||||
if (texture.point != INVALID_TEXTURE) {
|
||||
CU_CHECK_IGNORE(cudaDestroyTextureObject(texture.point), "Couldn't deallocate cuda texture that uses point interpolation");
|
||||
|
||||
texture.point = INVALID_TEXTURE;
|
||||
}
|
||||
|
||||
if (texture.linear != INVALID_TEXTURE) {
|
||||
CU_CHECK_IGNORE(cudaDestroyTextureObject(texture.linear), "Couldn't deallocate cuda texture that uses linear interpolation");
|
||||
|
||||
texture.linear = INVALID_TEXTURE;
|
||||
}
|
||||
|
||||
if (array) {
|
||||
CU_CHECK_IGNORE(cudaFreeArray(array), "Couldn't deallocate cuda array");
|
||||
|
||||
array = cudaArray_t {};
|
||||
}
|
||||
}
|
||||
|
||||
sws_t::sws_t(int in_width, int in_height, int out_width, int out_height, int pitch, int threadsPerBlock, ptr_t &&color_matrix):
|
||||
threadsPerBlock {threadsPerBlock},
|
||||
color_matrix {std::move(color_matrix)} {
|
||||
// Ensure aspect ratio is maintained
|
||||
auto scalar = std::fminf(out_width / (float) in_width, out_height / (float) in_height);
|
||||
auto out_width_f = in_width * scalar;
|
||||
auto out_height_f = in_height * scalar;
|
||||
|
||||
// result is always positive
|
||||
auto offsetX_f = (out_width - out_width_f) / 2;
|
||||
auto offsetY_f = (out_height - out_height_f) / 2;
|
||||
|
||||
viewport.width = out_width_f;
|
||||
viewport.height = out_height_f;
|
||||
|
||||
viewport.offsetX = offsetX_f;
|
||||
viewport.offsetY = offsetY_f;
|
||||
|
||||
scale = 1.0f / scalar;
|
||||
}
|
||||
|
||||
std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int out_height, int pitch) {
|
||||
cudaDeviceProp props;
|
||||
int device;
|
||||
CU_CHECK_OPT(cudaGetDevice(&device), "Couldn't get cuda device");
|
||||
CU_CHECK_OPT(cudaGetDeviceProperties(&props, device), "Couldn't get cuda device properties");
|
||||
|
||||
auto ptr = make_ptr<cuda_color_t>();
|
||||
if (!ptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::make_optional<sws_t>(in_width, in_height, out_width, out_height, pitch, props.maxThreadsPerMultiProcessor / props.maxBlocksPerMultiProcessor, std::move(ptr));
|
||||
}
|
||||
|
||||
int sws_t::convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream) {
|
||||
return convert(Y, UV, pitchY, pitchUV, texture, stream, viewport);
|
||||
}
|
||||
|
||||
int sws_t::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 threadsX = viewport.width / 2;
|
||||
int threadsY = viewport.height / 2;
|
||||
|
||||
dim3 block(threadsPerBlock);
|
||||
dim3 grid(div_align(threadsX, threadsPerBlock), threadsY);
|
||||
|
||||
RGBA_to_NV12<<<grid, block, 0, stream>>>(texture, Y, UV, pitchY, pitchUV, scale, viewport, (cuda_color_t *) color_matrix.get());
|
||||
|
||||
return CU_CHECK_IGNORE(cudaGetLastError(), "RGBA_to_NV12 failed");
|
||||
}
|
||||
|
||||
void sws_t::apply_colorspace(const video::sunshine_colorspace_t &colorspace) {
|
||||
auto color_p = video::color_vectors_from_colorspace(colorspace);
|
||||
CU_CHECK_IGNORE(cudaMemcpy(color_matrix.get(), color_p, sizeof(video::color_t), cudaMemcpyHostToDevice), "Couldn't copy color matrix to cuda");
|
||||
}
|
||||
|
||||
int sws_t::load_ram(platf::img_t &img, cudaArray_t array) {
|
||||
return CU_CHECK_IGNORE(cudaMemcpy2DToArray(array, 0, 0, img.data, img.row_pitch, img.width * img.pixel_pitch, img.height, cudaMemcpyHostToDevice), "Couldn't copy to cuda array");
|
||||
}
|
||||
|
||||
} // namespace cuda
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@
|
|||
#pragma once
|
||||
|
||||
#if defined(SUNSHINE_BUILD_CUDA)
|
||||
|
||||
#include "src/video_colorspace.h"
|
||||
|
||||
// standard includes
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// local includes
|
||||
#include "src/video_colorspace.h"
|
||||
|
||||
namespace platf {
|
||||
class avcodec_encode_device_t;
|
||||
class img_t;
|
||||
|
|
@ -22,11 +23,10 @@ namespace platf {
|
|||
namespace cuda {
|
||||
|
||||
namespace nvfbc {
|
||||
std::vector<std::string>
|
||||
display_names();
|
||||
std::vector<std::string> display_names();
|
||||
}
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(int width, int height, bool vram);
|
||||
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(int width, int height, bool vram);
|
||||
|
||||
/**
|
||||
* @brief Create a GL->CUDA encoding device for consuming captured dmabufs.
|
||||
|
|
@ -36,11 +36,9 @@ namespace cuda {
|
|||
* @param offset_y Offset of content in captured frame.
|
||||
* @return FFmpeg encoding device context.
|
||||
*/
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_gl_encode_device(int width, int height, int offset_x, int offset_y);
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_gl_encode_device(int width, int height, int offset_x, int offset_y);
|
||||
|
||||
int
|
||||
init();
|
||||
int init();
|
||||
} // namespace cuda
|
||||
|
||||
typedef struct cudaArray *cudaArray_t;
|
||||
|
|
@ -57,21 +55,18 @@ namespace cuda {
|
|||
|
||||
class freeCudaPtr_t {
|
||||
public:
|
||||
void
|
||||
operator()(void *ptr);
|
||||
void operator()(void *ptr);
|
||||
};
|
||||
|
||||
class freeCudaStream_t {
|
||||
public:
|
||||
void
|
||||
operator()(cudaStream_t ptr);
|
||||
void operator()(cudaStream_t ptr);
|
||||
};
|
||||
|
||||
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 {
|
||||
int width, height;
|
||||
|
|
@ -80,19 +75,16 @@ namespace cuda {
|
|||
|
||||
class tex_t {
|
||||
public:
|
||||
static std::optional<tex_t>
|
||||
make(int height, int pitch);
|
||||
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;
|
||||
|
||||
|
|
@ -113,20 +105,15 @@ namespace cuda {
|
|||
*
|
||||
* 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
|
||||
apply_colorspace(const video::sunshine_colorspace_t &colorspace);
|
||||
void apply_colorspace(const video::sunshine_colorspace_t &colorspace);
|
||||
|
||||
int
|
||||
load_ram(platf::img_t &img, cudaArray_t array);
|
||||
int load_ram(platf::img_t &img, cudaArray_t array);
|
||||
|
||||
ptr_t color_matrix;
|
||||
|
||||
|
|
@ -138,4 +125,4 @@ namespace cuda {
|
|||
};
|
||||
} // namespace cuda
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
* @file src/platform/linux/graphics.cpp
|
||||
* @brief Definitions for graphics related functions.
|
||||
*/
|
||||
// standard includes
|
||||
#include <fcntl.h>
|
||||
|
||||
// local includes
|
||||
#include "graphics.h"
|
||||
#include "src/file_handler.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/video.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
|
@ -17,8 +19,7 @@ extern "C" {
|
|||
// There aren't that many DRM_FORMAT I need to use, so define them here
|
||||
//
|
||||
// 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_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 DRM_FORMAT_MOD_INVALID fourcc_mod_code(0, ((1ULL << 56) - 1))
|
||||
|
||||
|
|
@ -27,11 +28,11 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace gl {
|
||||
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) {
|
||||
BOOST_LOG(error) << "GL: "sv << prefix << ": ["sv << util::hex(err).to_string_view() << ']';
|
||||
|
|
@ -44,13 +45,12 @@ namespace gl {
|
|||
}
|
||||
}
|
||||
|
||||
tex_t
|
||||
tex_t::make(std::size_t count) {
|
||||
tex_t textures { 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 };
|
||||
float color[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
for (auto tex : textures) {
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, tex);
|
||||
|
|
@ -70,25 +70,22 @@ namespace gl {
|
|||
}
|
||||
}
|
||||
|
||||
frame_buf_t
|
||||
frame_buf_t::make(std::size_t count) {
|
||||
frame_buf_t frame_buf { 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);
|
||||
|
||||
|
|
@ -102,8 +99,7 @@ namespace gl {
|
|||
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();
|
||||
|
|
@ -123,13 +119,11 @@ namespace gl {
|
|||
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();
|
||||
|
|
@ -142,25 +136,21 @@ namespace gl {
|
|||
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());
|
||||
}
|
||||
|
||||
void
|
||||
buffer_t::update(std::string_view *members, std::size_t count, std::size_t offset) {
|
||||
util::buffer_t<std::uint8_t> buffer { _size };
|
||||
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) {
|
||||
auto val = members[x];
|
||||
|
|
@ -171,8 +161,7 @@ namespace gl {
|
|||
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);
|
||||
|
||||
|
|
@ -186,8 +175,7 @@ namespace gl {
|
|||
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();
|
||||
|
|
@ -214,16 +202,14 @@ namespace gl {
|
|||
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) {
|
||||
BOOST_LOG(error) << "Couldn't find index of ["sv << block << ']';
|
||||
|
|
@ -235,7 +221,7 @@ namespace gl {
|
|||
|
||||
bool error_flag = false;
|
||||
|
||||
util::buffer_t<GLint> offsets { count };
|
||||
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 names_p = names;
|
||||
|
|
@ -260,7 +246,7 @@ namespace gl {
|
|||
}
|
||||
|
||||
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) {
|
||||
auto val = std::get<1>(members[x]);
|
||||
|
|
@ -268,11 +254,10 @@ namespace gl {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -282,23 +267,24 @@ namespace gbm {
|
|||
device_destroy_fn device_destroy;
|
||||
create_device_fn create_device;
|
||||
|
||||
int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
int init() {
|
||||
static void *handle {nullptr};
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if (funcs_loaded) return 0;
|
||||
if (funcs_loaded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libgbm.so.1", "libgbm.so" });
|
||||
handle = dyn::handle({"libgbm.so.1", "libgbm.so"});
|
||||
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)) {
|
||||
|
|
@ -334,16 +320,14 @@ namespace egl {
|
|||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof egl::display_t
|
||||
*/
|
||||
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;
|
||||
|
|
@ -408,10 +392,11 @@ namespace egl {
|
|||
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
|
||||
EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
int count;
|
||||
|
|
@ -427,10 +412,12 @@ namespace egl {
|
|||
}
|
||||
|
||||
constexpr int attr[] {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE
|
||||
EGL_CONTEXT_CLIENT_VERSION,
|
||||
3,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
ctx_t ctx { display, eglCreateContext(display, conf, EGL_NO_CONTEXT, attr) };
|
||||
ctx_t ctx {display, eglCreateContext(display, conf, EGL_NO_CONTEXT, attr)};
|
||||
if (fail()) {
|
||||
BOOST_LOG(error) << "Couldn't create EGL context: ["sv << util::hex(eglGetError()).to_string_view() << ']';
|
||||
return std::nullopt;
|
||||
|
|
@ -465,8 +452,7 @@ namespace egl {
|
|||
EGLAttrib hi;
|
||||
};
|
||||
|
||||
inline plane_attr_t
|
||||
get_plane(std::uint32_t plane_indice) {
|
||||
inline plane_attr_t get_plane(std::uint32_t plane_indice) {
|
||||
switch (plane_indice) {
|
||||
case 0:
|
||||
return {
|
||||
|
|
@ -511,8 +497,7 @@ namespace egl {
|
|||
* @param surface The surface descriptor.
|
||||
* @return Vector of EGL attributes.
|
||||
*/
|
||||
std::vector<EGLAttrib>
|
||||
surface_descriptor_to_egl_attribs(const surface_descriptor_t &surface) {
|
||||
std::vector<EGLAttrib> surface_descriptor_to_egl_attribs(const surface_descriptor_t &surface) {
|
||||
std::vector<EGLAttrib> attribs;
|
||||
|
||||
attribs.emplace_back(EGL_WIDTH);
|
||||
|
|
@ -549,8 +534,7 @@ namespace egl {
|
|||
return attribs;
|
||||
}
|
||||
|
||||
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) {
|
||||
auto attribs = surface_descriptor_to_egl_attribs(xrgb);
|
||||
|
||||
rgb_t rgb {
|
||||
|
|
@ -580,8 +564,7 @@ namespace egl {
|
|||
* @param img The image to use for texture sizing.
|
||||
* @return The new RGB texture.
|
||||
*/
|
||||
rgb_t
|
||||
create_blank(platf::img_t &img) {
|
||||
rgb_t create_blank(platf::img_t &img) {
|
||||
rgb_t rgb {
|
||||
EGL_NO_DISPLAY,
|
||||
EGL_NO_IMAGE,
|
||||
|
|
@ -597,7 +580,7 @@ namespace egl {
|
|||
|
||||
GLenum attachment = GL_COLOR_ATTACHMENT0;
|
||||
gl::ctx.DrawBuffers(1, &attachment);
|
||||
const GLuint rgb_black[] = { 0, 0, 0, 0 };
|
||||
const GLuint rgb_black[] = {0, 0, 0, 0};
|
||||
gl::ctx.ClearBufferuiv(GL_COLOR, 0, rgb_black);
|
||||
|
||||
gl_drain_errors;
|
||||
|
|
@ -605,8 +588,7 @@ namespace egl {
|
|||
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 &y, const surface_descriptor_t &uv) {
|
||||
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 &y, const surface_descriptor_t &uv) {
|
||||
auto y_attribs = surface_descriptor_to_egl_attribs(y);
|
||||
auto uv_attribs = surface_descriptor_to_egl_attribs(uv);
|
||||
|
||||
|
|
@ -642,8 +624,8 @@ namespace egl {
|
|||
gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, nv12->buf[x]);
|
||||
gl::ctx.DrawBuffers(1, &attachments[x]);
|
||||
|
||||
const float y_black[] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
const float uv_black[] = { 0.5f, 0.5f, 0.5f, 0.5f };
|
||||
const float y_black[] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
const float uv_black[] = {0.5f, 0.5f, 0.5f, 0.5f};
|
||||
gl::ctx.ClearBufferfv(GL_COLOR, 0, x == 0 ? y_black : uv_black);
|
||||
}
|
||||
|
||||
|
|
@ -661,8 +643,7 @@ namespace egl {
|
|||
* @param format Format of the target frame.
|
||||
* @return The new RGB texture.
|
||||
*/
|
||||
std::optional<nv12_t>
|
||||
create_target(int width, int height, AVPixelFormat format) {
|
||||
std::optional<nv12_t> create_target(int width, int height, AVPixelFormat format) {
|
||||
nv12_t nv12 {
|
||||
EGL_NO_DISPLAY,
|
||||
EGL_NO_IMAGE,
|
||||
|
|
@ -679,12 +660,10 @@ namespace egl {
|
|||
if (fmt_desc->comp[0].depth <= 8) {
|
||||
y_format = GL_R8;
|
||||
uv_format = GL_RG8;
|
||||
}
|
||||
else if (fmt_desc->comp[0].depth <= 16) {
|
||||
} else if (fmt_desc->comp[0].depth <= 16) {
|
||||
y_format = GL_R16;
|
||||
uv_format = GL_RG16;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(error) << "Unsupported target pixel format: "sv << format;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -693,8 +672,7 @@ namespace egl {
|
|||
gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, y_format, width, height);
|
||||
|
||||
gl::ctx.BindTexture(GL_TEXTURE_2D, nv12->tex[1]);
|
||||
gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, uv_format,
|
||||
width >> fmt_desc->log2_chroma_w, height >> fmt_desc->log2_chroma_h);
|
||||
gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, uv_format, width >> fmt_desc->log2_chroma_w, height >> fmt_desc->log2_chroma_h);
|
||||
|
||||
nv12->buf.bind(std::begin(nv12->tex), std::end(nv12->tex));
|
||||
|
||||
|
|
@ -707,8 +685,8 @@ namespace egl {
|
|||
gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, nv12->buf[x]);
|
||||
gl::ctx.DrawBuffers(1, &attachments[x]);
|
||||
|
||||
const float y_black[] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
const float uv_black[] = { 0.5f, 0.5f, 0.5f, 0.5f };
|
||||
const float y_black[] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
const float uv_black[] = {0.5f, 0.5f, 0.5f, 0.5f};
|
||||
gl::ctx.ClearBufferfv(GL_COLOR, 0, x == 0 ? y_black : uv_black);
|
||||
}
|
||||
|
||||
|
|
@ -719,8 +697,7 @@ namespace egl {
|
|||
return nv12;
|
||||
}
|
||||
|
||||
void
|
||||
sws_t::apply_colorspace(const video::sunshine_colorspace_t &colorspace) {
|
||||
void sws_t::apply_colorspace(const video::sunshine_colorspace_t &colorspace) {
|
||||
auto color_p = video::color_vectors_from_colorspace(colorspace);
|
||||
|
||||
std::string_view members[] {
|
||||
|
|
@ -737,8 +714,7 @@ namespace egl {
|
|||
program[1].bind(color_matrix);
|
||||
}
|
||||
|
||||
std::optional<sws_t>
|
||||
sws_t::make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex) {
|
||||
std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex) {
|
||||
sws_t sws;
|
||||
|
||||
sws.serial = std::numeric_limits<std::uint64_t>::max();
|
||||
|
|
@ -866,8 +842,7 @@ namespace egl {
|
|||
return 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);
|
||||
|
|
@ -881,8 +856,7 @@ namespace egl {
|
|||
return convert(fb);
|
||||
}
|
||||
|
||||
std::optional<sws_t>
|
||||
sws_t::make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format) {
|
||||
std::optional<sws_t> sws_t::make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format) {
|
||||
GLint gl_format;
|
||||
|
||||
// Decide the bit depth format of the backing texture based the target frame format
|
||||
|
|
@ -916,16 +890,14 @@ namespace egl {
|
|||
return make(in_width, in_height, out_width, out_height, 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) {
|
||||
|
|
@ -934,8 +906,7 @@ namespace egl {
|
|||
|
||||
loaded_texture = tex[0];
|
||||
framebuf.copy(0, loaded_texture, offset_x, offset_y, in_width, in_height);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
loaded_texture = texture;
|
||||
}
|
||||
|
||||
|
|
@ -985,8 +956,7 @@ namespace egl {
|
|||
}
|
||||
}
|
||||
|
||||
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[] {
|
||||
|
|
@ -1019,7 +989,6 @@ namespace egl {
|
|||
}
|
||||
} // namespace egl
|
||||
|
||||
void
|
||||
free_frame(AVFrame *frame) {
|
||||
void free_frame(AVFrame *frame) {
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
// lib includes
|
||||
#include <glad/egl.h>
|
||||
#include <glad/gl.h>
|
||||
|
||||
// local includes
|
||||
#include "misc.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
|
|
@ -21,35 +24,30 @@
|
|||
#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);
|
||||
void drain_errors(const std::string_view &prefix);
|
||||
|
||||
class tex_t: public util::buffer_t<GLuint> {
|
||||
using util::buffer_t<GLuint>::buffer_t;
|
||||
|
||||
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> {
|
||||
|
|
@ -57,16 +55,13 @@ namespace gl {
|
|||
|
||||
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)) {
|
||||
ctx.BindFramebuffer(GL_FRAMEBUFFER, fb);
|
||||
|
|
@ -77,9 +72,8 @@ namespace gl {
|
|||
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()) {
|
||||
BOOST_LOG(warning) << "To many elements to bind"sv;
|
||||
|
|
@ -100,8 +94,7 @@ namespace gl {
|
|||
/**
|
||||
* 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 {
|
||||
|
|
@ -112,14 +105,11 @@ namespace gl {
|
|||
});
|
||||
|
||||
public:
|
||||
std::string
|
||||
err_str();
|
||||
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:
|
||||
shader_internal_t _shader;
|
||||
|
|
@ -133,19 +123,14 @@ namespace gl {
|
|||
});
|
||||
|
||||
public:
|
||||
static buffer_t
|
||||
make(util::buffer_t<GLint> &&offsets, const char *block, const std::string_view &data);
|
||||
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:
|
||||
const char *_block;
|
||||
|
|
@ -165,20 +150,15 @@ namespace gl {
|
|||
});
|
||||
|
||||
public:
|
||||
std::string
|
||||
err_str();
|
||||
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:
|
||||
program_internal_t _program;
|
||||
|
|
@ -195,8 +175,7 @@ namespace gbm {
|
|||
|
||||
using gbm_t = util::dyn_safe_ptr<device, &device_destroy>;
|
||||
|
||||
int
|
||||
init();
|
||||
int init();
|
||||
|
||||
} // namespace gbm
|
||||
|
||||
|
|
@ -258,24 +237,23 @@ namespace egl {
|
|||
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(
|
||||
display_t::pointer egl_display,
|
||||
const surface_descriptor_t &xrgb);
|
||||
import_source(
|
||||
display_t::pointer egl_display,
|
||||
const surface_descriptor_t &xrgb
|
||||
);
|
||||
|
||||
rgb_t
|
||||
create_blank(platf::img_t &img);
|
||||
rgb_t create_blank(platf::img_t &img);
|
||||
|
||||
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 &y, const surface_descriptor_t &uv);
|
||||
const surface_descriptor_t &y,
|
||||
const surface_descriptor_t &uv
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Creates biplanar YUV textures to render into.
|
||||
|
|
@ -284,8 +262,7 @@ namespace egl {
|
|||
* @param format Format of the target frame.
|
||||
* @return The new RGB texture.
|
||||
*/
|
||||
std::optional<nv12_t>
|
||||
create_target(int width, int height, AVPixelFormat format);
|
||||
std::optional<nv12_t> create_target(int width, int height, AVPixelFormat format);
|
||||
|
||||
class cursor_t: public platf::img_t {
|
||||
public:
|
||||
|
|
@ -304,8 +281,7 @@ namespace egl {
|
|||
reset();
|
||||
}
|
||||
|
||||
void
|
||||
reset() {
|
||||
void reset() {
|
||||
for (auto x = 0; x < 4; ++x) {
|
||||
if (sd.fds[x] >= 0) {
|
||||
close(sd.fds[x]);
|
||||
|
|
@ -323,26 +299,19 @@ namespace egl {
|
|||
|
||||
class sws_t {
|
||||
public:
|
||||
static std::optional<sws_t>
|
||||
make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex);
|
||||
static std::optional<sws_t>
|
||||
make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format);
|
||||
static std::optional<sws_t> make(int in_width, int in_height, int out_width, int out_height, gl::tex_t &&tex);
|
||||
static std::optional<sws_t> make(int in_width, int in_height, int out_width, int out_height, AVPixelFormat format);
|
||||
|
||||
// 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
|
||||
apply_colorspace(const video::sunshine_colorspace_t &colorspace);
|
||||
void apply_colorspace(const video::sunshine_colorspace_t &colorspace);
|
||||
|
||||
// The first texture is the monitor image.
|
||||
// The second texture is the cursor image
|
||||
|
|
@ -367,6 +336,5 @@ namespace egl {
|
|||
std::uint64_t serial;
|
||||
};
|
||||
|
||||
bool
|
||||
fail();
|
||||
bool fail();
|
||||
} // namespace egl
|
||||
|
|
|
|||
|
|
@ -2,132 +2,114 @@
|
|||
* @file src/platform/linux/input/inputtino.cpp
|
||||
* @brief Definitions for the inputtino Linux input handling.
|
||||
*/
|
||||
// lib includes
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_gamepad.h"
|
||||
#include "inputtino_keyboard.h"
|
||||
#include "inputtino_mouse.h"
|
||||
#include "inputtino_pen.h"
|
||||
#include "inputtino_touch.h"
|
||||
#include "src/config.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf {
|
||||
|
||||
input_t
|
||||
input() {
|
||||
return { new input_raw_t() };
|
||||
input_t input() {
|
||||
return {new input_raw_t()};
|
||||
}
|
||||
|
||||
std::unique_ptr<client_input_t>
|
||||
allocate_client_input_context(input_t &input) {
|
||||
std::unique_ptr<client_input_t> allocate_client_input_context(input_t &input) {
|
||||
return std::make_unique<client_input_raw_t>(input);
|
||||
}
|
||||
|
||||
void
|
||||
freeInput(void *p) {
|
||||
void freeInput(void *p) {
|
||||
auto *input = (input_raw_t *) p;
|
||||
delete input;
|
||||
}
|
||||
|
||||
void
|
||||
move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
void move_mouse(input_t &input, int deltaX, int deltaY) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::move(raw, deltaX, deltaY);
|
||||
}
|
||||
|
||||
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) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::move_abs(raw, touch_port, x, y);
|
||||
}
|
||||
|
||||
void
|
||||
button_mouse(input_t &input, int button, bool release) {
|
||||
void button_mouse(input_t &input, int button, bool release) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::button(raw, button, release);
|
||||
}
|
||||
|
||||
void
|
||||
scroll(input_t &input, int high_res_distance) {
|
||||
void scroll(input_t &input, int high_res_distance) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::scroll(raw, high_res_distance);
|
||||
}
|
||||
|
||||
void
|
||||
hscroll(input_t &input, int high_res_distance) {
|
||||
void hscroll(input_t &input, int high_res_distance) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::mouse::hscroll(raw, high_res_distance);
|
||||
}
|
||||
|
||||
void
|
||||
keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
void keyboard_update(input_t &input, uint16_t modcode, bool release, uint8_t flags) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::keyboard::update(raw, modcode, release, flags);
|
||||
}
|
||||
|
||||
void
|
||||
unicode(input_t &input, char *utf8, int size) {
|
||||
void unicode(input_t &input, char *utf8, int size) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::keyboard::unicode(raw, utf8, size);
|
||||
}
|
||||
|
||||
void
|
||||
touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
void touch_update(client_input_t *input, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
auto raw = (client_input_raw_t *) input;
|
||||
platf::touch::update(raw, touch_port, touch);
|
||||
}
|
||||
|
||||
void
|
||||
pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
void pen_update(client_input_t *input, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
auto raw = (client_input_raw_t *) input;
|
||||
platf::pen::update(raw, touch_port, pen);
|
||||
}
|
||||
|
||||
int
|
||||
alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
|
||||
int alloc_gamepad(input_t &input, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
return platf::gamepad::alloc(raw, id, metadata, feedback_queue);
|
||||
}
|
||||
|
||||
void
|
||||
free_gamepad(input_t &input, int nr) {
|
||||
void free_gamepad(input_t &input, int nr) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::free(raw, nr);
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
void gamepad_update(input_t &input, int nr, const gamepad_state_t &gamepad_state) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::update(raw, nr, gamepad_state);
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_touch(input_t &input, const gamepad_touch_t &touch) {
|
||||
void gamepad_touch(input_t &input, const gamepad_touch_t &touch) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::touch(raw, touch);
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_motion(input_t &input, const gamepad_motion_t &motion) {
|
||||
void gamepad_motion(input_t &input, const gamepad_motion_t &motion) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::motion(raw, motion);
|
||||
}
|
||||
|
||||
void
|
||||
gamepad_battery(input_t &input, const gamepad_battery_t &battery) {
|
||||
void gamepad_battery(input_t &input, const gamepad_battery_t &battery) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
platf::gamepad::battery(raw, battery);
|
||||
}
|
||||
|
||||
platform_caps::caps_t
|
||||
get_capabilities() {
|
||||
platform_caps::caps_t get_capabilities() {
|
||||
platform_caps::caps_t caps = 0;
|
||||
// TODO: if has_uinput
|
||||
caps |= platform_caps::pen_touch;
|
||||
|
|
@ -140,14 +122,12 @@ namespace platf {
|
|||
return caps;
|
||||
}
|
||||
|
||||
util::point_t
|
||||
get_mouse_loc(input_t &input) {
|
||||
util::point_t get_mouse_loc(input_t &input) {
|
||||
auto raw = (input_raw_t *) input.get();
|
||||
return platf::mouse::get_location(raw);
|
||||
}
|
||||
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input) {
|
||||
std::vector<supported_gamepad_t> &supported_gamepads(input_t *input) {
|
||||
return platf::gamepad::supported_gamepads(input);
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
// local includes
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
|
|
@ -94,8 +96,7 @@ namespace platf {
|
|||
inputtino::Result<inputtino::PenTablet> pen;
|
||||
};
|
||||
|
||||
inline float
|
||||
deg2rad(float degree) {
|
||||
inline float deg2rad(float degree) {
|
||||
return degree * (M_PI / 180.f);
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -2,18 +2,19 @@
|
|||
* @file src/platform/linux/input/inputtino_gamepad.cpp
|
||||
* @brief Definitions for inputtino gamepad input handling.
|
||||
*/
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_gamepad.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_gamepad.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::gamepad {
|
||||
|
|
@ -25,69 +26,54 @@ namespace platf::gamepad {
|
|||
GAMEPAD_STATUS ///< Helper to indicate the number of status
|
||||
};
|
||||
|
||||
auto
|
||||
create_xbox_one() {
|
||||
return inputtino::XboxOneJoypad::create({ .name = "Sunshine X-Box One (virtual) pad",
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c#L147
|
||||
.vendor_id = 0x045E,
|
||||
.product_id = 0x02EA,
|
||||
.version = 0x0408 });
|
||||
auto create_xbox_one() {
|
||||
return inputtino::XboxOneJoypad::create({.name = "Sunshine X-Box One (virtual) pad",
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c#L147
|
||||
.vendor_id = 0x045E,
|
||||
.product_id = 0x02EA,
|
||||
.version = 0x0408});
|
||||
}
|
||||
|
||||
auto
|
||||
create_switch() {
|
||||
return inputtino::SwitchJoypad::create({ .name = "Sunshine Nintendo (virtual) pad",
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L981
|
||||
.vendor_id = 0x057e,
|
||||
.product_id = 0x2009,
|
||||
.version = 0x8111 });
|
||||
auto create_switch() {
|
||||
return inputtino::SwitchJoypad::create({.name = "Sunshine Nintendo (virtual) pad",
|
||||
// https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L981
|
||||
.vendor_id = 0x057e,
|
||||
.product_id = 0x2009,
|
||||
.version = 0x8111});
|
||||
}
|
||||
|
||||
auto
|
||||
create_ds5() {
|
||||
return inputtino::PS5Joypad::create({ .name = "Sunshine DualSense (virtual) pad",
|
||||
.vendor_id = 0x054C,
|
||||
.product_id = 0x0CE6,
|
||||
.version = 0x8111 });
|
||||
auto create_ds5() {
|
||||
return inputtino::PS5Joypad::create({.name = "Sunshine DualSense (virtual) pad", .vendor_id = 0x054C, .product_id = 0x0CE6, .version = 0x8111});
|
||||
}
|
||||
|
||||
int
|
||||
alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
|
||||
int alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
|
||||
ControllerType selectedGamepadType;
|
||||
|
||||
if (config::input.gamepad == "xone"sv) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (manual selection)"sv;
|
||||
selectedGamepadType = XboxOneWired;
|
||||
}
|
||||
else if (config::input.gamepad == "ds5"sv) {
|
||||
} else if (config::input.gamepad == "ds5"sv) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualSense 5 controller (manual selection)"sv;
|
||||
selectedGamepadType = DualSenseWired;
|
||||
}
|
||||
else if (config::input.gamepad == "switch"sv) {
|
||||
} else if (config::input.gamepad == "switch"sv) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Nintendo Pro controller (manual selection)"sv;
|
||||
selectedGamepadType = SwitchProWired;
|
||||
}
|
||||
else if (metadata.type == LI_CTYPE_XBOX) {
|
||||
} else if (metadata.type == LI_CTYPE_XBOX) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (auto-selected by client-reported type)"sv;
|
||||
selectedGamepadType = XboxOneWired;
|
||||
}
|
||||
else if (metadata.type == LI_CTYPE_PS) {
|
||||
} else if (metadata.type == LI_CTYPE_PS) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by client-reported type)"sv;
|
||||
selectedGamepadType = DualSenseWired;
|
||||
}
|
||||
else if (metadata.type == LI_CTYPE_NINTENDO) {
|
||||
} else if (metadata.type == LI_CTYPE_NINTENDO) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Nintendo Pro controller (auto-selected by client-reported type)"sv;
|
||||
selectedGamepadType = SwitchProWired;
|
||||
}
|
||||
else if (config::input.motion_as_ds4 && (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) {
|
||||
} else if (config::input.motion_as_ds4 && (metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by motion sensor presence)"sv;
|
||||
selectedGamepadType = DualSenseWired;
|
||||
}
|
||||
else if (config::input.touchpad_as_ds4 && (metadata.capabilities & LI_CCAP_TOUCHPAD)) {
|
||||
} else if (config::input.touchpad_as_ds4 && (metadata.capabilities & LI_CCAP_TOUCHPAD)) {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be DualShock 5 controller (auto-selected by touchpad presence)"sv;
|
||||
selectedGamepadType = DualSenseWired;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(info) << "Gamepad " << id.globalIndex << " will be Xbox One controller (default)"sv;
|
||||
selectedGamepadType = XboxOneWired;
|
||||
}
|
||||
|
|
@ -102,8 +88,7 @@ namespace platf::gamepad {
|
|||
if (metadata.capabilities & LI_CCAP_RGB_LED) {
|
||||
BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " has an RGB LED, but it is not usable when emulating a joypad different from DS5"sv;
|
||||
}
|
||||
}
|
||||
else if (selectedGamepadType == DualSenseWired) {
|
||||
} else if (selectedGamepadType == DualSenseWired) {
|
||||
if (!(metadata.capabilities & (LI_CCAP_ACCEL | LI_CCAP_GYRO))) {
|
||||
BOOST_LOG(warning) << "Gamepad " << id.globalIndex << " is emulating a DualShock 5 controller, but the client gamepad doesn't have motion sensors active"sv;
|
||||
}
|
||||
|
|
@ -125,73 +110,71 @@ namespace platf::gamepad {
|
|||
};
|
||||
|
||||
switch (selectedGamepadType) {
|
||||
case XboxOneWired: {
|
||||
auto xOne = create_xbox_one();
|
||||
if (xOne) {
|
||||
(*xOne).set_on_rumble(on_rumble_fn);
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*xOne));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
case XboxOneWired:
|
||||
{
|
||||
auto xOne = create_xbox_one();
|
||||
if (xOne) {
|
||||
(*xOne).set_on_rumble(on_rumble_fn);
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*xOne));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
} else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual Xbox One controller: " << xOne.getErrorMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual Xbox One controller: " << xOne.getErrorMessage();
|
||||
return -1;
|
||||
case SwitchProWired:
|
||||
{
|
||||
auto switchPro = create_switch();
|
||||
if (switchPro) {
|
||||
(*switchPro).set_on_rumble(on_rumble_fn);
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*switchPro));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
} else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual Switch Pro controller: " << switchPro.getErrorMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
case SwitchProWired: {
|
||||
auto switchPro = create_switch();
|
||||
if (switchPro) {
|
||||
(*switchPro).set_on_rumble(on_rumble_fn);
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*switchPro));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual Switch Pro controller: " << switchPro.getErrorMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case DualSenseWired: {
|
||||
auto ds5 = create_ds5();
|
||||
if (ds5) {
|
||||
(*ds5).set_on_rumble(on_rumble_fn);
|
||||
(*ds5).set_on_led([feedback_queue, idx = id.clientRelativeIndex, gamepad](int r, int g, int b) {
|
||||
// Don't resend duplicate LED data
|
||||
if (gamepad->last_rgb_led.type == platf::gamepad_feedback_e::set_rgb_led && gamepad->last_rgb_led.data.rgb_led.r == r && gamepad->last_rgb_led.data.rgb_led.g == g && gamepad->last_rgb_led.data.rgb_led.b == b) {
|
||||
return;
|
||||
}
|
||||
case DualSenseWired:
|
||||
{
|
||||
auto ds5 = create_ds5();
|
||||
if (ds5) {
|
||||
(*ds5).set_on_rumble(on_rumble_fn);
|
||||
(*ds5).set_on_led([feedback_queue, idx = id.clientRelativeIndex, gamepad](int r, int g, int b) {
|
||||
// Don't resend duplicate LED data
|
||||
if (gamepad->last_rgb_led.type == platf::gamepad_feedback_e::set_rgb_led && gamepad->last_rgb_led.data.rgb_led.r == r && gamepad->last_rgb_led.data.rgb_led.g == g && gamepad->last_rgb_led.data.rgb_led.b == b) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto msg = gamepad_feedback_msg_t::make_rgb_led(idx, r, g, b);
|
||||
feedback_queue->raise(msg);
|
||||
gamepad->last_rgb_led = msg;
|
||||
});
|
||||
auto msg = gamepad_feedback_msg_t::make_rgb_led(idx, r, g, b);
|
||||
feedback_queue->raise(msg);
|
||||
gamepad->last_rgb_led = msg;
|
||||
});
|
||||
|
||||
// Activate the motion sensors
|
||||
feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_ACCEL, 100));
|
||||
feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_GYRO, 100));
|
||||
// Activate the motion sensors
|
||||
feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_ACCEL, 100));
|
||||
feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_GYRO, 100));
|
||||
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*ds5));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
gamepad->joypad = std::make_unique<joypads_t>(std::move(*ds5));
|
||||
raw->gamepads[id.globalIndex] = std::move(gamepad);
|
||||
return 0;
|
||||
} else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual DualShock 5 controller: " << ds5.getErrorMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Unable to create virtual DualShock 5 controller: " << ds5.getErrorMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
free(input_raw_t *raw, int nr) {
|
||||
void free(input_raw_t *raw, int nr) {
|
||||
// This will call the destructor which in turn will stop the background threads for rumble and LED (and ultimately remove the joypad device)
|
||||
raw->gamepads[nr]->joypad.reset();
|
||||
raw->gamepads[nr].reset();
|
||||
}
|
||||
|
||||
void
|
||||
update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state) {
|
||||
void update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state) {
|
||||
auto gamepad = raw->gamepads[nr];
|
||||
if (!gamepad) {
|
||||
return;
|
||||
|
|
@ -203,11 +186,10 @@ namespace platf::gamepad {
|
|||
gc.set_stick(inputtino::Joypad::RS, gamepad_state.rsX, gamepad_state.rsY);
|
||||
gc.set_triggers(gamepad_state.lt, gamepad_state.rt);
|
||||
},
|
||||
*gamepad->joypad);
|
||||
*gamepad->joypad);
|
||||
}
|
||||
|
||||
void
|
||||
touch(input_raw_t *raw, const gamepad_touch_t &touch) {
|
||||
void touch(input_raw_t *raw, const gamepad_touch_t &touch) {
|
||||
auto gamepad = raw->gamepads[touch.id.globalIndex];
|
||||
if (!gamepad) {
|
||||
return;
|
||||
|
|
@ -216,15 +198,13 @@ namespace platf::gamepad {
|
|||
if (std::holds_alternative<inputtino::PS5Joypad>(*gamepad->joypad)) {
|
||||
if (touch.pressure > 0.5) {
|
||||
std::get<inputtino::PS5Joypad>(*gamepad->joypad).place_finger(touch.pointerId, touch.x * inputtino::PS5Joypad::touchpad_width, touch.y * inputtino::PS5Joypad::touchpad_height);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
std::get<inputtino::PS5Joypad>(*gamepad->joypad).release_finger(touch.pointerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
motion(input_raw_t *raw, const gamepad_motion_t &motion) {
|
||||
void motion(input_raw_t *raw, const gamepad_motion_t &motion) {
|
||||
auto gamepad = raw->gamepads[motion.id.globalIndex];
|
||||
if (!gamepad) {
|
||||
return;
|
||||
|
|
@ -242,8 +222,7 @@ namespace platf::gamepad {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
battery(input_raw_t *raw, const gamepad_battery_t &battery) {
|
||||
void battery(input_raw_t *raw, const gamepad_battery_t &battery) {
|
||||
auto gamepad = raw->gamepads[battery.id.globalIndex];
|
||||
if (!gamepad) {
|
||||
return;
|
||||
|
|
@ -272,14 +251,13 @@ namespace platf::gamepad {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input) {
|
||||
std::vector<supported_gamepad_t> &supported_gamepads(input_t *input) {
|
||||
if (!input) {
|
||||
static std::vector gps {
|
||||
supported_gamepad_t { "auto", true, "" },
|
||||
supported_gamepad_t { "xone", false, "" },
|
||||
supported_gamepad_t { "ds5", false, "" },
|
||||
supported_gamepad_t { "switch", false, "" },
|
||||
supported_gamepad_t {"auto", true, ""},
|
||||
supported_gamepad_t {"xone", false, ""},
|
||||
supported_gamepad_t {"ds5", false, ""},
|
||||
supported_gamepad_t {"switch", false, ""},
|
||||
};
|
||||
|
||||
return gps;
|
||||
|
|
@ -290,10 +268,10 @@ namespace platf::gamepad {
|
|||
auto xOne = create_xbox_one();
|
||||
|
||||
static std::vector gps {
|
||||
supported_gamepad_t { "auto", true, "" },
|
||||
supported_gamepad_t { "xone", static_cast<bool>(xOne), !xOne ? xOne.getErrorMessage() : "" },
|
||||
supported_gamepad_t { "ds5", static_cast<bool>(ds5), !ds5 ? ds5.getErrorMessage() : "" },
|
||||
supported_gamepad_t { "switch", static_cast<bool>(switchPro), !switchPro ? switchPro.getErrorMessage() : "" },
|
||||
supported_gamepad_t {"auto", true, ""},
|
||||
supported_gamepad_t {"xone", static_cast<bool>(xOne), !xOne ? xOne.getErrorMessage() : ""},
|
||||
supported_gamepad_t {"ds5", static_cast<bool>(ds5), !ds5 ? ds5.getErrorMessage() : ""},
|
||||
supported_gamepad_t {"switch", static_cast<bool>(switchPro), !switchPro ? switchPro.getErrorMessage() : ""},
|
||||
};
|
||||
|
||||
for (auto &[name, is_enabled, reason_disabled] : gps) {
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "src/platform/common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
|
|
@ -22,24 +23,17 @@ namespace platf::gamepad {
|
|||
SwitchProWired ///< Switch Pro Wired Controller
|
||||
};
|
||||
|
||||
int
|
||||
alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue);
|
||||
int alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue);
|
||||
|
||||
void
|
||||
free(input_raw_t *raw, int nr);
|
||||
void free(input_raw_t *raw, int nr);
|
||||
|
||||
void
|
||||
update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state);
|
||||
void update(input_raw_t *raw, int nr, const gamepad_state_t &gamepad_state);
|
||||
|
||||
void
|
||||
touch(input_raw_t *raw, const gamepad_touch_t &touch);
|
||||
void touch(input_raw_t *raw, const gamepad_touch_t &touch);
|
||||
|
||||
void
|
||||
motion(input_raw_t *raw, const gamepad_motion_t &motion);
|
||||
void motion(input_raw_t *raw, const gamepad_motion_t &motion);
|
||||
|
||||
void
|
||||
battery(input_raw_t *raw, const gamepad_battery_t &battery);
|
||||
void battery(input_raw_t *raw, const gamepad_battery_t &battery);
|
||||
|
||||
std::vector<supported_gamepad_t> &
|
||||
supported_gamepads(input_t *input);
|
||||
std::vector<supported_gamepad_t> &supported_gamepads(input_t *input);
|
||||
} // namespace platf::gamepad
|
||||
|
|
|
|||
|
|
@ -2,18 +2,19 @@
|
|||
* @file src/platform/linux/input/inputtino_keyboard.cpp
|
||||
* @brief Definitions for inputtino keyboard input handling.
|
||||
*/
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_keyboard.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_keyboard.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::keyboard {
|
||||
|
|
@ -25,8 +26,7 @@ namespace platf::keyboard {
|
|||
*
|
||||
* adapted from: https://stackoverflow.com/a/7639754
|
||||
*/
|
||||
std::string
|
||||
to_hex(const std::basic_string<char32_t> &str) {
|
||||
std::string to_hex(const std::basic_string<char32_t> &str) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::setfill('0');
|
||||
for (const auto &ch : str) {
|
||||
|
|
@ -42,48 +42,123 @@ namespace platf::keyboard {
|
|||
* A map of linux scan code -> Moonlight keyboard code
|
||||
*/
|
||||
static const std::map<short, short> key_mappings = {
|
||||
{ KEY_BACKSPACE, 0x08 }, { KEY_TAB, 0x09 }, { KEY_ENTER, 0x0D }, { KEY_LEFTSHIFT, 0x10 },
|
||||
{ KEY_LEFTCTRL, 0x11 }, { KEY_CAPSLOCK, 0x14 }, { KEY_ESC, 0x1B }, { KEY_SPACE, 0x20 },
|
||||
{ KEY_PAGEUP, 0x21 }, { KEY_PAGEDOWN, 0x22 }, { KEY_END, 0x23 }, { KEY_HOME, 0x24 },
|
||||
{ KEY_LEFT, 0x25 }, { KEY_UP, 0x26 }, { KEY_RIGHT, 0x27 }, { KEY_DOWN, 0x28 },
|
||||
{ KEY_SYSRQ, 0x2C }, { KEY_INSERT, 0x2D }, { KEY_DELETE, 0x2E }, { KEY_0, 0x30 },
|
||||
{ KEY_1, 0x31 }, { KEY_2, 0x32 }, { KEY_3, 0x33 }, { KEY_4, 0x34 },
|
||||
{ KEY_5, 0x35 }, { KEY_6, 0x36 }, { KEY_7, 0x37 }, { KEY_8, 0x38 },
|
||||
{ KEY_9, 0x39 }, { KEY_A, 0x41 }, { KEY_B, 0x42 }, { KEY_C, 0x43 },
|
||||
{ KEY_D, 0x44 }, { KEY_E, 0x45 }, { KEY_F, 0x46 }, { KEY_G, 0x47 },
|
||||
{ KEY_H, 0x48 }, { KEY_I, 0x49 }, { KEY_J, 0x4A }, { KEY_K, 0x4B },
|
||||
{ KEY_L, 0x4C }, { KEY_M, 0x4D }, { KEY_N, 0x4E }, { KEY_O, 0x4F },
|
||||
{ KEY_P, 0x50 }, { KEY_Q, 0x51 }, { KEY_R, 0x52 }, { KEY_S, 0x53 },
|
||||
{ KEY_T, 0x54 }, { KEY_U, 0x55 }, { KEY_V, 0x56 }, { KEY_W, 0x57 },
|
||||
{ KEY_X, 0x58 }, { KEY_Y, 0x59 }, { KEY_Z, 0x5A }, { KEY_LEFTMETA, 0x5B },
|
||||
{ KEY_RIGHTMETA, 0x5C }, { KEY_KP0, 0x60 }, { KEY_KP1, 0x61 }, { KEY_KP2, 0x62 },
|
||||
{ KEY_KP3, 0x63 }, { KEY_KP4, 0x64 }, { KEY_KP5, 0x65 }, { KEY_KP6, 0x66 },
|
||||
{ KEY_KP7, 0x67 }, { KEY_KP8, 0x68 }, { KEY_KP9, 0x69 }, { KEY_KPASTERISK, 0x6A },
|
||||
{ KEY_KPPLUS, 0x6B }, { KEY_KPMINUS, 0x6D }, { KEY_KPDOT, 0x6E }, { KEY_KPSLASH, 0x6F },
|
||||
{ KEY_F1, 0x70 }, { KEY_F2, 0x71 }, { KEY_F3, 0x72 }, { KEY_F4, 0x73 },
|
||||
{ KEY_F5, 0x74 }, { KEY_F6, 0x75 }, { KEY_F7, 0x76 }, { KEY_F8, 0x77 },
|
||||
{ KEY_F9, 0x78 }, { KEY_F10, 0x79 }, { KEY_F11, 0x7A }, { KEY_F12, 0x7B },
|
||||
{ KEY_NUMLOCK, 0x90 }, { KEY_SCROLLLOCK, 0x91 }, { KEY_LEFTSHIFT, 0xA0 }, { KEY_RIGHTSHIFT, 0xA1 },
|
||||
{ KEY_LEFTCTRL, 0xA2 }, { KEY_RIGHTCTRL, 0xA3 }, { KEY_LEFTALT, 0xA4 }, { KEY_RIGHTALT, 0xA5 },
|
||||
{ KEY_SEMICOLON, 0xBA }, { KEY_EQUAL, 0xBB }, { KEY_COMMA, 0xBC }, { KEY_MINUS, 0xBD },
|
||||
{ KEY_DOT, 0xBE }, { KEY_SLASH, 0xBF }, { KEY_GRAVE, 0xC0 }, { KEY_LEFTBRACE, 0xDB },
|
||||
{ KEY_BACKSLASH, 0xDC }, { KEY_RIGHTBRACE, 0xDD }, { KEY_APOSTROPHE, 0xDE }, { KEY_102ND, 0xE2 }
|
||||
{KEY_BACKSPACE, 0x08},
|
||||
{KEY_TAB, 0x09},
|
||||
{KEY_ENTER, 0x0D},
|
||||
{KEY_LEFTSHIFT, 0x10},
|
||||
{KEY_LEFTCTRL, 0x11},
|
||||
{KEY_CAPSLOCK, 0x14},
|
||||
{KEY_ESC, 0x1B},
|
||||
{KEY_SPACE, 0x20},
|
||||
{KEY_PAGEUP, 0x21},
|
||||
{KEY_PAGEDOWN, 0x22},
|
||||
{KEY_END, 0x23},
|
||||
{KEY_HOME, 0x24},
|
||||
{KEY_LEFT, 0x25},
|
||||
{KEY_UP, 0x26},
|
||||
{KEY_RIGHT, 0x27},
|
||||
{KEY_DOWN, 0x28},
|
||||
{KEY_SYSRQ, 0x2C},
|
||||
{KEY_INSERT, 0x2D},
|
||||
{KEY_DELETE, 0x2E},
|
||||
{KEY_0, 0x30},
|
||||
{KEY_1, 0x31},
|
||||
{KEY_2, 0x32},
|
||||
{KEY_3, 0x33},
|
||||
{KEY_4, 0x34},
|
||||
{KEY_5, 0x35},
|
||||
{KEY_6, 0x36},
|
||||
{KEY_7, 0x37},
|
||||
{KEY_8, 0x38},
|
||||
{KEY_9, 0x39},
|
||||
{KEY_A, 0x41},
|
||||
{KEY_B, 0x42},
|
||||
{KEY_C, 0x43},
|
||||
{KEY_D, 0x44},
|
||||
{KEY_E, 0x45},
|
||||
{KEY_F, 0x46},
|
||||
{KEY_G, 0x47},
|
||||
{KEY_H, 0x48},
|
||||
{KEY_I, 0x49},
|
||||
{KEY_J, 0x4A},
|
||||
{KEY_K, 0x4B},
|
||||
{KEY_L, 0x4C},
|
||||
{KEY_M, 0x4D},
|
||||
{KEY_N, 0x4E},
|
||||
{KEY_O, 0x4F},
|
||||
{KEY_P, 0x50},
|
||||
{KEY_Q, 0x51},
|
||||
{KEY_R, 0x52},
|
||||
{KEY_S, 0x53},
|
||||
{KEY_T, 0x54},
|
||||
{KEY_U, 0x55},
|
||||
{KEY_V, 0x56},
|
||||
{KEY_W, 0x57},
|
||||
{KEY_X, 0x58},
|
||||
{KEY_Y, 0x59},
|
||||
{KEY_Z, 0x5A},
|
||||
{KEY_LEFTMETA, 0x5B},
|
||||
{KEY_RIGHTMETA, 0x5C},
|
||||
{KEY_KP0, 0x60},
|
||||
{KEY_KP1, 0x61},
|
||||
{KEY_KP2, 0x62},
|
||||
{KEY_KP3, 0x63},
|
||||
{KEY_KP4, 0x64},
|
||||
{KEY_KP5, 0x65},
|
||||
{KEY_KP6, 0x66},
|
||||
{KEY_KP7, 0x67},
|
||||
{KEY_KP8, 0x68},
|
||||
{KEY_KP9, 0x69},
|
||||
{KEY_KPASTERISK, 0x6A},
|
||||
{KEY_KPPLUS, 0x6B},
|
||||
{KEY_KPMINUS, 0x6D},
|
||||
{KEY_KPDOT, 0x6E},
|
||||
{KEY_KPSLASH, 0x6F},
|
||||
{KEY_F1, 0x70},
|
||||
{KEY_F2, 0x71},
|
||||
{KEY_F3, 0x72},
|
||||
{KEY_F4, 0x73},
|
||||
{KEY_F5, 0x74},
|
||||
{KEY_F6, 0x75},
|
||||
{KEY_F7, 0x76},
|
||||
{KEY_F8, 0x77},
|
||||
{KEY_F9, 0x78},
|
||||
{KEY_F10, 0x79},
|
||||
{KEY_F11, 0x7A},
|
||||
{KEY_F12, 0x7B},
|
||||
{KEY_NUMLOCK, 0x90},
|
||||
{KEY_SCROLLLOCK, 0x91},
|
||||
{KEY_LEFTSHIFT, 0xA0},
|
||||
{KEY_RIGHTSHIFT, 0xA1},
|
||||
{KEY_LEFTCTRL, 0xA2},
|
||||
{KEY_RIGHTCTRL, 0xA3},
|
||||
{KEY_LEFTALT, 0xA4},
|
||||
{KEY_RIGHTALT, 0xA5},
|
||||
{KEY_SEMICOLON, 0xBA},
|
||||
{KEY_EQUAL, 0xBB},
|
||||
{KEY_COMMA, 0xBC},
|
||||
{KEY_MINUS, 0xBD},
|
||||
{KEY_DOT, 0xBE},
|
||||
{KEY_SLASH, 0xBF},
|
||||
{KEY_GRAVE, 0xC0},
|
||||
{KEY_LEFTBRACE, 0xDB},
|
||||
{KEY_BACKSLASH, 0xDC},
|
||||
{KEY_RIGHTBRACE, 0xDD},
|
||||
{KEY_APOSTROPHE, 0xDE},
|
||||
{KEY_102ND, 0xE2}
|
||||
};
|
||||
|
||||
void
|
||||
update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags) {
|
||||
void update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags) {
|
||||
if (raw->keyboard) {
|
||||
if (release) {
|
||||
(*raw->keyboard).release(modcode);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
(*raw->keyboard).press(modcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
unicode(input_raw_t *raw, char *utf8, int size) {
|
||||
void unicode(input_raw_t *raw, char *utf8, int size) {
|
||||
if (raw->keyboard) {
|
||||
/* Reading input text as UTF-8 */
|
||||
auto utf8_str = boost::locale::conv::to_utf<wchar_t>(utf8, utf8 + size, "UTF-8");
|
||||
|
|
@ -106,8 +181,7 @@ namespace platf::keyboard {
|
|||
auto wincode = key_mappings.find(keycode);
|
||||
if (keycode == -1 || wincode == key_mappings.end()) {
|
||||
BOOST_LOG(warning) << "Unicode, unable to find keycode for: "sv << ch;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
(*raw->keyboard).press(wincode->second);
|
||||
(*raw->keyboard).release(wincode->second);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::keyboard {
|
||||
void
|
||||
update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags);
|
||||
void update(input_raw_t *raw, uint16_t modcode, bool release, uint8_t flags);
|
||||
|
||||
void
|
||||
unicode(input_raw_t *raw, char *utf8, int size);
|
||||
void unicode(input_raw_t *raw, char *utf8, int size);
|
||||
} // namespace platf::keyboard
|
||||
|
|
|
|||
|
|
@ -2,38 +2,36 @@
|
|||
* @file src/platform/linux/input/inputtino_mouse.cpp
|
||||
* @brief Definitions for inputtino mouse input handling.
|
||||
*/
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_mouse.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_mouse.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::mouse {
|
||||
|
||||
void
|
||||
move(input_raw_t *raw, int deltaX, int deltaY) {
|
||||
void move(input_raw_t *raw, int deltaX, int deltaY) {
|
||||
if (raw->mouse) {
|
||||
(*raw->mouse).move(deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y) {
|
||||
void move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y) {
|
||||
if (raw->mouse) {
|
||||
(*raw->mouse).move_abs(x, y, touch_port.width, touch_port.height);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
button(input_raw_t *raw, int button, bool release) {
|
||||
void button(input_raw_t *raw, int button, bool release) {
|
||||
if (raw->mouse) {
|
||||
inputtino::Mouse::MOUSE_BUTTON btn_type;
|
||||
switch (button) {
|
||||
|
|
@ -58,35 +56,31 @@ namespace platf::mouse {
|
|||
}
|
||||
if (release) {
|
||||
(*raw->mouse).release(btn_type);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
(*raw->mouse).press(btn_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scroll(input_raw_t *raw, int high_res_distance) {
|
||||
void scroll(input_raw_t *raw, int high_res_distance) {
|
||||
if (raw->mouse) {
|
||||
(*raw->mouse).vertical_scroll(high_res_distance);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hscroll(input_raw_t *raw, int high_res_distance) {
|
||||
void hscroll(input_raw_t *raw, int high_res_distance) {
|
||||
if (raw->mouse) {
|
||||
(*raw->mouse).horizontal_scroll(high_res_distance);
|
||||
}
|
||||
}
|
||||
|
||||
util::point_t
|
||||
get_location(input_raw_t *raw) {
|
||||
util::point_t get_location(input_raw_t *raw) {
|
||||
if (raw->mouse) {
|
||||
// TODO: decide what to do after https://github.com/games-on-whales/inputtino/issues/6 is resolved.
|
||||
// TODO: auto x = (*raw->mouse).get_absolute_x();
|
||||
// TODO: auto y = (*raw->mouse).get_absolute_y();
|
||||
return { 0, 0 };
|
||||
return {0, 0};
|
||||
}
|
||||
return { 0, 0 };
|
||||
return {0, 0};
|
||||
}
|
||||
} // namespace platf::mouse
|
||||
|
|
|
|||
|
|
@ -3,33 +3,27 @@
|
|||
* @brief Declarations for inputtino mouse input handling.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "src/platform/common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::mouse {
|
||||
void
|
||||
move(input_raw_t *raw, int deltaX, int deltaY);
|
||||
void move(input_raw_t *raw, int deltaX, int deltaY);
|
||||
|
||||
void
|
||||
move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y);
|
||||
void move_abs(input_raw_t *raw, const touch_port_t &touch_port, float x, float y);
|
||||
|
||||
void
|
||||
button(input_raw_t *raw, int button, bool release);
|
||||
void button(input_raw_t *raw, int button, bool release);
|
||||
|
||||
void
|
||||
scroll(input_raw_t *raw, int high_res_distance);
|
||||
void scroll(input_raw_t *raw, int high_res_distance);
|
||||
|
||||
void
|
||||
hscroll(input_raw_t *raw, int high_res_distance);
|
||||
void hscroll(input_raw_t *raw, int high_res_distance);
|
||||
|
||||
util::point_t
|
||||
get_location(input_raw_t *raw);
|
||||
util::point_t get_location(input_raw_t *raw);
|
||||
} // namespace platf::mouse
|
||||
|
|
|
|||
|
|
@ -2,23 +2,23 @@
|
|||
* @file src/platform/linux/input/inputtino_pen.cpp
|
||||
* @brief Definitions for inputtino pen input handling.
|
||||
*/
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_pen.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_pen.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::pen {
|
||||
void
|
||||
update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
void update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen) {
|
||||
if (raw->pen) {
|
||||
// First set the buttons
|
||||
(*raw->pen).set_btn(inputtino::PenTablet::PRIMARY, pen.penButtons & LI_PEN_BUTTON_PRIMARY);
|
||||
|
|
@ -63,13 +63,7 @@ namespace platf::pen {
|
|||
|
||||
bool is_touching = pen.eventType == LI_TOUCH_EVENT_DOWN || pen.eventType == LI_TOUCH_EVENT_MOVE;
|
||||
|
||||
(*raw->pen).place_tool(tool,
|
||||
pen.x,
|
||||
pen.y,
|
||||
is_touching ? pen.pressureOrDistance : -1,
|
||||
is_touching ? -1 : pen.pressureOrDistance,
|
||||
tilt_x,
|
||||
tilt_y);
|
||||
(*raw->pen).place_tool(tool, pen.x, pen.y, is_touching ? pen.pressureOrDistance : -1, is_touching ? -1 : pen.pressureOrDistance, tilt_x, tilt_y);
|
||||
}
|
||||
}
|
||||
} // namespace platf::pen
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "src/platform/common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::pen {
|
||||
void
|
||||
update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen);
|
||||
void update(client_input_raw_t *raw, const touch_port_t &touch_port, const pen_input_t &pen);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,52 +2,53 @@
|
|||
* @file src/platform/linux/input/inputtino_touch.cpp
|
||||
* @brief Definitions for inputtino touch input handling.
|
||||
*/
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_touch.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
#include "inputtino_common.h"
|
||||
#include "inputtino_touch.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::touch {
|
||||
void
|
||||
update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
void update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch) {
|
||||
if (raw->touch) {
|
||||
switch (touch.eventType) {
|
||||
case LI_TOUCH_EVENT_HOVER:
|
||||
case LI_TOUCH_EVENT_DOWN:
|
||||
case LI_TOUCH_EVENT_MOVE: {
|
||||
// Convert our 0..360 range to -90..90 relative to Y axis
|
||||
int adjusted_angle = touch.rotation;
|
||||
case LI_TOUCH_EVENT_MOVE:
|
||||
{
|
||||
// Convert our 0..360 range to -90..90 relative to Y axis
|
||||
int adjusted_angle = touch.rotation;
|
||||
|
||||
if (adjusted_angle > 90 && adjusted_angle < 270) {
|
||||
// Lower hemisphere
|
||||
adjusted_angle = 180 - adjusted_angle;
|
||||
}
|
||||
if (adjusted_angle > 90 && adjusted_angle < 270) {
|
||||
// Lower hemisphere
|
||||
adjusted_angle = 180 - adjusted_angle;
|
||||
}
|
||||
|
||||
// Wrap the value if it's out of range
|
||||
if (adjusted_angle > 90) {
|
||||
adjusted_angle -= 360;
|
||||
// Wrap the value if it's out of range
|
||||
if (adjusted_angle > 90) {
|
||||
adjusted_angle -= 360;
|
||||
} else if (adjusted_angle < -90) {
|
||||
adjusted_angle += 360;
|
||||
}
|
||||
(*raw->touch).place_finger(touch.pointerId, touch.x, touch.y, touch.pressureOrDistance, adjusted_angle);
|
||||
break;
|
||||
}
|
||||
else if (adjusted_angle < -90) {
|
||||
adjusted_angle += 360;
|
||||
}
|
||||
(*raw->touch).place_finger(touch.pointerId, touch.x, touch.y, touch.pressureOrDistance, adjusted_angle);
|
||||
break;
|
||||
}
|
||||
case LI_TOUCH_EVENT_CANCEL:
|
||||
case LI_TOUCH_EVENT_UP:
|
||||
case LI_TOUCH_EVENT_HOVER_LEAVE: {
|
||||
(*raw->touch).release_finger(touch.pointerId);
|
||||
break;
|
||||
}
|
||||
case LI_TOUCH_EVENT_HOVER_LEAVE:
|
||||
{
|
||||
(*raw->touch).release_finger(touch.pointerId);
|
||||
break;
|
||||
}
|
||||
// TODO: LI_TOUCH_EVENT_CANCEL_ALL
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// lib includes
|
||||
#include <boost/locale.hpp>
|
||||
#include <inputtino/input.hpp>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
// local includes
|
||||
#include "inputtino_common.h"
|
||||
#include "src/platform/common.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf::touch {
|
||||
void
|
||||
update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch);
|
||||
void update(client_input_raw_t *raw, const touch_port_t &touch_port, const touch_input_t &touch);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,28 +2,30 @@
|
|||
* @file src/platform/linux/kmsgrab.cpp
|
||||
* @brief Definitions for KMS screen capture.
|
||||
*/
|
||||
#include <drm_fourcc.h>
|
||||
// standard includes
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
// platform includes
|
||||
#include <drm_fourcc.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
// local includes
|
||||
#include "cuda.h"
|
||||
#include "graphics.h"
|
||||
#include "src/config.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/round_robin.h"
|
||||
#include "src/utility.h"
|
||||
#include "src/video.h"
|
||||
|
||||
#include "cuda.h"
|
||||
#include "graphics.h"
|
||||
#include "vaapi.h"
|
||||
#include "wayland.h"
|
||||
|
||||
|
|
@ -59,7 +61,10 @@ namespace platf {
|
|||
class wrapper_fb {
|
||||
public:
|
||||
wrapper_fb(drmModeFB *fb):
|
||||
fb { fb }, fb_id { fb->fb_id }, width { fb->width }, height { fb->height } {
|
||||
fb {fb},
|
||||
fb_id {fb->fb_id},
|
||||
width {fb->width},
|
||||
height {fb->height} {
|
||||
pixel_format = DRM_FORMAT_XRGB8888;
|
||||
modifier = DRM_FORMAT_MOD_INVALID;
|
||||
std::fill_n(handles, 4, 0);
|
||||
|
|
@ -70,7 +75,10 @@ namespace platf {
|
|||
}
|
||||
|
||||
wrapper_fb(drmModeFB2 *fb2):
|
||||
fb2 { fb2 }, fb_id { fb2->fb_id }, width { fb2->width }, height { fb2->height } {
|
||||
fb2 {fb2},
|
||||
fb_id {fb2->fb_id},
|
||||
width {fb2->width},
|
||||
height {fb2->height} {
|
||||
pixel_format = fb2->pixel_format;
|
||||
modifier = (fb2->flags & DRM_MODE_FB_MODIFIERS) ? fb2->modifier : DRM_FORMAT_MOD_INVALID;
|
||||
|
||||
|
|
@ -82,8 +90,7 @@ namespace platf {
|
|||
~wrapper_fb() {
|
||||
if (fb) {
|
||||
drmModeFreeFB(fb);
|
||||
}
|
||||
else if (fb2) {
|
||||
} else if (fb2) {
|
||||
drmModeFreeFB2(fb2);
|
||||
}
|
||||
}
|
||||
|
|
@ -116,8 +123,7 @@ namespace platf {
|
|||
static int env_width;
|
||||
static int env_height;
|
||||
|
||||
std::string_view
|
||||
plane_type(std::uint64_t val) {
|
||||
std::string_view plane_type(std::uint64_t val) {
|
||||
switch (val) {
|
||||
case DRM_PLANE_TYPE_OVERLAY:
|
||||
return "DRM_PLANE_TYPE_OVERLAY"sv;
|
||||
|
|
@ -165,10 +171,10 @@ namespace platf {
|
|||
|
||||
static std::vector<card_descriptor_t> card_descriptors;
|
||||
|
||||
static std::uint32_t
|
||||
from_view(const std::string_view &string) {
|
||||
static std::uint32_t from_view(const std::string_view &string) {
|
||||
#define _CONVERT(x, y) \
|
||||
if (string == x) return DRM_MODE_CONNECTOR_##y
|
||||
if (string == x) \
|
||||
return DRM_MODE_CONNECTOR_##y
|
||||
|
||||
// This list was created from the following sources:
|
||||
// https://gitlab.freedesktop.org/mesa/drm/-/blob/main/xf86drmMode.c (drmModeGetConnectorTypeName)
|
||||
|
|
@ -212,7 +218,7 @@ namespace platf {
|
|||
// value appended to the string. Let's try to read it.
|
||||
if (string.find("Unknown"sv) == 0) {
|
||||
std::uint32_t type;
|
||||
std::string null_terminated_string { string };
|
||||
std::string null_terminated_string {string};
|
||||
if (std::sscanf(null_terminated_string.c_str(), "Unknown%u", &type) == 1) {
|
||||
return type;
|
||||
}
|
||||
|
|
@ -225,15 +231,19 @@ namespace platf {
|
|||
class plane_it_t: public round_robin_util::it_wrap_t<plane_t::element_type, plane_it_t> {
|
||||
public:
|
||||
plane_it_t(int fd, std::uint32_t *plane_p, std::uint32_t *end):
|
||||
fd { fd }, plane_p { plane_p }, end { end } {
|
||||
fd {fd},
|
||||
plane_p {plane_p},
|
||||
end {end} {
|
||||
load_next_valid_plane();
|
||||
}
|
||||
|
||||
plane_it_t(int fd, std::uint32_t *end):
|
||||
fd { fd }, plane_p { end }, end { end } {}
|
||||
fd {fd},
|
||||
plane_p {end},
|
||||
end {end} {
|
||||
}
|
||||
|
||||
void
|
||||
load_next_valid_plane() {
|
||||
void load_next_valid_plane() {
|
||||
this->plane.reset();
|
||||
|
||||
for (; plane_p != end; ++plane_p) {
|
||||
|
|
@ -248,19 +258,16 @@ namespace platf {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
inc() {
|
||||
void inc() {
|
||||
++plane_p;
|
||||
load_next_valid_plane();
|
||||
}
|
||||
|
||||
bool
|
||||
eq(const plane_it_t &other) const {
|
||||
bool eq(const plane_it_t &other) const {
|
||||
return plane_p == other.plane_p;
|
||||
}
|
||||
|
||||
plane_t::pointer
|
||||
get() {
|
||||
plane_t::pointer get() {
|
||||
return plane.get();
|
||||
}
|
||||
|
||||
|
|
@ -289,8 +296,7 @@ namespace platf {
|
|||
public:
|
||||
using connector_interal_t = util::safe_ptr<drmModeConnector, drmModeFreeConnector>;
|
||||
|
||||
int
|
||||
init(const char *path) {
|
||||
int init(const char *path) {
|
||||
cap_sys_admin admin;
|
||||
fd.el = open(path, O_RDWR);
|
||||
|
||||
|
|
@ -299,7 +305,7 @@ namespace platf {
|
|||
return -1;
|
||||
}
|
||||
|
||||
version_t ver { drmGetVersion(fd.el) };
|
||||
version_t ver {drmGetVersion(fd.el)};
|
||||
BOOST_LOG(info) << path << " -> "sv << ((ver && ver->name) ? ver->name : "UNKNOWN");
|
||||
|
||||
// Open the render node for this card to share with libva.
|
||||
|
|
@ -313,8 +319,7 @@ namespace platf {
|
|||
render_fd.el = dup(fd.el);
|
||||
}
|
||||
free(rendernode_path);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(warning) << "No render device name for: "sv << path;
|
||||
render_fd.el = dup(fd.el);
|
||||
}
|
||||
|
|
@ -346,8 +351,7 @@ namespace platf {
|
|||
return 0;
|
||||
}
|
||||
|
||||
fb_t
|
||||
fb(plane_t::pointer plane) {
|
||||
fb_t fb(plane_t::pointer plane) {
|
||||
cap_sys_admin admin;
|
||||
|
||||
auto fb2 = drmModeGetFB2(fd.el, plane->fb_id);
|
||||
|
|
@ -363,36 +367,30 @@ namespace platf {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
crtc_t
|
||||
crtc(std::uint32_t id) {
|
||||
crtc_t crtc(std::uint32_t id) {
|
||||
return drmModeGetCrtc(fd.el, id);
|
||||
}
|
||||
|
||||
encoder_t
|
||||
encoder(std::uint32_t id) {
|
||||
encoder_t encoder(std::uint32_t id) {
|
||||
return drmModeGetEncoder(fd.el, id);
|
||||
}
|
||||
|
||||
res_t
|
||||
res() {
|
||||
res_t res() {
|
||||
return drmModeGetResources(fd.el);
|
||||
}
|
||||
|
||||
bool
|
||||
is_nvidia() {
|
||||
version_t ver { drmGetVersion(fd.el) };
|
||||
bool is_nvidia() {
|
||||
version_t ver {drmGetVersion(fd.el)};
|
||||
return ver && ver->name && strncmp(ver->name, "nvidia-drm", 10) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
is_cursor(std::uint32_t plane_id) {
|
||||
bool is_cursor(std::uint32_t plane_id) {
|
||||
auto props = plane_props(plane_id);
|
||||
for (auto &[prop, val] : props) {
|
||||
if (prop->name == "type"sv) {
|
||||
if (val == DRM_PLANE_TYPE_CURSOR) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -401,8 +399,7 @@ namespace platf {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::uint64_t>
|
||||
prop_value_by_name(const std::vector<std::pair<prop_t, std::uint64_t>> &props, std::string_view name) {
|
||||
std::optional<std::uint64_t> prop_value_by_name(const std::vector<std::pair<prop_t, std::uint64_t>> &props, std::string_view name) {
|
||||
for (auto &[prop, val] : props) {
|
||||
if (prop->name == name) {
|
||||
return val;
|
||||
|
|
@ -411,8 +408,7 @@ namespace platf {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
get_panel_orientation(std::uint32_t plane_id) {
|
||||
std::uint32_t get_panel_orientation(std::uint32_t plane_id) {
|
||||
auto props = plane_props(plane_id);
|
||||
auto value = prop_value_by_name(props, "rotation"sv);
|
||||
if (value) {
|
||||
|
|
@ -423,8 +419,7 @@ namespace platf {
|
|||
return DRM_MODE_ROTATE_0;
|
||||
}
|
||||
|
||||
int
|
||||
get_crtc_index_by_id(std::uint32_t crtc_id) {
|
||||
int get_crtc_index_by_id(std::uint32_t crtc_id) {
|
||||
auto resources = res();
|
||||
for (int i = 0; i < resources->count_crtcs; i++) {
|
||||
if (resources->crtcs[i] == crtc_id) {
|
||||
|
|
@ -434,13 +429,11 @@ namespace platf {
|
|||
return -1;
|
||||
}
|
||||
|
||||
connector_interal_t
|
||||
connector(std::uint32_t id) {
|
||||
connector_interal_t connector(std::uint32_t id) {
|
||||
return drmModeGetConnector(fd.el, id);
|
||||
}
|
||||
|
||||
std::vector<connector_t>
|
||||
monitors(conn_type_count_t &conn_type_count) {
|
||||
std::vector<connector_t> monitors(conn_type_count_t &conn_type_count) {
|
||||
auto resources = res();
|
||||
if (!resources) {
|
||||
BOOST_LOG(error) << "Couldn't get connector resources"sv;
|
||||
|
|
@ -474,8 +467,7 @@ namespace platf {
|
|||
return monitors;
|
||||
}
|
||||
|
||||
file_t
|
||||
handleFD(std::uint32_t handle) {
|
||||
file_t handleFD(std::uint32_t handle) {
|
||||
file_t fb_fd;
|
||||
|
||||
auto status = drmPrimeHandleToFD(fd.el, handle, 0 /* flags */, &fb_fd.el);
|
||||
|
|
@ -486,8 +478,7 @@ namespace platf {
|
|||
return fb_fd;
|
||||
}
|
||||
|
||||
std::vector<std::pair<prop_t, std::uint64_t>>
|
||||
props(std::uint32_t id, std::uint32_t type) {
|
||||
std::vector<std::pair<prop_t, std::uint64_t>> props(std::uint32_t id, std::uint32_t type) {
|
||||
obj_prop_t obj_prop = drmModeObjectGetProperties(fd.el, id, type);
|
||||
if (!obj_prop) {
|
||||
return {};
|
||||
|
|
@ -503,39 +494,32 @@ namespace platf {
|
|||
return props;
|
||||
}
|
||||
|
||||
std::vector<std::pair<prop_t, std::uint64_t>>
|
||||
plane_props(std::uint32_t id) {
|
||||
std::vector<std::pair<prop_t, std::uint64_t>> plane_props(std::uint32_t id) {
|
||||
return props(id, DRM_MODE_OBJECT_PLANE);
|
||||
}
|
||||
|
||||
std::vector<std::pair<prop_t, std::uint64_t>>
|
||||
crtc_props(std::uint32_t id) {
|
||||
std::vector<std::pair<prop_t, std::uint64_t>> crtc_props(std::uint32_t id) {
|
||||
return props(id, DRM_MODE_OBJECT_CRTC);
|
||||
}
|
||||
|
||||
std::vector<std::pair<prop_t, std::uint64_t>>
|
||||
connector_props(std::uint32_t id) {
|
||||
std::vector<std::pair<prop_t, std::uint64_t>> connector_props(std::uint32_t id) {
|
||||
return props(id, DRM_MODE_OBJECT_CONNECTOR);
|
||||
}
|
||||
|
||||
plane_t
|
||||
operator[](std::uint32_t index) {
|
||||
plane_t operator[](std::uint32_t index) {
|
||||
return drmModeGetPlane(fd.el, plane_res->planes[index]);
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
count() {
|
||||
std::uint32_t count() {
|
||||
return plane_res->count_planes;
|
||||
}
|
||||
|
||||
plane_it_t
|
||||
begin() const {
|
||||
return plane_it_t { fd.el, plane_res->planes, plane_res->planes + plane_res->count_planes };
|
||||
plane_it_t begin() const {
|
||||
return plane_it_t {fd.el, plane_res->planes, plane_res->planes + plane_res->count_planes};
|
||||
}
|
||||
|
||||
plane_it_t
|
||||
end() const {
|
||||
return plane_it_t { fd.el, plane_res->planes + plane_res->count_planes };
|
||||
plane_it_t end() const {
|
||||
return plane_it_t {fd.el, plane_res->planes + plane_res->count_planes};
|
||||
}
|
||||
|
||||
file_t fd;
|
||||
|
|
@ -543,16 +527,14 @@ namespace platf {
|
|||
plane_res_t plane_res;
|
||||
};
|
||||
|
||||
std::map<std::uint32_t, monitor_t>
|
||||
map_crtc_to_monitor(const std::vector<connector_t> &connectors) {
|
||||
std::map<std::uint32_t, monitor_t> map_crtc_to_monitor(const std::vector<connector_t> &connectors) {
|
||||
std::map<std::uint32_t, monitor_t> result;
|
||||
|
||||
for (auto &connector : connectors) {
|
||||
result.emplace(connector.crtc_id,
|
||||
monitor_t {
|
||||
connector.type,
|
||||
connector.index,
|
||||
});
|
||||
result.emplace(connector.crtc_id, monitor_t {
|
||||
connector.type,
|
||||
connector.index,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -565,8 +547,7 @@ namespace platf {
|
|||
}
|
||||
};
|
||||
|
||||
void
|
||||
print(plane_t::pointer plane, fb_t::pointer fb, crtc_t::pointer crtc) {
|
||||
void print(plane_t::pointer plane, fb_t::pointer fb, crtc_t::pointer crtc) {
|
||||
if (crtc) {
|
||||
BOOST_LOG(debug) << "crtc("sv << crtc->x << ", "sv << crtc->y << ')';
|
||||
BOOST_LOG(debug) << "crtc("sv << crtc->width << ", "sv << crtc->height << ')';
|
||||
|
|
@ -601,21 +582,22 @@ namespace platf {
|
|||
class display_t: public platf::display_t {
|
||||
public:
|
||||
display_t(mem_type_e mem_type):
|
||||
platf::display_t(), mem_type { mem_type } {}
|
||||
platf::display_t(),
|
||||
mem_type {mem_type} {
|
||||
}
|
||||
|
||||
int
|
||||
init(const std::string &display_name, const ::video::config_t &config) {
|
||||
delay = std::chrono::nanoseconds { 1s } / config.framerate;
|
||||
int init(const std::string &display_name, const ::video::config_t &config) {
|
||||
delay = std::chrono::nanoseconds {1s} / config.framerate;
|
||||
|
||||
int monitor_index = util::from_view(display_name);
|
||||
int monitor = 0;
|
||||
|
||||
fs::path card_dir { "/dev/dri"sv };
|
||||
for (auto &entry : fs::directory_iterator { card_dir }) {
|
||||
fs::path card_dir {"/dev/dri"sv};
|
||||
for (auto &entry : fs::directory_iterator {card_dir}) {
|
||||
auto file = entry.path().filename();
|
||||
|
||||
auto filestring = file.generic_string();
|
||||
if (filestring.size() < 4 || std::string_view { filestring }.substr(0, 4) != "card"sv) {
|
||||
if (filestring.size() < 4 || std::string_view {filestring}.substr(0, 4) != "card"sv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -779,8 +761,7 @@ namespace platf {
|
|||
if (!(plane->possible_crtcs & (1 << crtc_index))) {
|
||||
// Skip cursor planes for other CRTCs
|
||||
continue;
|
||||
}
|
||||
else if (plane->possible_crtcs != (1 << crtc_index)) {
|
||||
} else if (plane->possible_crtcs != (1 << crtc_index)) {
|
||||
// We assume a 1:1 mapping between cursor planes and CRTCs, which seems to
|
||||
// match the behavior of drivers in the real world. If it's violated, we'll
|
||||
// proceed anyway but print a warning in the log.
|
||||
|
|
@ -799,8 +780,7 @@ namespace platf {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
is_hdr() {
|
||||
bool is_hdr() {
|
||||
if (!hdr_metadata_blob_id || *hdr_metadata_blob_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -846,8 +826,7 @@ namespace platf {
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
||||
bool get_hdr_metadata(SS_HDR_METADATA &metadata) {
|
||||
// This performs all the metadata validation
|
||||
if (!is_hdr()) {
|
||||
return false;
|
||||
|
|
@ -876,8 +855,7 @@ namespace platf {
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
update_cursor() {
|
||||
void update_cursor() {
|
||||
if (cursor_plane_id < 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -898,26 +876,19 @@ namespace platf {
|
|||
for (auto &[prop, val] : props) {
|
||||
if (prop->name == "CRTC_X"sv) {
|
||||
prop_crtc_x = val;
|
||||
}
|
||||
else if (prop->name == "CRTC_Y"sv) {
|
||||
} else if (prop->name == "CRTC_Y"sv) {
|
||||
prop_crtc_y = val;
|
||||
}
|
||||
else if (prop->name == "CRTC_W"sv) {
|
||||
} else if (prop->name == "CRTC_W"sv) {
|
||||
prop_crtc_w = val;
|
||||
}
|
||||
else if (prop->name == "CRTC_H"sv) {
|
||||
} else if (prop->name == "CRTC_H"sv) {
|
||||
prop_crtc_h = val;
|
||||
}
|
||||
else if (prop->name == "SRC_X"sv) {
|
||||
} else if (prop->name == "SRC_X"sv) {
|
||||
prop_src_x = val;
|
||||
}
|
||||
else if (prop->name == "SRC_Y"sv) {
|
||||
} else if (prop->name == "SRC_Y"sv) {
|
||||
prop_src_y = val;
|
||||
}
|
||||
else if (prop->name == "SRC_W"sv) {
|
||||
} else if (prop->name == "SRC_W"sv) {
|
||||
prop_src_w = val;
|
||||
}
|
||||
else if (prop->name == "SRC_H"sv) {
|
||||
} else if (prop->name == "SRC_H"sv) {
|
||||
prop_src_h = val;
|
||||
}
|
||||
}
|
||||
|
|
@ -951,15 +922,13 @@ namespace platf {
|
|||
if (!plane->fb_id) {
|
||||
captured_cursor.visible = false;
|
||||
captured_cursor.fb_id = 0;
|
||||
}
|
||||
else if (plane->fb_id != captured_cursor.fb_id) {
|
||||
} else if (plane->fb_id != captured_cursor.fb_id) {
|
||||
BOOST_LOG(debug) << "Refreshing cursor image after FB changed"sv;
|
||||
cursor_dirty = true;
|
||||
}
|
||||
else if (*prop_src_x != captured_cursor.prop_src_x ||
|
||||
*prop_src_y != captured_cursor.prop_src_y ||
|
||||
*prop_src_w != captured_cursor.prop_src_w ||
|
||||
*prop_src_h != captured_cursor.prop_src_h) {
|
||||
} else if (*prop_src_x != captured_cursor.prop_src_x ||
|
||||
*prop_src_y != captured_cursor.prop_src_y ||
|
||||
*prop_src_w != captured_cursor.prop_src_w ||
|
||||
*prop_src_h != captured_cursor.prop_src_h) {
|
||||
BOOST_LOG(debug) << "Refreshing cursor image after source dimensions changed"sv;
|
||||
cursor_dirty = true;
|
||||
}
|
||||
|
|
@ -1041,8 +1010,7 @@ namespace platf {
|
|||
// If the image is tightly packed, copy it in one shot
|
||||
if (fb->pitches[0] == src_w * 4 && src_x == 0) {
|
||||
memcpy(captured_cursor.pixels.data(), &((std::uint8_t *) mapped_data)[src_y * fb->pitches[0]], src_h * fb->pitches[0]);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Copy row by row to deal with mismatched pitch or an X offset
|
||||
auto pixel_dst = captured_cursor.pixels.data();
|
||||
for (int y = 0; y < src_h; y++) {
|
||||
|
|
@ -1068,8 +1036,7 @@ namespace platf {
|
|||
}
|
||||
}
|
||||
|
||||
inline capture_e
|
||||
refresh(file_t *file, egl::surface_descriptor_t *sd, std::optional<std::chrono::steady_clock::time_point> &frame_timestamp) {
|
||||
inline capture_e refresh(file_t *file, egl::surface_descriptor_t *sd, std::optional<std::chrono::steady_clock::time_point> &frame_timestamp) {
|
||||
// Check for a change in HDR metadata
|
||||
if (connector_id) {
|
||||
auto connector_props = card.connector_props(*connector_id);
|
||||
|
|
@ -1123,7 +1090,8 @@ namespace platf {
|
|||
|
||||
if (
|
||||
fb->width != img_width ||
|
||||
fb->height != img_height) {
|
||||
fb->height != img_height
|
||||
) {
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
|
|
@ -1155,10 +1123,10 @@ namespace platf {
|
|||
class display_ram_t: public display_t {
|
||||
public:
|
||||
display_ram_t(mem_type_e mem_type):
|
||||
display_t(mem_type) {}
|
||||
display_t(mem_type) {
|
||||
}
|
||||
|
||||
int
|
||||
init(const std::string &display_name, const ::video::config_t &config) {
|
||||
int init(const std::string &display_name, const ::video::config_t &config) {
|
||||
if (!gbm::create_device) {
|
||||
BOOST_LOG(warning) << "libgbm not initialized"sv;
|
||||
return -1;
|
||||
|
|
@ -1189,8 +1157,7 @@ namespace platf {
|
|||
return 0;
|
||||
}
|
||||
|
||||
capture_e
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_logger.reset();
|
||||
|
|
@ -1235,8 +1202,7 @@ namespace platf {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
std::unique_ptr<avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
|
||||
std::unique_ptr<avcodec_encode_device_t> make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
|
||||
#ifdef SUNSHINE_BUILD_VAAPI
|
||||
if (mem_type == mem_type_e::vaapi) {
|
||||
return va::make_avcodec_encode_device(width, height, false);
|
||||
|
|
@ -1252,8 +1218,7 @@ namespace platf {
|
|||
return std::make_unique<avcodec_encode_device_t>();
|
||||
}
|
||||
|
||||
void
|
||||
blend_cursor(img_t &img) {
|
||||
void blend_cursor(img_t &img) {
|
||||
// TODO: Cursor scaling is not supported in this codepath.
|
||||
// We always draw the cursor at the source size.
|
||||
auto pixels = (int *) img.data;
|
||||
|
|
@ -1290,8 +1255,7 @@ namespace platf {
|
|||
auto alpha = (*(uint *) &cursor_pixel) >> 24u;
|
||||
if (alpha == 255) {
|
||||
*pixels_begin = cursor_pixel;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto colors_out = (uint8_t *) &cursor_pixel;
|
||||
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;
|
||||
|
|
@ -1302,8 +1266,7 @@ namespace platf {
|
|||
}
|
||||
}
|
||||
|
||||
capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
file_t fb_fd[4];
|
||||
|
||||
egl::surface_descriptor_t sd;
|
||||
|
|
@ -1345,8 +1308,7 @@ namespace platf {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override {
|
||||
std::shared_ptr<img_t> alloc_img() override {
|
||||
auto img = std::make_shared<kms_img_t>();
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
|
|
@ -1357,8 +1319,7 @@ namespace platf {
|
|||
return img;
|
||||
}
|
||||
|
||||
int
|
||||
dummy_img(platf::img_t *img) override {
|
||||
int dummy_img(platf::img_t *img) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1370,10 +1331,10 @@ namespace platf {
|
|||
class display_vram_t: public display_t {
|
||||
public:
|
||||
display_vram_t(mem_type_e mem_type):
|
||||
display_t(mem_type) {}
|
||||
display_t(mem_type) {
|
||||
}
|
||||
|
||||
std::unique_ptr<avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
|
||||
std::unique_ptr<avcodec_encode_device_t> make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
|
||||
#ifdef SUNSHINE_BUILD_VAAPI
|
||||
if (mem_type == mem_type_e::vaapi) {
|
||||
return va::make_avcodec_encode_device(width, height, dup(card.render_fd.el), img_offset_x, img_offset_y, true);
|
||||
|
|
@ -1390,8 +1351,7 @@ namespace platf {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<img_t>
|
||||
alloc_img() override {
|
||||
std::shared_ptr<img_t> alloc_img() override {
|
||||
auto img = std::make_shared<egl::img_descriptor_t>();
|
||||
|
||||
img->width = width;
|
||||
|
|
@ -1406,14 +1366,12 @@ namespace platf {
|
|||
return img;
|
||||
}
|
||||
|
||||
int
|
||||
dummy_img(platf::img_t *img) override {
|
||||
int dummy_img(platf::img_t *img) override {
|
||||
// Empty images are recognized as dummies by the zero sequence number
|
||||
return 0;
|
||||
}
|
||||
|
||||
capture_e
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) {
|
||||
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_logger.reset();
|
||||
|
|
@ -1458,8 +1416,7 @@ namespace platf {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds /* timeout */, bool cursor) {
|
||||
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds /* timeout */, bool cursor) {
|
||||
file_t fb_fd[4];
|
||||
|
||||
if (!pull_free_image_cb(img_out)) {
|
||||
|
|
@ -1491,8 +1448,7 @@ namespace platf {
|
|||
img->pixel_pitch = 4;
|
||||
img->row_pitch = img->pixel_pitch * img->width;
|
||||
img->data = img->buffer.data();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
img->data = nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -1502,8 +1458,7 @@ namespace platf {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
int
|
||||
init(const std::string &display_name, const ::video::config_t &config) {
|
||||
int init(const std::string &display_name, const ::video::config_t &config) {
|
||||
if (display_t::init(display_name, config)) {
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1530,8 +1485,7 @@ namespace platf {
|
|||
|
||||
} // namespace kms
|
||||
|
||||
std::shared_ptr<display_t>
|
||||
kms_display(mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
std::shared_ptr<display_t> kms_display(mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) {
|
||||
if (hwdevice_type == mem_type_e::vaapi || hwdevice_type == mem_type_e::cuda) {
|
||||
auto disp = std::make_shared<kms::display_vram_t>(hwdevice_type);
|
||||
|
||||
|
|
@ -1561,8 +1515,7 @@ namespace platf {
|
|||
*
|
||||
* This is an ugly hack :(
|
||||
*/
|
||||
void
|
||||
correlate_to_wayland(std::vector<kms::card_descriptor_t> &cds) {
|
||||
void correlate_to_wayland(std::vector<kms::card_descriptor_t> &cds) {
|
||||
auto monitors = wl::monitors();
|
||||
|
||||
BOOST_LOG(info) << "-------- Start of KMS monitor list --------"sv;
|
||||
|
|
@ -1578,8 +1531,7 @@ namespace platf {
|
|||
std::uint32_t index;
|
||||
if (index_begin == std::string_view::npos) {
|
||||
index = 1;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
index = std::max<int64_t>(1, util::from_view(name.substr(index_begin + 1)));
|
||||
}
|
||||
|
||||
|
|
@ -1594,7 +1546,8 @@ namespace platf {
|
|||
// A sanity check, it's guesswork after all.
|
||||
if (
|
||||
monitor_descriptor.viewport.width != monitor->viewport.width ||
|
||||
monitor_descriptor.viewport.height != monitor->viewport.height) {
|
||||
monitor_descriptor.viewport.height != monitor->viewport.height
|
||||
) {
|
||||
BOOST_LOG(warning)
|
||||
<< "Mismatch on expected Resolution compared to actual resolution: "sv
|
||||
<< monitor_descriptor.viewport.width << 'x' << monitor_descriptor.viewport.height
|
||||
|
|
@ -1616,8 +1569,7 @@ namespace platf {
|
|||
}
|
||||
|
||||
// A list of names of displays accepted as display_name
|
||||
std::vector<std::string>
|
||||
kms_display_names(mem_type_e hwdevice_type) {
|
||||
std::vector<std::string> kms_display_names(mem_type_e hwdevice_type) {
|
||||
int count = 0;
|
||||
|
||||
if (!fs::exists("/dev/dri")) {
|
||||
|
|
@ -1635,12 +1587,12 @@ namespace platf {
|
|||
std::vector<kms::card_descriptor_t> cds;
|
||||
std::vector<std::string> display_names;
|
||||
|
||||
fs::path card_dir { "/dev/dri"sv };
|
||||
for (auto &entry : fs::directory_iterator { card_dir }) {
|
||||
fs::path card_dir {"/dev/dri"sv};
|
||||
for (auto &entry : fs::directory_iterator {card_dir}) {
|
||||
auto file = entry.path().filename();
|
||||
|
||||
auto filestring = file.generic_string();
|
||||
if (std::string_view { filestring }.substr(0, 4) != "card"sv) {
|
||||
if (std::string_view {filestring}.substr(0, 4) != "card"sv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1655,8 +1607,7 @@ namespace platf {
|
|||
BOOST_LOG(debug) << file << " is not a CUDA device"sv;
|
||||
if (config::video.encoder == "nvenc") {
|
||||
BOOST_LOG(warning) << "Using NVENC with your display connected to a different GPU may not work properly!"sv;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,16 +12,18 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
// lib includes
|
||||
// platform includes
|
||||
#include <arpa/inet.h>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/asio/ip/host_name.hpp>
|
||||
#include <boost/process/v1.hpp>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <pwd.h>
|
||||
|
||||
// lib includes
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/asio/ip/host_name.hpp>
|
||||
#include <boost/process/v1.hpp>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// local includes
|
||||
|
|
@ -46,8 +48,7 @@ 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) {
|
||||
|
|
@ -70,8 +71,7 @@ namespace dyn {
|
|||
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) {
|
||||
TUPLE_2D_REF(fn, name, func);
|
||||
|
|
@ -88,16 +88,16 @@ namespace dyn {
|
|||
return err;
|
||||
}
|
||||
} // namespace dyn
|
||||
|
||||
namespace platf {
|
||||
using ifaddr_t = util::safe_ptr<ifaddrs, freeifaddrs>;
|
||||
|
||||
ifaddr_t
|
||||
get_ifaddrs() {
|
||||
ifaddrs *p { nullptr };
|
||||
ifaddr_t get_ifaddrs() {
|
||||
ifaddrs *p {nullptr};
|
||||
|
||||
getifaddrs(&p);
|
||||
|
||||
return ifaddr_t { p };
|
||||
return ifaddr_t {p};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -105,8 +105,7 @@ namespace platf {
|
|||
* @details This is used for the log directory, so it cannot invoke Boost logging!
|
||||
* @return The path of the appdata directory that should be used.
|
||||
*/
|
||||
fs::path
|
||||
appdata() {
|
||||
fs::path appdata() {
|
||||
static std::once_flag migration_flag;
|
||||
static fs::path config_path;
|
||||
|
||||
|
|
@ -172,8 +171,7 @@ namespace platf {
|
|||
std::cerr << "Migration failed: " << ec.message() << std::endl;
|
||||
config_path = old_config_path;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// We cannot use Boost logging because it hasn't been initialized yet!
|
||||
std::cerr << "Config exists in both "sv << old_config_path << " and "sv << config_path << ". Using "sv << config_path << " for config" << std::endl;
|
||||
std::cerr << "It is recommended to remove "sv << old_config_path << std::endl;
|
||||
|
|
@ -185,45 +183,36 @@ namespace platf {
|
|||
return config_path;
|
||||
}
|
||||
|
||||
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,
|
||||
INET6_ADDRSTRLEN);
|
||||
}
|
||||
else if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
} else if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
return std::string { data };
|
||||
return std::string {data};
|
||||
}
|
||||
|
||||
std::pair<std::uint16_t, std::string>
|
||||
from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
std::pair<std::uint16_t, std::string> from_sockaddr_ex(const sockaddr *const ip_addr) {
|
||||
char data[INET6_ADDRSTRLEN] = {};
|
||||
|
||||
auto family = ip_addr->sa_family;
|
||||
std::uint16_t port = 0;
|
||||
if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data,
|
||||
INET6_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN);
|
||||
port = ((sockaddr_in6 *) ip_addr)->sin6_port;
|
||||
}
|
||||
else if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data,
|
||||
INET_ADDRSTRLEN);
|
||||
} else if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN);
|
||||
port = ((sockaddr_in *) ip_addr)->sin_port;
|
||||
}
|
||||
|
||||
return { port, std::string { data } };
|
||||
return {port, std::string {data}};
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
|
@ -240,8 +229,7 @@ namespace platf {
|
|||
return "00:00:00:00:00:00"s;
|
||||
}
|
||||
|
||||
bp::child
|
||||
run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
bp::child run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
// clang-format off
|
||||
if (!group) {
|
||||
if (!file) {
|
||||
|
|
@ -266,8 +254,7 @@ namespace platf {
|
|||
* @brief Open a url in the default web browser.
|
||||
* @param url The url to open.
|
||||
*/
|
||||
void
|
||||
open_url(const std::string &url) {
|
||||
void open_url(const std::string &url) {
|
||||
// set working dir to user home directory
|
||||
auto working_dir = boost::filesystem::path(std::getenv("HOME"));
|
||||
std::string cmd = R"(xdg-open ")" + url + R"(")";
|
||||
|
|
@ -277,30 +264,25 @@ namespace platf {
|
|||
auto child = run_command(false, false, cmd, working_dir, _env, nullptr, ec, nullptr);
|
||||
if (ec) {
|
||||
BOOST_LOG(warning) << "Couldn't open url ["sv << url << "]: System: "sv << ec.message();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(info) << "Opened url ["sv << url << "]"sv;
|
||||
child.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority) {
|
||||
void adjust_thread_priority(thread_priority_e priority) {
|
||||
// Unimplemented
|
||||
}
|
||||
|
||||
void
|
||||
streaming_will_start() {
|
||||
void streaming_will_start() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
void
|
||||
streaming_will_stop() {
|
||||
void streaming_will_stop() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
void
|
||||
restart_on_exit() {
|
||||
void restart_on_exit() {
|
||||
char executable[PATH_MAX];
|
||||
ssize_t len = readlink("/proc/self/exe", executable, PATH_MAX - 1);
|
||||
if (len == -1) {
|
||||
|
|
@ -322,42 +304,35 @@ namespace platf {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
restart() {
|
||||
void restart() {
|
||||
// Gracefully clean up and restart ourselves instead of exiting
|
||||
atexit(restart_on_exit);
|
||||
lifetime::exit_sunshine(0, true);
|
||||
}
|
||||
|
||||
int
|
||||
set_env(const std::string &name, const std::string &value) {
|
||||
int set_env(const std::string &name, const std::string &value) {
|
||||
return setenv(name.c_str(), value.c_str(), 1);
|
||||
}
|
||||
|
||||
int
|
||||
unset_env(const std::string &name) {
|
||||
int unset_env(const std::string &name) {
|
||||
return unsetenv(name.c_str());
|
||||
}
|
||||
|
||||
bool
|
||||
request_process_group_exit(std::uintptr_t native_handle) {
|
||||
bool request_process_group_exit(std::uintptr_t native_handle) {
|
||||
if (kill(-((pid_t) native_handle), SIGTERM) == 0 || errno == ESRCH) {
|
||||
BOOST_LOG(debug) << "Successfully sent SIGTERM to process group: "sv << native_handle;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(warning) << "Unable to send SIGTERM to process group ["sv << native_handle << "]: "sv << errno;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
process_group_running(std::uintptr_t native_handle) {
|
||||
bool process_group_running(std::uintptr_t native_handle) {
|
||||
return waitpid(-((pid_t) native_handle), nullptr, WNOHANG) >= 0;
|
||||
}
|
||||
|
||||
struct sockaddr_in
|
||||
to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
||||
struct sockaddr_in to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
||||
struct sockaddr_in saddr_v4 = {};
|
||||
|
||||
saddr_v4.sin_family = AF_INET;
|
||||
|
|
@ -369,8 +344,7 @@ namespace platf {
|
|||
return saddr_v4;
|
||||
}
|
||||
|
||||
struct sockaddr_in6
|
||||
to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
||||
struct sockaddr_in6 to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
||||
struct sockaddr_in6 saddr_v6 = {};
|
||||
|
||||
saddr_v6.sin6_family = AF_INET6;
|
||||
|
|
@ -383,8 +357,7 @@ namespace platf {
|
|||
return saddr_v6;
|
||||
}
|
||||
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info) {
|
||||
bool send_batch(batched_send_info_t &send_info) {
|
||||
auto sockfd = (int) send_info.native_socket;
|
||||
struct msghdr msg = {};
|
||||
|
||||
|
|
@ -396,8 +369,7 @@ namespace platf {
|
|||
|
||||
msg.msg_name = (struct sockaddr *) &taddr_v6;
|
||||
msg.msg_namelen = sizeof(taddr_v6);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
|
||||
|
||||
msg.msg_name = (struct sockaddr *) &taddr_v4;
|
||||
|
|
@ -405,10 +377,10 @@ namespace platf {
|
|||
}
|
||||
|
||||
union {
|
||||
char buf[CMSG_SPACE(sizeof(uint16_t)) +
|
||||
std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
|
||||
char buf[CMSG_SPACE(sizeof(uint16_t)) + std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
|
||||
struct cmsghdr alignment;
|
||||
} cmbuf = {}; // Must be zeroed for CMSG_NXTHDR()
|
||||
|
||||
socklen_t cmbuflen = 0;
|
||||
|
||||
msg.msg_control = cmbuf.buf;
|
||||
|
|
@ -430,8 +402,7 @@ namespace platf {
|
|||
pktinfo_cm->cmsg_type = IPV6_PKTINFO;
|
||||
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
struct in_pktinfo pktInfo;
|
||||
|
||||
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
|
||||
|
|
@ -469,8 +440,7 @@ namespace platf {
|
|||
iovs[iovlen].iov_len = send_info.payload_size;
|
||||
iovlen++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Translate buffer descriptors into iovs
|
||||
auto payload_offset = (send_info.block_offset + seg_index) * send_info.payload_size;
|
||||
auto payload_length = payload_offset + (segs_in_batch * send_info.payload_size);
|
||||
|
|
@ -496,8 +466,7 @@ namespace platf {
|
|||
cm->cmsg_type = UDP_SEGMENT;
|
||||
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
|
||||
*((uint16_t *) CMSG_DATA(cm)) = msg_size;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
msg.msg_controllen = cmbuflen;
|
||||
}
|
||||
|
||||
|
|
@ -593,8 +562,7 @@ namespace platf {
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
send(send_info_t &send_info) {
|
||||
bool send(send_info_t &send_info) {
|
||||
auto sockfd = (int) send_info.native_socket;
|
||||
struct msghdr msg = {};
|
||||
|
||||
|
|
@ -606,8 +574,7 @@ namespace platf {
|
|||
|
||||
msg.msg_name = (struct sockaddr *) &taddr_v6;
|
||||
msg.msg_namelen = sizeof(taddr_v6);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
|
||||
|
||||
msg.msg_name = (struct sockaddr *) &taddr_v4;
|
||||
|
|
@ -618,6 +585,7 @@ namespace platf {
|
|||
char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
|
||||
struct cmsghdr alignment;
|
||||
} cmbuf;
|
||||
|
||||
socklen_t cmbuflen = 0;
|
||||
|
||||
msg.msg_control = cmbuf.buf;
|
||||
|
|
@ -637,8 +605,7 @@ namespace platf {
|
|||
pktinfo_cm->cmsg_type = IPV6_PKTINFO;
|
||||
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
struct in_pktinfo pktInfo;
|
||||
|
||||
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
|
||||
|
|
@ -703,7 +670,8 @@ namespace platf {
|
|||
class qos_t: public deinit_t {
|
||||
public:
|
||||
qos_t(int sockfd, std::vector<std::tuple<int, int, int>> options):
|
||||
sockfd(sockfd), options(options) {
|
||||
sockfd(sockfd),
|
||||
options(options) {
|
||||
qos_ref_count++;
|
||||
}
|
||||
|
||||
|
|
@ -731,8 +699,7 @@ namespace platf {
|
|||
* @param data_type The type of traffic sent on this socket.
|
||||
* @param dscp_tagging Specifies whether to enable DSCP tagging on outgoing traffic.
|
||||
*/
|
||||
std::unique_ptr<deinit_t>
|
||||
enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) {
|
||||
std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) {
|
||||
int sockfd = (int) native_socket;
|
||||
std::vector<std::tuple<int, int, int>> reset_options;
|
||||
|
||||
|
|
@ -745,8 +712,7 @@ namespace platf {
|
|||
if (address.is_v6() && !address.to_v6().is_v4_mapped()) {
|
||||
level = SOL_IPV6;
|
||||
option = IPV6_TCLASS;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
level = SOL_IP;
|
||||
option = IP_TOS;
|
||||
}
|
||||
|
|
@ -773,8 +739,7 @@ namespace platf {
|
|||
if (setsockopt(sockfd, level, option, &dscp, sizeof(dscp)) == 0) {
|
||||
// Reset TOS to -1 when QoS is disabled
|
||||
reset_options.emplace_back(std::make_tuple(level, option, -1));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(error) << "Failed to set TOS/TCLASS: "sv << errno;
|
||||
}
|
||||
}
|
||||
|
|
@ -790,20 +755,17 @@ namespace platf {
|
|||
if (setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) == 0) {
|
||||
// Reset SO_PRIORITY to 0 when QoS is disabled
|
||||
reset_options.emplace_back(std::make_tuple(SOL_SOCKET, SO_PRIORITY, 0));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(error) << "Failed to set SO_PRIORITY: "sv << errno;
|
||||
}
|
||||
|
||||
return std::make_unique<qos_t>(sockfd, reset_options);
|
||||
}
|
||||
|
||||
std::string
|
||||
get_host_name() {
|
||||
std::string get_host_name() {
|
||||
try {
|
||||
return boost::asio::ip::host_name();
|
||||
}
|
||||
catch (boost::system::system_error &err) {
|
||||
} catch (boost::system::system_error &err) {
|
||||
BOOST_LOG(error) << "Failed to get hostname: "sv << err.what();
|
||||
return "Sunshine"s;
|
||||
}
|
||||
|
|
@ -830,67 +792,62 @@ namespace platf {
|
|||
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(mem_type_e hwdevice_type);
|
||||
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(mem_type_e hwdevice_type);
|
||||
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(mem_type_e::unknown).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(hwdevice_type);
|
||||
if (sources[source::KMS]) {
|
||||
return kms_display_names(hwdevice_type);
|
||||
}
|
||||
#endif
|
||||
#ifdef SUNSHINE_BUILD_X11
|
||||
if (sources[source::X11]) return x11_display_names();
|
||||
if (sources[source::X11]) {
|
||||
return x11_display_names();
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
|
@ -899,14 +856,12 @@ namespace platf {
|
|||
* @brief Returns if GPUs/drivers have changed since the last call to this function.
|
||||
* @return `true` if a change has occurred or if it is unknown whether a change occurred.
|
||||
*/
|
||||
bool
|
||||
needs_encoder_reenumeration() {
|
||||
bool needs_encoder_reenumeration() {
|
||||
// We don't track GPU state, so we will always reenumerate. Fortunately, it is fast on Linux.
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
BOOST_LOG(info) << "Screencasting with NvFBC"sv;
|
||||
|
|
@ -935,8 +890,7 @@ namespace platf {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<deinit_t>
|
||||
init() {
|
||||
std::unique_ptr<deinit_t> init() {
|
||||
// enable low latency mode for AMD
|
||||
// https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/30039
|
||||
set_env("AMD_DEBUG", "lowlatencyenc");
|
||||
|
|
@ -1005,8 +959,7 @@ namespace platf {
|
|||
|
||||
class linux_high_precision_timer: public high_precision_timer {
|
||||
public:
|
||||
void
|
||||
sleep_for(const std::chrono::nanoseconds &duration) override {
|
||||
void sleep_for(const std::chrono::nanoseconds &duration) override {
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
|
||||
|
|
@ -1015,8 +968,7 @@ namespace platf {
|
|||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<high_precision_timer>
|
||||
create_high_precision_timer() {
|
||||
std::unique_ptr<high_precision_timer> create_high_precision_timer() {
|
||||
return std::make_unique<linux_high_precision_timer>();
|
||||
}
|
||||
} // namespace platf
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
// local includes
|
||||
#include "src/utility.h"
|
||||
|
||||
KITTY_USING_MOVE_T(file_t, int, -1, {
|
||||
|
|
@ -26,9 +28,7 @@ extern window_system_e window_system;
|
|||
namespace dyn {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
* @brief Definitions for publishing services on Linux.
|
||||
* @note Adapted from https://www.avahi.org/doxygen/html/client-publish-service_8c-example.html
|
||||
*/
|
||||
// standard includes
|
||||
#include <thread>
|
||||
|
||||
// local includes
|
||||
#include "misc.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/network.h"
|
||||
|
|
@ -84,6 +86,7 @@ namespace avahi {
|
|||
};
|
||||
|
||||
constexpr auto IF_UNSPEC = -1;
|
||||
|
||||
enum proto {
|
||||
PROTO_INET = 0, ///< IPv4
|
||||
PROTO_INET6 = 1, ///< IPv6
|
||||
|
|
@ -164,7 +167,8 @@ namespace avahi {
|
|||
const char *domain,
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
...);
|
||||
...
|
||||
);
|
||||
|
||||
typedef int (*entry_group_is_empty_fn)(EntryGroup *);
|
||||
typedef int (*entry_group_reset_fn)(EntryGroup *);
|
||||
|
|
@ -199,30 +203,31 @@ namespace avahi {
|
|||
simple_poll_new_fn simple_poll_new;
|
||||
simple_poll_free_fn simple_poll_free;
|
||||
|
||||
int
|
||||
init_common() {
|
||||
static void *handle { nullptr };
|
||||
int init_common() {
|
||||
static void *handle {nullptr};
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if (funcs_loaded) return 0;
|
||||
if (funcs_loaded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libavahi-common.so.3", "libavahi-common.so" });
|
||||
handle = dyn::handle({"libavahi-common.so.3", "libavahi-common.so"});
|
||||
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)) {
|
||||
|
|
@ -233,34 +238,35 @@ namespace avahi {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
init_client() {
|
||||
int init_client() {
|
||||
if (init_common()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *handle { nullptr };
|
||||
static void *handle {nullptr};
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if (funcs_loaded) return 0;
|
||||
if (funcs_loaded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libavahi-client.so.3", "libavahi-client.so" });
|
||||
handle = dyn::handle({"libavahi-client.so.3", "libavahi-client.so"});
|
||||
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)) {
|
||||
|
|
@ -274,13 +280,12 @@ 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>
|
||||
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>;
|
||||
|
|
@ -292,11 +297,9 @@ namespace platf::publish {
|
|||
|
||||
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) {
|
||||
|
|
@ -319,8 +322,7 @@ namespace platf::publish {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
create_services(avahi::Client *c) {
|
||||
void create_services(avahi::Client *c) {
|
||||
int ret;
|
||||
|
||||
auto fg = util::fail_guard([]() {
|
||||
|
|
@ -339,13 +341,16 @@ namespace platf::publish {
|
|||
|
||||
ret = avahi::entry_group_add_service(
|
||||
group,
|
||||
avahi::IF_UNSPEC, avahi::PROTO_UNSPEC,
|
||||
avahi::IF_UNSPEC,
|
||||
avahi::PROTO_UNSPEC,
|
||||
avahi::PublishFlags(0),
|
||||
name.get(),
|
||||
SERVICE_TYPE,
|
||||
nullptr, nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
net::map_port(nvhttp::PORT_HTTP),
|
||||
nullptr);
|
||||
nullptr
|
||||
);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == avahi::ERR_COLLISION) {
|
||||
|
|
@ -375,8 +380,7 @@ namespace platf::publish {
|
|||
fg.disable();
|
||||
}
|
||||
|
||||
void
|
||||
client_callback(avahi::Client *c, avahi::ClientState state, void *) {
|
||||
void client_callback(avahi::Client *c, avahi::ClientState state, void *) {
|
||||
switch (state) {
|
||||
case avahi::CLIENT_S_RUNNING:
|
||||
create_services(c);
|
||||
|
|
@ -387,8 +391,9 @@ namespace platf::publish {
|
|||
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:;
|
||||
}
|
||||
|
|
@ -399,7 +404,8 @@ namespace platf::publish {
|
|||
std::thread poll_thread;
|
||||
|
||||
deinit_t(std::thread poll_thread):
|
||||
poll_thread { std::move(poll_thread) } {}
|
||||
poll_thread {std::move(poll_thread)} {
|
||||
}
|
||||
|
||||
~deinit_t() override {
|
||||
if (avahi::simple_poll_quit && poll) {
|
||||
|
|
@ -412,8 +418,7 @@ namespace platf::publish {
|
|||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<::platf::deinit_t>
|
||||
start() {
|
||||
[[nodiscard]] std::unique_ptr<::platf::deinit_t> start() {
|
||||
if (avahi::init_client()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -430,13 +435,14 @@ namespace platf::publish {
|
|||
name.reset(avahi::strdup(instance_name.c_str()));
|
||||
|
||||
client.reset(
|
||||
avahi::client_new(avahi::simple_poll_get(poll.get()), avahi::ClientFlags(0), client_callback, nullptr, &avhi_error));
|
||||
avahi::client_new(avahi::simple_poll_get(poll.get()), avahi::ClientFlags(0), client_callback, nullptr, &avhi_error)
|
||||
);
|
||||
|
||||
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() });
|
||||
return std::make_unique<deinit_t>(std::thread {avahi::simple_poll_loop, poll.get()});
|
||||
}
|
||||
} // namespace platf::publish
|
||||
|
|
|
|||
|
|
@ -2,28 +2,30 @@
|
|||
* @file src/platform/linux/vaapi.cpp
|
||||
* @brief Definitions for VA-API hardware accelerated capture.
|
||||
*/
|
||||
// standard includes
|
||||
#include <fcntl.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <va/va.h>
|
||||
#include <va/va_drm.h>
|
||||
#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(
|
||||
VADisplay dpy,
|
||||
VABufferID buf_id,
|
||||
uint64_t timeout_ns) {
|
||||
return VA_STATUS_ERROR_UNIMPLEMENTED;
|
||||
}
|
||||
// vaSyncBuffer stub allows Sunshine built against libva <2.9.0 to link against ffmpeg on libva 2.9.0 or later
|
||||
VAStatus
|
||||
vaSyncBuffer(
|
||||
VADisplay dpy,
|
||||
VABufferID buf_id,
|
||||
uint64_t timeout_ns
|
||||
) {
|
||||
return VA_STATUS_ERROR_UNIMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// local includes
|
||||
#include "graphics.h"
|
||||
#include "misc.h"
|
||||
#include "src/config.h"
|
||||
|
|
@ -69,6 +71,7 @@ namespace va {
|
|||
|
||||
// Number of layers making up the surface.
|
||||
uint32_t num_layers;
|
||||
|
||||
struct {
|
||||
// DRM format fourcc of this layer (DRM_FOURCC_*).
|
||||
uint32_t drm_format;
|
||||
|
|
@ -89,13 +92,11 @@ namespace va {
|
|||
|
||||
using display_t = util::safe_ptr_v2<void, VAStatus, vaTerminate>;
|
||||
|
||||
int
|
||||
vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *encode_device, AVBufferRef **hw_device_buf);
|
||||
int vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *encode_device, AVBufferRef **hw_device_buf);
|
||||
|
||||
class va_t: public platf::avcodec_encode_device_t {
|
||||
public:
|
||||
int
|
||||
init(int in_width, int in_height, file_t &&render_device) {
|
||||
int init(int in_width, int in_height, file_t &&render_device) {
|
||||
file = std::move(render_device);
|
||||
|
||||
if (!gbm::create_device) {
|
||||
|
|
@ -135,8 +136,7 @@ namespace va {
|
|||
* @param profile The profile to match.
|
||||
* @return A valid encoding entrypoint or 0 on failure.
|
||||
*/
|
||||
VAEntrypoint
|
||||
select_va_entrypoint(VAProfile profile) {
|
||||
VAEntrypoint select_va_entrypoint(VAProfile profile) {
|
||||
std::vector<VAEntrypoint> entrypoints(vaMaxNumEntrypoints(va_display));
|
||||
int num_eps;
|
||||
auto status = vaQueryConfigEntrypoints(va_display, profile, entrypoints.data(), &num_eps);
|
||||
|
|
@ -166,8 +166,7 @@ namespace va {
|
|||
* @param profile The profile to match.
|
||||
* @return Boolean value indicating if the profile is supported.
|
||||
*/
|
||||
bool
|
||||
is_va_profile_supported(VAProfile profile) {
|
||||
bool is_va_profile_supported(VAProfile profile) {
|
||||
std::vector<VAProfile> profiles(vaMaxNumProfiles(va_display));
|
||||
int num_profs;
|
||||
auto status = vaQueryConfigProfiles(va_display, profiles.data(), &num_profs);
|
||||
|
|
@ -185,13 +184,11 @@ namespace va {
|
|||
* @param ctx The FFmpeg codec context.
|
||||
* @return The matching VA profile or `VAProfileNone` on failure.
|
||||
*/
|
||||
VAProfile
|
||||
get_va_profile(AVCodecContext *ctx) {
|
||||
VAProfile get_va_profile(AVCodecContext *ctx) {
|
||||
if (ctx->codec_id == AV_CODEC_ID_H264) {
|
||||
// There's no VAAPI profile for H.264 4:4:4
|
||||
return VAProfileH264High;
|
||||
}
|
||||
else if (ctx->codec_id == AV_CODEC_ID_HEVC) {
|
||||
} else if (ctx->codec_id == AV_CODEC_ID_HEVC) {
|
||||
switch (ctx->profile) {
|
||||
case FF_PROFILE_HEVC_REXT:
|
||||
switch (av_pix_fmt_desc_get(ctx->sw_pix_fmt)->comp[0].depth) {
|
||||
|
|
@ -206,8 +203,7 @@ namespace va {
|
|||
case FF_PROFILE_HEVC_MAIN:
|
||||
return VAProfileHEVCMain;
|
||||
}
|
||||
}
|
||||
else if (ctx->codec_id == AV_CODEC_ID_AV1) {
|
||||
} else if (ctx->codec_id == AV_CODEC_ID_AV1) {
|
||||
switch (ctx->profile) {
|
||||
case FF_PROFILE_AV1_HIGH:
|
||||
return VAProfileAV1Profile1;
|
||||
|
|
@ -220,8 +216,7 @@ namespace va {
|
|||
return VAProfileNone;
|
||||
}
|
||||
|
||||
void
|
||||
init_codec_options(AVCodecContext *ctx, AVDictionary **options) override {
|
||||
void init_codec_options(AVCodecContext *ctx, AVDictionary **options) override {
|
||||
auto va_profile = get_va_profile(ctx);
|
||||
if (va_profile == VAProfileNone || !is_va_profile_supported(va_profile)) {
|
||||
// Don't bother doing anything if the profile isn't supported
|
||||
|
|
@ -239,19 +234,18 @@ namespace va {
|
|||
if (va_entrypoint == VAEntrypointEncSliceLP) {
|
||||
BOOST_LOG(info) << "Using LP encoding mode"sv;
|
||||
av_dict_set_int(options, "low_power", 1, 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(info) << "Using normal encoding mode"sv;
|
||||
}
|
||||
|
||||
VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
|
||||
VAConfigAttrib rc_attr = {VAConfigAttribRateControl};
|
||||
auto status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &rc_attr, 1);
|
||||
if (status != VA_STATUS_SUCCESS) {
|
||||
// Stick to the default rate control (CQP)
|
||||
rc_attr.value = 0;
|
||||
}
|
||||
|
||||
VAConfigAttrib slice_attr = { VAConfigAttribEncMaxSlices };
|
||||
VAConfigAttrib slice_attr = {VAConfigAttribEncMaxSlices};
|
||||
status = vaGetConfigAttributes(va_display, va_profile, va_entrypoint, &slice_attr, 1);
|
||||
if (status != VA_STATUS_SUCCESS) {
|
||||
// Assume only a single slice is supported
|
||||
|
|
@ -281,27 +275,22 @@ namespace va {
|
|||
if (rc_attr.value & VA_RC_VBR) {
|
||||
BOOST_LOG(info) << "Using VBR with single frame VBV size"sv;
|
||||
av_dict_set(options, "rc_mode", "VBR", 0);
|
||||
}
|
||||
else if (rc_attr.value & VA_RC_CBR) {
|
||||
} else if (rc_attr.value & VA_RC_CBR) {
|
||||
BOOST_LOG(info) << "Using CBR with single frame VBV size"sv;
|
||||
av_dict_set(options, "rc_mode", "CBR", 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(warning) << "Using CQP with single frame VBV size"sv;
|
||||
av_dict_set_int(options, "qp", config::video.qp, 0);
|
||||
}
|
||||
}
|
||||
else if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
|
||||
} else if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
|
||||
BOOST_LOG(warning) << "Using CQP rate control"sv;
|
||||
av_dict_set_int(options, "qp", config::video.qp, 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(info) << "Using default rate control"sv;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx_buf) override {
|
||||
int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx_buf) override {
|
||||
this->hwframe.reset(frame);
|
||||
this->frame = frame;
|
||||
|
||||
|
|
@ -321,7 +310,8 @@ namespace va {
|
|||
surface,
|
||||
va::SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
|
||||
va::EXPORT_SURFACE_WRITE_ONLY | va::EXPORT_SURFACE_SEPARATE_LAYERS,
|
||||
&prime);
|
||||
&prime
|
||||
);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't export va surface handle: ["sv << (int) surface << "]: "sv << vaErrorStr(status);
|
||||
|
||||
|
|
@ -377,8 +367,7 @@ namespace va {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
apply_colorspace() override {
|
||||
void apply_colorspace() override {
|
||||
sws.apply_colorspace(colorspace);
|
||||
}
|
||||
|
||||
|
|
@ -401,8 +390,7 @@ namespace va {
|
|||
|
||||
class va_ram_t: public va_t {
|
||||
public:
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
int convert(platf::img_t &img) override {
|
||||
sws.load_ram(img);
|
||||
|
||||
sws.convert(nv12->buf);
|
||||
|
|
@ -412,15 +400,13 @@ namespace va {
|
|||
|
||||
class va_vram_t: public va_t {
|
||||
public:
|
||||
int
|
||||
convert(platf::img_t &img) override {
|
||||
int convert(platf::img_t &img) override {
|
||||
auto &descriptor = (egl::img_descriptor_t &) img;
|
||||
|
||||
if (descriptor.sequence == 0) {
|
||||
// For dummy images, use a blank RGB texture instead of importing a DMA-BUF
|
||||
rgb = egl::create_blank(img);
|
||||
}
|
||||
else if (descriptor.sequence > sequence) {
|
||||
} else if (descriptor.sequence > sequence) {
|
||||
sequence = descriptor.sequence;
|
||||
|
||||
rgb = egl::rgb_t {};
|
||||
|
|
@ -440,8 +426,7 @@ namespace va {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
init(int in_width, int in_height, file_t &&render_device, int offset_x, int offset_y) {
|
||||
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;
|
||||
}
|
||||
|
|
@ -471,6 +456,7 @@ namespace va {
|
|||
void *xdisplay;
|
||||
int fd;
|
||||
} drm;
|
||||
|
||||
int drm_fd;
|
||||
} VAAPIDevicePriv;
|
||||
|
||||
|
|
@ -494,13 +480,11 @@ namespace va {
|
|||
unsigned int driver_quirks;
|
||||
} AVVAAPIDeviceContext;
|
||||
|
||||
static void
|
||||
__log(void *level, const char *msg) {
|
||||
static void __log(void *level, const char *msg) {
|
||||
BOOST_LOG(*(boost::log::sources::severity_logger<int> *) level) << msg;
|
||||
}
|
||||
|
||||
static void
|
||||
vaapi_hwdevice_ctx_free(AVHWDeviceContext *ctx) {
|
||||
static void vaapi_hwdevice_ctx_free(AVHWDeviceContext *ctx) {
|
||||
auto hwctx = (AVVAAPIDeviceContext *) ctx->hwctx;
|
||||
auto priv = (VAAPIDevicePriv *) ctx->user_opaque;
|
||||
|
||||
|
|
@ -509,8 +493,7 @@ namespace va {
|
|||
av_freep(&priv);
|
||||
}
|
||||
|
||||
int
|
||||
vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *base, AVBufferRef **hw_device_buf) {
|
||||
int vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *base, AVBufferRef **hw_device_buf) {
|
||||
auto va = (va::va_t *) base;
|
||||
auto fd = dup(va->file.el);
|
||||
|
||||
|
|
@ -522,7 +505,7 @@ namespace va {
|
|||
av_free(priv);
|
||||
});
|
||||
|
||||
va::display_t display { vaGetDisplayDRM(fd) };
|
||||
va::display_t display {vaGetDisplayDRM(fd)};
|
||||
if (!display) {
|
||||
auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str();
|
||||
|
||||
|
|
@ -556,7 +539,7 @@ namespace va {
|
|||
|
||||
auto err = av_hwdevice_ctx_init(*hw_device_buf);
|
||||
if (err) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
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);
|
||||
|
||||
return err;
|
||||
|
|
@ -565,8 +548,7 @@ namespace va {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
query(display_t::pointer display, VAProfile profile) {
|
||||
static bool query(display_t::pointer display, VAProfile profile) {
|
||||
std::vector<VAEntrypoint> entrypoints;
|
||||
entrypoints.resize(vaMaxNumEntrypoints(display));
|
||||
|
||||
|
|
@ -587,15 +569,14 @@ namespace va {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
validate(int fd) {
|
||||
va::display_t display { vaGetDisplayDRM(fd) };
|
||||
bool validate(int fd) {
|
||||
va::display_t display {vaGetDisplayDRM(fd)};
|
||||
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;
|
||||
|
|
@ -623,8 +604,7 @@ namespace va {
|
|||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram) {
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram) {
|
||||
if (vram) {
|
||||
auto egl = std::make_unique<va::va_vram_t>();
|
||||
if (egl->init(width, height, std::move(card), offset_x, offset_y)) {
|
||||
|
|
@ -644,8 +624,7 @@ namespace va {
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram) {
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(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);
|
||||
|
|
@ -659,8 +638,7 @@ namespace va {
|
|||
return make_avcodec_encode_device(width, height, std::move(file), offset_x, offset_y, vram);
|
||||
}
|
||||
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(int width, int height, bool vram) {
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(int width, int height, bool vram) {
|
||||
return make_avcodec_encode_device(width, height, 0, 0, vram);
|
||||
}
|
||||
} // namespace va
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// local includes
|
||||
#include "misc.h"
|
||||
#include "src/platform/common.h"
|
||||
|
||||
namespace egl {
|
||||
struct surface_descriptor_t;
|
||||
}
|
||||
|
||||
namespace va {
|
||||
/**
|
||||
* Width --> Width of the image
|
||||
|
|
@ -18,14 +20,10 @@ namespace va {
|
|||
* offset_y --> Vertical offset of the image in the texture
|
||||
* file_t card --> The file descriptor of the render device used for encoding
|
||||
*/
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(int width, int height, bool vram);
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram);
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram);
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(int width, int height, bool vram);
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram);
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(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);
|
||||
bool validate(int fd);
|
||||
} // namespace va
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@
|
|||
* @file src/platform/linux/wayland.cpp
|
||||
* @brief Definitions for Wayland capture.
|
||||
*/
|
||||
// standard includes
|
||||
#include <cstdlib>
|
||||
|
||||
// platform includes
|
||||
#include <poll.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
// local includes
|
||||
#include "graphics.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
|
|
@ -27,16 +30,14 @@ 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...)) {
|
||||
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) {
|
||||
int display_t::init(const char *display_name) {
|
||||
if (!display_name) {
|
||||
display_name = std::getenv("WAYLAND_DISPLAY");
|
||||
}
|
||||
|
|
@ -57,8 +58,7 @@ namespace wl {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
display_t::roundtrip() {
|
||||
void display_t::roundtrip() {
|
||||
wl_display_roundtrip(display_internal.get());
|
||||
}
|
||||
|
||||
|
|
@ -67,8 +67,7 @@ namespace wl {
|
|||
* @param timeout The timeout in milliseconds.
|
||||
* @return `true` if new events were dispatched or `false` if the timeout expired.
|
||||
*/
|
||||
bool
|
||||
display_t::dispatch(std::chrono::milliseconds timeout) {
|
||||
bool display_t::dispatch(std::chrono::milliseconds timeout) {
|
||||
// Check if any events are queued already. If not, flush
|
||||
// outgoing events, and prepare to wait for readability.
|
||||
if (wl_display_prepare_read(display_internal.get()) == 0) {
|
||||
|
|
@ -81,8 +80,7 @@ namespace wl {
|
|||
if (poll(&pfd, 1, timeout.count()) == 1 && (pfd.revents & POLLIN)) {
|
||||
// Read the new event(s)
|
||||
wl_display_read_events(display_internal.get());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// We timed out, so unlock the queue now
|
||||
wl_display_cancel_read(display_internal.get());
|
||||
return false;
|
||||
|
|
@ -94,13 +92,12 @@ namespace wl {
|
|||
return true;
|
||||
}
|
||||
|
||||
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 },
|
||||
output {output},
|
||||
wl_listener {
|
||||
&CLASS_CALL(monitor_t, wl_geometry),
|
||||
&CLASS_CALL(monitor_t, wl_mode),
|
||||
|
|
@ -113,46 +110,40 @@ namespace wl {
|
|||
&CLASS_CALL(monitor_t, xdg_done),
|
||||
&CLASS_CALL(monitor_t, xdg_name),
|
||||
&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) {
|
||||
BOOST_LOG(info) << "Logical size: "sv << width << 'x' << height;
|
||||
}
|
||||
|
||||
void
|
||||
monitor_t::wl_mode(wl_output *wl_output, std::uint32_t flags,
|
||||
std::int32_t width, std::int32_t height, std::int32_t refresh) {
|
||||
void monitor_t::wl_mode(wl_output *wl_output, std::uint32_t flags, std::int32_t width, std::int32_t height, std::int32_t refresh) {
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
|
||||
BOOST_LOG(info) << "Resolution: "sv << width << 'x' << height;
|
||||
}
|
||||
|
||||
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, &xdg_listener, this);
|
||||
wl_output_add_listener(output, &wl_listener, this);
|
||||
|
|
@ -160,34 +151,33 @@ namespace wl {
|
|||
|
||||
interface_t::interface_t() noexcept
|
||||
:
|
||||
output_manager { nullptr },
|
||||
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)) {
|
||||
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, 2)));
|
||||
}
|
||||
else if (!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) {
|
||||
(wl_output *) wl_registry_bind(registry, id, &wl_output_interface, 2)
|
||||
)
|
||||
);
|
||||
} 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);
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -195,13 +185,15 @@ namespace wl {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
status {READY},
|
||||
frames {},
|
||||
current_frame {&frames[0]},
|
||||
listener {
|
||||
&CLASS_CALL(dmabuf_t, frame),
|
||||
&CLASS_CALL(dmabuf_t, object),
|
||||
&CLASS_CALL(dmabuf_t, ready),
|
||||
|
|
@ -209,8 +201,7 @@ namespace wl {
|
|||
} {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -223,15 +214,19 @@ namespace wl {
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
std::uint32_t buffer_flags, std::uint32_t flags,
|
||||
std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::uint32_t x,
|
||||
std::uint32_t y,
|
||||
std::uint32_t buffer_flags,
|
||||
std::uint32_t flags,
|
||||
std::uint32_t format,
|
||||
std::uint32_t high, std::uint32_t low,
|
||||
std::uint32_t obj_count) {
|
||||
std::uint32_t high,
|
||||
std::uint32_t low,
|
||||
std::uint32_t obj_count
|
||||
) {
|
||||
auto next_frame = get_next_frame();
|
||||
|
||||
next_frame->sd.fourcc = format;
|
||||
|
|
@ -240,15 +235,15 @@ namespace wl {
|
|||
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,
|
||||
std::uint32_t size,
|
||||
std::uint32_t offset,
|
||||
std::uint32_t stride,
|
||||
std::uint32_t plane_index) {
|
||||
std::uint32_t plane_index
|
||||
) {
|
||||
auto next_frame = get_next_frame();
|
||||
|
||||
next_frame->sd.fds[plane_index] = fd;
|
||||
|
|
@ -256,10 +251,12 @@ namespace wl {
|
|||
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) {
|
||||
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();
|
||||
|
|
@ -268,10 +265,10 @@ namespace wl {
|
|||
status = READY;
|
||||
}
|
||||
|
||||
void
|
||||
dmabuf_t::cancel(
|
||||
void dmabuf_t::cancel(
|
||||
zwlr_export_dmabuf_frame_v1 *frame,
|
||||
std::uint32_t reason) {
|
||||
std::uint32_t reason
|
||||
) {
|
||||
zwlr_export_dmabuf_frame_v1_destroy(frame);
|
||||
|
||||
auto next_frame = get_next_frame();
|
||||
|
|
@ -280,8 +277,7 @@ namespace wl {
|
|||
status = REINIT;
|
||||
}
|
||||
|
||||
void
|
||||
frame_t::destroy() {
|
||||
void frame_t::destroy() {
|
||||
for (auto x = 0; x < 4; ++x) {
|
||||
if (sd.fds[x] >= 0) {
|
||||
close(sd.fds[x]);
|
||||
|
|
@ -296,8 +292,7 @@ namespace wl {
|
|||
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)) {
|
||||
|
|
@ -323,15 +318,13 @@ namespace wl {
|
|||
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;
|
||||
|
|
@ -339,4 +332,4 @@ namespace wl {
|
|||
|
||||
} // namespace wl
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
#pragma GCC diagnostic pop
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <bitset>
|
||||
|
||||
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||
|
|
@ -11,6 +12,7 @@
|
|||
#include <xdg-output-unstable-v1.h>
|
||||
#endif
|
||||
|
||||
// local includes
|
||||
#include "graphics.h"
|
||||
|
||||
/**
|
||||
|
|
@ -27,8 +29,7 @@ namespace wl {
|
|||
frame_t();
|
||||
egl::surface_descriptor_t sd;
|
||||
|
||||
void
|
||||
destroy();
|
||||
void destroy();
|
||||
};
|
||||
|
||||
class dmabuf_t {
|
||||
|
|
@ -42,50 +43,52 @@ namespace wl {
|
|||
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,
|
||||
std::uint32_t buffer_flags, std::uint32_t flags,
|
||||
std::uint32_t width,
|
||||
std::uint32_t height,
|
||||
std::uint32_t x,
|
||||
std::uint32_t y,
|
||||
std::uint32_t buffer_flags,
|
||||
std::uint32_t flags,
|
||||
std::uint32_t format,
|
||||
std::uint32_t high, std::uint32_t low,
|
||||
std::uint32_t obj_count);
|
||||
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,
|
||||
std::uint32_t size,
|
||||
std::uint32_t offset,
|
||||
std::uint32_t stride,
|
||||
std::uint32_t plane_index);
|
||||
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);
|
||||
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);
|
||||
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];
|
||||
}
|
||||
|
||||
|
|
@ -102,38 +105,31 @@ namespace wl {
|
|||
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
|
||||
wl_geometry(wl_output *wl_output, std::int32_t x, std::int32_t y,
|
||||
std::int32_t physical_width, std::int32_t physical_height, std::int32_t subpixel,
|
||||
const char *make, const char *model, std::int32_t transform) {}
|
||||
void
|
||||
wl_mode(wl_output *wl_output, std::uint32_t flags,
|
||||
std::int32_t width, std::int32_t height, std::int32_t refresh);
|
||||
void
|
||||
wl_done(wl_output *wl_output) {}
|
||||
void
|
||||
wl_scale(wl_output *wl_output, std::int32_t factor) {}
|
||||
void xdg_done(zxdg_output_v1 *) {
|
||||
}
|
||||
|
||||
void
|
||||
listen(zxdg_output_manager_v1 *output_manager);
|
||||
void wl_geometry(wl_output *wl_output, std::int32_t x, std::int32_t y, std::int32_t physical_width, std::int32_t physical_height, std::int32_t subpixel, const char *make, const char *model, std::int32_t transform) {
|
||||
}
|
||||
|
||||
void wl_mode(wl_output *wl_output, std::uint32_t flags, std::int32_t width, std::int32_t height, std::int32_t refresh);
|
||||
|
||||
void wl_done(wl_output *wl_output) {
|
||||
}
|
||||
|
||||
void wl_scale(wl_output *wl_output, std::int32_t factor) {
|
||||
}
|
||||
|
||||
void listen(zxdg_output_manager_v1 *output_manager);
|
||||
|
||||
wl_output *output;
|
||||
|
||||
|
|
@ -162,31 +158,25 @@ namespace wl {
|
|||
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);
|
||||
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;
|
||||
|
||||
|
|
@ -201,24 +191,19 @@ namespace wl {
|
|||
* @param display_name The name of the display.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
init(const char *display_name = nullptr);
|
||||
int init(const char *display_name = nullptr);
|
||||
|
||||
// Roundtrip with Wayland connection
|
||||
void
|
||||
roundtrip();
|
||||
void roundtrip();
|
||||
|
||||
// Wait up to the timeout to read and dispatch new events
|
||||
bool
|
||||
dispatch(std::chrono::milliseconds timeout);
|
||||
bool dispatch(std::chrono::milliseconds timeout);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
|
@ -226,11 +211,9 @@ namespace wl {
|
|||
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
|
||||
|
||||
|
|
@ -243,15 +226,12 @@ namespace wl {
|
|||
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;
|
||||
|
||||
|
|
@ -261,10 +241,12 @@ namespace wl {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -2,18 +2,19 @@
|
|||
* @file src/platform/linux/wlgrab.cpp
|
||||
* @brief Definitions for wlgrab capture.
|
||||
*/
|
||||
// standard includes
|
||||
#include <thread>
|
||||
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include "src/logging.h"
|
||||
#include "src/video.h"
|
||||
|
||||
// local includes
|
||||
#include "cuda.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/video.h"
|
||||
#include "vaapi.h"
|
||||
#include "wayland.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace wl {
|
||||
static int env_width;
|
||||
static int env_height;
|
||||
|
|
@ -27,9 +28,8 @@ namespace wl {
|
|||
|
||||
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;
|
||||
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()) {
|
||||
|
|
@ -82,13 +82,11 @@ namespace wl {
|
|||
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(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
inline platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
auto to = std::chrono::steady_clock::now() + timeout;
|
||||
|
||||
// Dispatch events until we get a new frame or the timeout expires
|
||||
|
|
@ -105,7 +103,8 @@ namespace wl {
|
|||
if (
|
||||
dmabuf.status == dmabuf_t::REINIT ||
|
||||
current_frame->sd.width != width ||
|
||||
current_frame->sd.height != height) {
|
||||
current_frame->sd.height != height
|
||||
) {
|
||||
return platf::capture_e::reinit;
|
||||
}
|
||||
|
||||
|
|
@ -125,8 +124,7 @@ namespace wl {
|
|||
|
||||
class wlr_ram_t: public wlr_t {
|
||||
public:
|
||||
platf::capture_e
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
platf::capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_logger.reset();
|
||||
|
|
@ -171,8 +169,7 @@ namespace wl {
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
platf::capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
auto status = wlr_t::snapshot(pull_free_image_cb, img_out, timeout, cursor);
|
||||
if (status != platf::capture_e::ok) {
|
||||
return status;
|
||||
|
|
@ -204,8 +201,7 @@ namespace wl {
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
int
|
||||
init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &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;
|
||||
}
|
||||
|
|
@ -225,8 +221,7 @@ namespace wl {
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override {
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override {
|
||||
#ifdef SUNSHINE_BUILD_VAAPI
|
||||
if (mem_type == platf::mem_type_e::vaapi) {
|
||||
return va::make_avcodec_encode_device(width, height, false);
|
||||
|
|
@ -242,8 +237,7 @@ namespace wl {
|
|||
return std::make_unique<platf::avcodec_encode_device_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;
|
||||
|
|
@ -260,8 +254,7 @@ namespace wl {
|
|||
|
||||
class wlr_vram_t: public wlr_t {
|
||||
public:
|
||||
platf::capture_e
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
platf::capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_logger.reset();
|
||||
|
|
@ -306,8 +299,7 @@ namespace wl {
|
|||
return platf::capture_e::ok;
|
||||
}
|
||||
|
||||
platf::capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
auto status = wlr_t::snapshot(pull_free_image_cb, img_out, timeout, cursor);
|
||||
if (status != platf::capture_e::ok) {
|
||||
return status;
|
||||
|
|
@ -332,8 +324,7 @@ namespace wl {
|
|||
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->width = width;
|
||||
|
|
@ -348,8 +339,7 @@ namespace wl {
|
|||
return img;
|
||||
}
|
||||
|
||||
std::unique_ptr<platf::avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override {
|
||||
std::unique_ptr<platf::avcodec_encode_device_t> make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override {
|
||||
#ifdef SUNSHINE_BUILD_VAAPI
|
||||
if (mem_type == platf::mem_type_e::vaapi) {
|
||||
return va::make_avcodec_encode_device(width, height, 0, 0, true);
|
||||
|
|
@ -365,8 +355,7 @@ namespace wl {
|
|||
return std::make_unique<platf::avcodec_encode_device_t>();
|
||||
}
|
||||
|
||||
int
|
||||
dummy_img(platf::img_t *img) override {
|
||||
int dummy_img(platf::img_t *img) override {
|
||||
// Empty images are recognized as dummies by the zero sequence number
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -377,8 +366,7 @@ namespace wl {
|
|||
} // 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) {
|
||||
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;
|
||||
|
|
@ -401,8 +389,7 @@ namespace platf {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -2,61 +2,49 @@
|
|||
* @file src/platform/linux/x11grab.cpp
|
||||
* @brief Definitions for x11 capture.
|
||||
*/
|
||||
#include "src/platform/common.h"
|
||||
|
||||
// standard includes
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
|
||||
// plaform includes
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <xcb/shm.h>
|
||||
#include <xcb/xfixes.h>
|
||||
|
||||
#include "src/config.h"
|
||||
#include "src/globals.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/task_pool.h"
|
||||
#include "src/video.h"
|
||||
|
||||
// local includes
|
||||
#include "cuda.h"
|
||||
#include "graphics.h"
|
||||
#include "misc.h"
|
||||
#include "src/config.h"
|
||||
#include "src/globals.h"
|
||||
#include "src/logging.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/task_pool.h"
|
||||
#include "src/video.h"
|
||||
#include "vaapi.h"
|
||||
#include "x11grab.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platf {
|
||||
int
|
||||
load_xcb();
|
||||
int
|
||||
load_x11();
|
||||
int load_xcb();
|
||||
int load_x11();
|
||||
|
||||
namespace x11 {
|
||||
#define _FN(x, ret, args) \
|
||||
#define _FN(x, ret, args) \
|
||||
typedef ret(*x##_fn) args; \
|
||||
static x##_fn x
|
||||
|
||||
_FN(GetImage, XImage *,
|
||||
(
|
||||
Display * display,
|
||||
Drawable d,
|
||||
int x, int y,
|
||||
unsigned int width, unsigned int height,
|
||||
unsigned long plane_mask,
|
||||
int format));
|
||||
_FN(GetImage, XImage *, (Display * display, Drawable d, int x, int y, unsigned int width, unsigned int height, unsigned long plane_mask, int format));
|
||||
|
||||
_FN(OpenDisplay, Display *, (_Xconst char *display_name));
|
||||
_FN(GetWindowAttributes, Status,
|
||||
(
|
||||
Display * display,
|
||||
Window w,
|
||||
XWindowAttributes *window_attributes_return));
|
||||
_FN(GetWindowAttributes, Status, (Display * display, Window w, XWindowAttributes *window_attributes_return));
|
||||
|
||||
_FN(CloseDisplay, int, (Display * display));
|
||||
_FN(Free, int, (void *data));
|
||||
|
|
@ -70,27 +58,28 @@ namespace platf {
|
|||
_FN(FreeOutputInfo, void, (XRROutputInfo * outputInfo));
|
||||
_FN(FreeCrtcInfo, void, (XRRCrtcInfo * crtcInfo));
|
||||
|
||||
static int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
static int init() {
|
||||
static void *handle {nullptr};
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if (funcs_loaded) return 0;
|
||||
if (funcs_loaded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libXrandr.so.2", "libXrandr.so" });
|
||||
handle = dyn::handle({"libXrandr.so.2", "libXrandr.so"});
|
||||
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)) {
|
||||
|
|
@ -102,25 +91,27 @@ namespace platf {
|
|||
}
|
||||
|
||||
} // namespace rr
|
||||
|
||||
namespace fix {
|
||||
_FN(GetCursorImage, XFixesCursorImage *, (Display * dpy));
|
||||
|
||||
static int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
static int init() {
|
||||
static void *handle {nullptr};
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if (funcs_loaded) return 0;
|
||||
if (funcs_loaded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libXfixes.so.3", "libXfixes.so" });
|
||||
handle = dyn::handle({"libXfixes.so.3", "libXfixes.so"});
|
||||
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)) {
|
||||
|
|
@ -132,27 +123,28 @@ namespace platf {
|
|||
}
|
||||
} // namespace fix
|
||||
|
||||
static int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
static int init() {
|
||||
static void *handle {nullptr};
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if (funcs_loaded) return 0;
|
||||
if (funcs_loaded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libX11.so.6", "libX11.so" });
|
||||
handle = dyn::handle({"libX11.so.6", "libX11.so"});
|
||||
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)) {
|
||||
|
|
@ -167,31 +159,13 @@ namespace platf {
|
|||
namespace xcb {
|
||||
static xcb_extension_t *shm_id;
|
||||
|
||||
_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_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,
|
||||
(
|
||||
xcb_connection_t * c,
|
||||
xcb_drawable_t drawable,
|
||||
int16_t x, int16_t y,
|
||||
uint16_t width, uint16_t height,
|
||||
uint32_t plane_mask,
|
||||
uint8_t format,
|
||||
xcb_shm_seg_t shmseg,
|
||||
uint32_t offset));
|
||||
_FN(shm_get_image_unchecked, xcb_shm_get_image_cookie_t, (xcb_connection_t * c, xcb_drawable_t drawable, int16_t x, int16_t y, uint16_t width, uint16_t height, uint32_t plane_mask, uint8_t format, xcb_shm_seg_t shmseg, uint32_t offset));
|
||||
|
||||
_FN(shm_attach, xcb_void_cookie_t,
|
||||
(xcb_connection_t * c,
|
||||
xcb_shm_seg_t shmseg,
|
||||
uint32_t shmid,
|
||||
uint8_t read_only));
|
||||
_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 *,
|
||||
(xcb_connection_t * c, xcb_extension_t *ext));
|
||||
_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));
|
||||
|
|
@ -200,25 +174,26 @@ namespace platf {
|
|||
_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() {
|
||||
static void *handle { nullptr };
|
||||
int init_shm() {
|
||||
static void *handle {nullptr};
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if (funcs_loaded) return 0;
|
||||
if (funcs_loaded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libxcb-shm.so.0", "libxcb-shm.so" });
|
||||
handle = dyn::handle({"libxcb-shm.so.0", "libxcb-shm.so"});
|
||||
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)) {
|
||||
|
|
@ -229,28 +204,29 @@ namespace platf {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
init() {
|
||||
static void *handle { nullptr };
|
||||
int init() {
|
||||
static void *handle {nullptr};
|
||||
static bool funcs_loaded = false;
|
||||
|
||||
if (funcs_loaded) return 0;
|
||||
if (funcs_loaded) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
handle = dyn::handle({ "libxcb.so.1", "libxcb.so" });
|
||||
handle = dyn::handle({"libxcb.so.1", "libxcb.so"});
|
||||
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)) {
|
||||
|
|
@ -264,10 +240,8 @@ namespace platf {
|
|||
#undef _FN
|
||||
} // 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>;
|
||||
|
|
@ -282,9 +256,13 @@ namespace platf {
|
|||
class shm_id_t {
|
||||
public:
|
||||
shm_id_t():
|
||||
id { -1 } {}
|
||||
id {-1} {
|
||||
}
|
||||
|
||||
shm_id_t(int id):
|
||||
id { id } {}
|
||||
id {id} {
|
||||
}
|
||||
|
||||
shm_id_t(shm_id_t &&other) noexcept:
|
||||
id(other.id) {
|
||||
other.id = -1;
|
||||
|
|
@ -296,15 +274,19 @@ namespace platf {
|
|||
id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int id;
|
||||
};
|
||||
|
||||
class shm_data_t {
|
||||
public:
|
||||
shm_data_t():
|
||||
data { (void *) -1 } {}
|
||||
data {(void *) -1} {
|
||||
}
|
||||
|
||||
shm_data_t(void *data):
|
||||
data { data } {}
|
||||
data {data} {
|
||||
}
|
||||
|
||||
shm_data_t(shm_data_t &&other) noexcept:
|
||||
data(other.data) {
|
||||
|
|
@ -331,9 +313,8 @@ namespace platf {
|
|||
}
|
||||
};
|
||||
|
||||
static void
|
||||
blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) {
|
||||
xcursor_t overlay { x11::fix::GetCursorImage(display) };
|
||||
static void blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) {
|
||||
xcursor_t overlay {x11::fix::GetCursorImage(display)};
|
||||
|
||||
if (!overlay) {
|
||||
BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv;
|
||||
|
|
@ -370,8 +351,7 @@ namespace platf {
|
|||
auto alpha = (*(uint *) pixel_p) >> 24u;
|
||||
if (alpha == 255) {
|
||||
*pixels_begin = *pixel_p;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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;
|
||||
|
|
@ -398,18 +378,20 @@ namespace platf {
|
|||
// int env_width, env_height;
|
||||
|
||||
x11_attr_t(mem_type_e mem_type):
|
||||
xdisplay { x11::OpenDisplay(nullptr) }, xwindow {}, xattr {}, mem_type { 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
delay = std::chrono::nanoseconds { 1s } / config.framerate;
|
||||
delay = std::chrono::nanoseconds {1s} / config.framerate;
|
||||
|
||||
xwindow = DefaultRootWindow(xdisplay.get());
|
||||
|
||||
|
|
@ -422,13 +404,13 @@ namespace platf {
|
|||
|
||||
if (streamedMonitor != -1) {
|
||||
BOOST_LOG(info) << "Configuring selected display ("sv << streamedMonitor << ") to stream"sv;
|
||||
screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) };
|
||||
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) {
|
||||
output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) };
|
||||
output_info_t out_info {x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x])};
|
||||
if (out_info) {
|
||||
if (monitor++ == streamedMonitor) {
|
||||
result = std::move(out_info);
|
||||
|
|
@ -443,7 +425,7 @@ namespace platf {
|
|||
}
|
||||
|
||||
if (result->crtc) {
|
||||
crtc_info_t crt_info { x11::rr::GetCrtcInfo(xdisplay.get(), screenr.get(), 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;
|
||||
|
||||
|
|
@ -451,14 +433,12 @@ namespace platf {
|
|||
height = crt_info->height;
|
||||
offset_x = crt_info->x;
|
||||
offset_y = crt_info->y;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
BOOST_LOG(warning) << "Couldn't get requested display info, defaulting to recording entire virtual desktop"sv;
|
||||
width = xattr.width;
|
||||
height = xattr.height;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
width = xattr.width;
|
||||
height = xattr.height;
|
||||
}
|
||||
|
|
@ -472,13 +452,11 @@ namespace platf {
|
|||
/**
|
||||
* Called when the display attributes should change.
|
||||
*/
|
||||
void
|
||||
refresh() {
|
||||
void refresh() {
|
||||
x11::GetWindowAttributes(xdisplay.get(), xwindow, &xattr); // Update xattr's
|
||||
}
|
||||
|
||||
capture_e
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_logger.reset();
|
||||
|
|
@ -523,8 +501,7 @@ namespace platf {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
refresh();
|
||||
|
||||
// The whole X server changed, so we must reinit everything
|
||||
|
|
@ -538,7 +515,7 @@ namespace platf {
|
|||
}
|
||||
auto img = (x11_img_t *) img_out.get();
|
||||
|
||||
XImage *x_img { x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) };
|
||||
XImage *x_img {x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap)};
|
||||
img->frame_timestamp = std::chrono::steady_clock::now();
|
||||
|
||||
img->width = x_img->width;
|
||||
|
|
@ -555,13 +532,11 @@ namespace platf {
|
|||
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::unique_ptr<avcodec_encode_device_t>
|
||||
make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
|
||||
std::unique_ptr<avcodec_encode_device_t> make_avcodec_encode_device(pix_fmt_e pix_fmt) override {
|
||||
#ifdef SUNSHINE_BUILD_VAAPI
|
||||
if (mem_type == mem_type_e::vaapi) {
|
||||
return va::make_avcodec_encode_device(width, height, false);
|
||||
|
|
@ -577,8 +552,7 @@ namespace platf {
|
|||
return std::make_unique<avcodec_encode_device_t>();
|
||||
}
|
||||
|
||||
int
|
||||
dummy_img(img_t *img) override {
|
||||
int dummy_img(img_t *img) override {
|
||||
// TODO: stop cheating and give black image
|
||||
if (!img) {
|
||||
return -1;
|
||||
|
|
@ -605,15 +579,15 @@ namespace platf {
|
|||
|
||||
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) } {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -621,8 +595,7 @@ namespace platf {
|
|||
while (!task_pool.cancel(refresh_task_id));
|
||||
}
|
||||
|
||||
capture_e
|
||||
capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
|
||||
auto next_frame = std::chrono::steady_clock::now();
|
||||
|
||||
sleep_overshoot_logger.reset();
|
||||
|
|
@ -667,18 +640,16 @@ namespace platf {
|
|||
return capture_e::ok;
|
||||
}
|
||||
|
||||
capture_e
|
||||
snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr<platf::img_t> &img_out, std::chrono::milliseconds timeout, bool cursor) {
|
||||
// The whole X server changed, so we must reinit everything
|
||||
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;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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);
|
||||
auto frame_timestamp = std::chrono::steady_clock::now();
|
||||
|
||||
xcb_img_t img_reply { xcb::shm_get_image_reply(xcb.get(), img_cookie, nullptr) };
|
||||
xcb_img_t img_reply {xcb::shm_get_image_reply(xcb.get(), img_cookie, nullptr)};
|
||||
if (!img_reply) {
|
||||
BOOST_LOG(error) << "Could not get image reply"sv;
|
||||
return capture_e::reinit;
|
||||
|
|
@ -699,8 +670,7 @@ namespace platf {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -711,13 +681,11 @@ namespace platf {
|
|||
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) {
|
||||
int init(const std::string &display_name, const ::video::config_t &config) {
|
||||
if (x11_attr_t::init(display_name, config)) {
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -756,14 +724,12 @@ namespace platf {
|
|||
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) {
|
||||
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;
|
||||
|
|
@ -797,8 +763,7 @@ namespace platf {
|
|||
return x11_disp;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
x11_display_names() {
|
||||
std::vector<std::string> x11_display_names() {
|
||||
if (load_x11() || load_xcb()) {
|
||||
BOOST_LOG(error) << "Couldn't init x11 libraries"sv;
|
||||
|
||||
|
|
@ -807,18 +772,18 @@ namespace platf {
|
|||
|
||||
BOOST_LOG(info) << "Detecting displays"sv;
|
||||
|
||||
x11::xdisplay_t xdisplay { x11::OpenDisplay(nullptr) };
|
||||
x11::xdisplay_t xdisplay {x11::OpenDisplay(nullptr)};
|
||||
if (!xdisplay) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto xwindow = DefaultRootWindow(xdisplay.get());
|
||||
screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) };
|
||||
screen_res_t screenr {x11::rr::GetScreenResources(xdisplay.get(), xwindow)};
|
||||
int output = screenr->noutput;
|
||||
|
||||
int monitor = 0;
|
||||
for (int x = 0; x < output; ++x) {
|
||||
output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) };
|
||||
output_info_t out_info {x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x])};
|
||||
if (out_info) {
|
||||
BOOST_LOG(info) << "Detected display: "sv << out_info->name << " (id: "sv << monitor << ")"sv << out_info->name << " connected: "sv << (out_info->connection == RR_Connected);
|
||||
++monitor;
|
||||
|
|
@ -835,25 +800,22 @@ namespace platf {
|
|||
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 ||
|
||||
|
|
@ -863,8 +825,7 @@ namespace platf {
|
|||
}
|
||||
|
||||
namespace x11 {
|
||||
std::optional<cursor_t>
|
||||
cursor_t::make() {
|
||||
std::optional<cursor_t> cursor_t::make() {
|
||||
if (load_x11()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -876,8 +837,7 @@ namespace platf {
|
|||
return cursor;
|
||||
}
|
||||
|
||||
void
|
||||
cursor_t::capture(egl::cursor_t &img) {
|
||||
void cursor_t::capture(egl::cursor_t &img) {
|
||||
auto display = (xdisplay_t::pointer) ctx.get();
|
||||
|
||||
xcursor_t xcursor = fix::GetCursorImage(display);
|
||||
|
|
@ -904,23 +864,19 @@ namespace platf {
|
|||
img.serial = xcursor->cursor_serial;
|
||||
}
|
||||
|
||||
void
|
||||
cursor_t::blend(img_t &img, int offsetX, int 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) {
|
||||
void freeCursorCtx(cursor_ctx_t::pointer ctx) {
|
||||
CloseDisplay((xdisplay_t::pointer) ctx);
|
||||
}
|
||||
} // namespace x11
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
// standard includes
|
||||
#include <optional>
|
||||
|
||||
// local includes
|
||||
#include "src/platform/common.h"
|
||||
#include "src/utility.h"
|
||||
|
||||
|
|
@ -18,21 +20,17 @@ namespace egl {
|
|||
|
||||
namespace platf::x11 {
|
||||
struct cursor_ctx_raw_t;
|
||||
void
|
||||
freeCursorCtx(cursor_ctx_raw_t *ctx);
|
||||
void
|
||||
freeDisplay(_XDisplay *xdisplay);
|
||||
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>;
|
||||
|
||||
class cursor_t {
|
||||
public:
|
||||
static std::optional<cursor_t>
|
||||
make();
|
||||
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
|
||||
|
|
@ -40,12 +38,10 @@ namespace platf::x11 {
|
|||
* 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();
|
||||
} // namespace platf::x11
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue