Screencast wlroots based compositors

This commit is contained in:
Loki 2021-08-25 16:09:42 +02:00
parent 05dcff4f87
commit ec184fb2ab
13 changed files with 972 additions and 54 deletions

3
.gitmodules vendored
View File

@ -10,6 +10,3 @@
[submodule "third-party/miniupnp"]
path = third-party/miniupnp
url = https://github.com/miniupnp/miniupnp
[submodule "third-party/wayland-protocols"]
path = third-party/wayland-protocols
url = https://github.com/wayland-project/wayland-protocols.git

View File

@ -131,12 +131,13 @@ else()
list(APPEND SUNSHINE_DEFINITIONS EGL_NO_X11=1)
endif()
if(WAYLAND_FOUND)
macro(genWayland FILEPATH FILENAME)
message("wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILEPATH}/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c")
message("wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILEPATH}/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h")
add_compile_definitions(SUNSHINE_BUILD_WAYLAND)
macro(genWayland FILENAME)
message("wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c")
message("wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h")
execute_process(
COMMAND wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILEPATH}/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c
COMMAND wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILEPATH}/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h
COMMAND wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c
COMMAND wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h
RESULT_VARIABLE EXIT_INT
)
@ -151,7 +152,8 @@ else()
)
endmacro()
genWayland(unstable/xdg-output xdg-output-unstable-v1)
genWayland(xdg-output-unstable-v1)
genWayland(wlr-export-dmabuf-unstable-v1)
include_directories(
${WAYLAND_INCLUDE_DIRS}
@ -159,10 +161,12 @@ else()
)
list(APPEND PLATFORM_LIBRARIES ${WAYLAND_LIBRARIES})
list(APPEND PLATFORM_TARGET_FILES sunshine/platform/linux/wayland.h)
list(APPEND PLATFORM_TARGET_FILES sunshine/platform/linux/wayland.cpp)
list(APPEND PLATFORM_TARGET_FILES
sunshine/platform/linux/wlgrab.cpp
sunshine/platform/linux/wayland.cpp
sunshine/platform/linux/wayland.h)
endif()
if(NOT X11_FOUND AND NOT LIBDRM_FOUND)
if(NOT ${X11_FOUND} AND NOT ${LIBDRM_FOUND} AND NOT ${WAYLAND_FOUND})
message(FATAL_ERROR "Couldn't find either x11 or libdrm")
endif()

View File

@ -26,8 +26,6 @@
#include "upnp.h"
#include "video.h"
#include "platform/linux/wayland.h"
#include "platform/common.h"
extern "C" {
#include <libavutil/log.h>
@ -241,9 +239,6 @@ int main(int argc, char *argv[]) {
shutdown_event->raise(true);
});
wl::test();
return 0;
proc::refresh(config::stream.file_apps);
auto deinit_guard = platf::init();

View File

@ -288,10 +288,23 @@ bool fail() {
return eglGetError() != EGL_SUCCESS;
}
display_t make_display(gbm::gbm_t::pointer gbm) {
constexpr auto EGL_PLATFORM_GBM_MESA = 0x31D7;
display_t make_display(util::Either<gbm::gbm_t::pointer, wl_display *> native_display) {
constexpr auto EGL_PLATFORM_GBM_MESA = 0x31D7;
constexpr auto EGL_PLATFORM_WAYLAND_KHR = 0x31D8;
display_t display = eglGetPlatformDisplay(EGL_PLATFORM_GBM_MESA, gbm, nullptr);
int egl_platform;
void *native_display_p;
if(native_display.has_left()) {
egl_platform = EGL_PLATFORM_GBM_MESA;
native_display_p = native_display.left();
}
else {
egl_platform = EGL_PLATFORM_WAYLAND_KHR;
native_display_p = native_display.right();
}
// native_display.left() equals native_display.right()
display_t display = eglGetPlatformDisplay(egl_platform, native_display_p, nullptr);
if(fail()) {
BOOST_LOG(error) << "Couldn't open EGL display: ["sv << util::hex(eglGetError()).to_string_view() << ']';
@ -316,7 +329,7 @@ display_t make_display(gbm::gbm_t::pointer gbm) {
"EGL_KHR_create_context",
"EGL_KHR_surfaceless_context",
"EGL_EXT_image_dma_buf_import",
"EGL_KHR_image_pixmap"
// "EGL_KHR_image_pixmap"
};
for(auto ext : extensions) {

View File

@ -209,7 +209,7 @@ struct surface_descriptor_t {
int pitch;
};
display_t make_display(gbm::gbm_t::pointer gbm);
display_t make_display(util::Either<gbm::gbm_t::pointer, wl_display *> native_display);
std::optional<ctx_t> make_ctx(display_t::pointer display);
std::optional<rgb_t> import_source(

View File

@ -295,7 +295,7 @@ public:
auto file = entry.path().filename();
auto filestring = file.generic_u8string();
if(std::string_view { filestring }.substr(0, 4) != "card"sv) {
if(filestring.size() < 4 || std::string_view { filestring }.substr(0, 4) != "card"sv) {
continue;
}
@ -464,7 +464,7 @@ public:
return 0;
}
capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) {
capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<img_t> img, bool *cursor) override {
auto next_frame = std::chrono::steady_clock::now();
while(img) {

View File

@ -139,6 +139,9 @@ std::string get_mac_address(const std::string_view &address) {
}
enum class source_e {
#ifdef SUNSHINE_BUILD_WAYLAND
WAYLAND,
#endif
#ifdef SUNSHINE_BUILD_DRM
KMS,
#endif
@ -148,6 +151,15 @@ enum class source_e {
};
static source_e source;
#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, int framerate);
bool verify_wl() {
return !wl_display_names().empty();
}
#endif
#ifdef SUNSHINE_BUILD_DRM
std::vector<std::string> kms_display_names();
std::shared_ptr<display_t> kms_display(mem_type_e hwdevice_type, const std::string &display_name, int framerate);
@ -168,6 +180,10 @@ bool verify_x11() {
std::vector<std::string> display_names() {
switch(source) {
#ifdef SUNSHINE_BUILD_WAYLAND
case source_e::WAYLAND:
return wl_display_names();
#endif
#ifdef SUNSHINE_BUILD_DRM
case source_e::KMS:
return kms_display_names();
@ -183,6 +199,10 @@ std::vector<std::string> display_names() {
std::shared_ptr<display_t> display(mem_type_e hwdevice_type, const std::string &display_name, int framerate) {
switch(source) {
#ifdef SUNSHINE_BUILD_WAYLAND
case source_e::WAYLAND:
return wl_display(hwdevice_type, display_name, framerate);
#endif
#ifdef SUNSHINE_BUILD_DRM
case source_e::KMS:
return kms_display(hwdevice_type, display_name, framerate);
@ -200,7 +220,13 @@ std::unique_ptr<deinit_t> init() {
// These are allowed to fail.
gbm::init();
va::init();
#ifdef SUNSHINE_BUILD_WAYLAND
if(verify_wl()) {
BOOST_LOG(info) << "Using Wayland for screencasting"sv;
source = source_e::WAYLAND;
goto found_source;
}
#endif
#ifdef SUNSHINE_BUILD_DRM
if(verify_kms()) {
BOOST_LOG(info) << "Using KMS for screencasting"sv;

View File

@ -3,8 +3,10 @@
#include <cstdlib>
#include "graphics.h"
#include "sunshine/main.h"
#include "sunshine/platform/common.h"
#include "sunshine/round_robin.h"
#include "sunshine/utility.h"
#include "wayland.h"
@ -17,24 +19,6 @@ using namespace std::literals;
#pragma GCC diagnostic ignored "-Wpedantic"
namespace wl {
void test() {
display_t display;
if(display.init()) {
return;
}
interface_t interface { display.registry() };
display.roundtrip();
for(auto &monitor : interface.monitors) {
monitor->listen(interface.output_manager);
}
display.roundtrip();
}
int display_t::init(const char *display_name) {
if(!display_name) {
display_name = std::getenv("WAYLAND_DISPLAY");
@ -72,31 +56,31 @@ inline void monitor_t::xdg_name(zxdg_output_v1 *, const char *name) {
BOOST_LOG(info) << "Name: "sv << this->name;
}
inline 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;
}
inline 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;
}
inline void monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) {
void monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) {
viewport.width = width;
viewport.height = height;
BOOST_LOG(info) << "Resolution: "sv << width << 'x' << height;
}
inline void monitor_t::xdg_done(zxdg_output_v1 *) {
void monitor_t::xdg_done(zxdg_output_v1 *) {
BOOST_LOG(info) << "All info about monitor ["sv << name << "] has been send"sv;
}
inline 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);
#define CLASS_CALL(x, y) x = (decltype(x))&y
@ -111,15 +95,17 @@ inline void monitor_t::listen(zxdg_output_manager_v1 *output_manager) {
zxdg_output_v1_add_listener(xdg_output, &listener, this);
}
inline interface_t::interface_t(wl_registry *registry)
interface_t::interface_t() noexcept
: output_manager { nullptr }, listener {
(decltype(wl_registry_listener::global))&interface_t::add_interface,
(decltype(wl_registry_listener::global_remove))&interface_t::del_interface,
} {
} {}
void interface_t::listen(wl_registry *registry) {
wl_registry_add_listener(registry, &listener, this);
}
inline 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)) {
@ -131,13 +117,149 @@ inline void interface_t::add_interface(wl_registry *registry, std::uint32_t id,
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)) {
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);
this->interface[WLR_EXPORT_DMABUF] = true;
}
}
inline 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 { REINIT }, frames {}, current_frame { &frames[0] }, listener {
(decltype(zwlr_export_dmabuf_frame_v1_listener::frame))&dmabuf_t::frame,
(decltype(zwlr_export_dmabuf_frame_v1_listener::object))&dmabuf_t::object,
(decltype(zwlr_export_dmabuf_frame_v1_listener::ready))&dmabuf_t::ready,
(decltype(zwlr_export_dmabuf_frame_v1_listener::cancel))&dmabuf_t::cancel,
} {
}
int dmabuf_t::init(wl_display *display_p) {
display = egl::make_display(display_p);
if(!display) {
return -1;
}
auto ctx_opt = egl::make_ctx(display.get());
if(!ctx_opt) {
return -1;
}
ctx = std::move(*ctx_opt);
status = READY;
return 0;
}
void dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) {
auto frame = zwlr_export_dmabuf_manager_v1_capture_output(dmabuf_manager, blend_cursor, output);
zwlr_export_dmabuf_frame_v1_add_listener(frame, &listener, this);
status = WAITING;
}
dmabuf_t::~dmabuf_t() {
for(auto &frame : frames) {
frame.destroy();
}
}
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 format,
std::uint32_t high, std::uint32_t low,
std::uint32_t obj_count) {
auto next_frame = get_next_frame();
next_frame->format = format;
next_frame->width = width;
next_frame->height = height;
next_frame->obj_count = obj_count;
next_frame->frame = frame;
}
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) {
auto next_frame = get_next_frame();
next_frame->fds[index] = fd;
next_frame->sizes[index] = size;
next_frame->strides[index] = stride;
next_frame->offsets[index] = offset;
next_frame->plane_indices[index] = plane_index;
}
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) {
auto next_frame = get_next_frame();
auto rgb_opt = egl::import_source(display.get(),
{
next_frame->fds[0],
(int)next_frame->width,
(int)next_frame->height,
(int)next_frame->offsets[0],
(int)next_frame->strides[0],
});
if(!rgb_opt) {
status = REINIT;
return;
}
next_frame->rgb = std::move(*rgb_opt);
current_frame->destroy();
current_frame = next_frame;
status = READY;
}
void dmabuf_t::cancel(
zwlr_export_dmabuf_frame_v1 *frame,
zwlr_export_dmabuf_frame_v1_cancel_reason reason) {
auto next_frame = get_next_frame();
next_frame->destroy();
status = REINIT;
}
void frame_t::destroy() {
if(frame) {
zwlr_export_dmabuf_frame_v1_destroy(frame);
}
for(auto x = 0; x < obj_count; ++x) {
close(fds[x]);
}
rgb = egl::rgb_t {};
frame = nullptr;
obj_count = 0;
}
} // namespace wl
#pragma GCC diagnostic pop

View File

@ -1,11 +1,96 @@
#ifndef SUNSHINE_WAYLAND_H
#define SUNSHINE_WAYLAND_H
#include <bitset>
#include <wlr-export-dmabuf-unstable-v1.h>
#include <xdg-output-unstable-v1.h>
#include "graphics.h"
namespace wl {
using display_internal_t = util::safe_ptr<wl_display, wl_display_disconnect>;
class frame_t {
public:
std::uint32_t format;
std::uint32_t width, height;
std::uint32_t obj_count;
std::uint32_t strides[4];
std::uint32_t sizes[4];
std::int32_t fds[4];
std::uint32_t offsets[4];
std::uint32_t plane_indices[4];
egl::rgb_t rgb;
zwlr_export_dmabuf_frame_v1 *frame;
void destroy();
};
class dmabuf_t {
public:
enum status_e {
WAITING,
READY,
REINIT,
};
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();
int init(wl_display *display);
void listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false);
~dmabuf_t();
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 format,
std::uint32_t high, std::uint32_t low,
std::uint32_t obj_count);
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);
void ready(
zwlr_export_dmabuf_frame_v1 *frame,
std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec);
void cancel(
zwlr_export_dmabuf_frame_v1 *frame,
zwlr_export_dmabuf_frame_v1_cancel_reason reason);
inline frame_t *get_next_frame() {
return current_frame == &frames[0] ? &frames[1] : &frames[0];
}
status_e status;
egl::display_t display;
egl::ctx_t ctx;
std::array<frame_t, 2> frames;
frame_t *current_frame;
zwlr_export_dmabuf_frame_v1_listener listener;
};
class monitor_t {
public:
monitor_t(monitor_t &&) = delete;
@ -41,21 +126,37 @@ class interface_t {
};
public:
enum interface_e {
XDG_OUTPUT,
WLR_EXPORT_DMABUF,
MAX_INTERFACES,
};
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(wl_registry *registry);
interface_t() noexcept;
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 {
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);
std::bitset<MAX_INTERFACES> interface;
wl_registry_listener listener;
};
@ -74,6 +175,10 @@ public:
// No need to manually free the registry
wl_registry *registry();
inline display_internal_t::pointer get() {
return display_internal.get();
}
private:
display_internal_t display_internal;
};

View File

@ -0,0 +1,234 @@
#include "sunshine/platform/common.h"
#include "sunshine/main.h"
#include "vaapi.h"
#include "wayland.h"
using namespace std::literals;
namespace wl {
static int env_width;
static int env_height;
struct img_t : public platf::img_t {
~img_t() override {
delete[] data;
data = nullptr;
}
};
class wlr_t : public platf::display_t {
public:
int init(platf::mem_type_e hwdevice_type, const std::string &display_name, int framerate) {
delay = std::chrono::nanoseconds { 1s } / framerate;
mem_type = hwdevice_type;
if(display.init() || dmabuf.init(display.get())) {
return -1;
}
interface.listen(display.registry());
display.roundtrip();
if(!interface[wl::interface_t::XDG_OUTPUT]) {
BOOST_LOG(error) << "Missing Wayland wire for xdg_output"sv;
return -1;
}
if(!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
BOOST_LOG(error) << "Missing Wayland wire for wlr-export-dmabuf"sv;
return -1;
}
auto monitor = interface.monitors[0].get();
if(!display_name.empty()) {
auto streamedMonitor = util::from_view(display_name);
if(streamedMonitor >= 0 && streamedMonitor < interface.monitors.size()) {
monitor = interface.monitors[streamedMonitor].get();
}
}
monitor->listen(interface.output_manager);
display.roundtrip();
output = monitor->output;
offset_x = monitor->viewport.offset_x;
offset_y = monitor->viewport.offset_y;
width = monitor->viewport.width;
height = monitor->viewport.height;
this->env_width = ::wl::env_width;
this->env_height = ::wl::env_height;
BOOST_LOG(info) << "Selected monitor ["sv << monitor->description << "] for streaming"sv;
BOOST_LOG(debug) << "Offset: "sv << offset_x << 'x' << offset_y;
BOOST_LOG(debug) << "Resolution: "sv << width << 'x' << height;
BOOST_LOG(debug) << "Desktop Resolution: "sv << env_width << 'x' << env_height;
return 0;
}
platf::capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<platf::img_t> img, bool *cursor) override {
auto next_frame = std::chrono::steady_clock::now();
while(img) {
auto now = std::chrono::steady_clock::now();
if(next_frame > now) {
std::this_thread::sleep_for((next_frame - now) / 3 * 2);
}
while(next_frame > now) {
now = std::chrono::steady_clock::now();
}
next_frame = now + delay;
auto status = snapshot(img.get(), 1000ms, *cursor);
switch(status) {
case platf::capture_e::reinit:
case platf::capture_e::error:
return status;
case platf::capture_e::timeout:
continue;
case platf::capture_e::ok:
img = snapshot_cb(img);
break;
default:
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']';
return status;
}
}
return platf::capture_e::ok;
}
platf::capture_e snapshot(platf::img_t *img_out_base, std::chrono::milliseconds timeout, bool cursor) {
auto to = std::chrono::steady_clock::now() + timeout;
dmabuf.listen(interface.dmabuf_manager, output, cursor);
do {
display.roundtrip();
if(to < std::chrono::steady_clock::now()) {
return platf::capture_e::timeout;
}
} while(dmabuf.status == dmabuf_t::WAITING);
auto current_frame = dmabuf.current_frame;
if(
dmabuf.status == dmabuf_t::REINIT ||
current_frame->width != width ||
current_frame->height != height) {
return platf::capture_e::reinit;
}
auto &rgb = current_frame->rgb;
gl::ctx.BindTexture(GL_TEXTURE_2D, rgb->tex[0]);
gl::ctx.GetTextureSubImage(rgb->tex[0], 0, 0, 0, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out_base->height * img_out_base->row_pitch, img_out_base->data);
gl::ctx.BindTexture(GL_TEXTURE_2D, 0);
return platf::capture_e::ok;
}
std::shared_ptr<platf::img_t> alloc_img() override {
auto img = std::make_shared<img_t>();
img->width = width;
img->height = height;
img->pixel_pitch = 4;
img->row_pitch = img->pixel_pitch * width;
img->data = new std::uint8_t[height * img->row_pitch];
return img;
}
int dummy_img(platf::img_t *img) override {
return 0;
}
std::shared_ptr<platf::hwdevice_t> make_hwdevice(platf::pix_fmt_e pix_fmt) override {
if(mem_type == platf::mem_type_e::vaapi) {
return va::make_hwdevice(width, height);
}
return std::make_shared<platf::hwdevice_t>();
}
platf::mem_type_e mem_type;
std::chrono::nanoseconds delay;
wl::display_t display;
interface_t interface;
dmabuf_t dmabuf;
wl_output *output;
};
} // namespace wl
namespace platf {
std::shared_ptr<display_t> wl_display(mem_type_e hwdevice_type, const std::string &display_name, int framerate) {
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;
}
auto wlr = std::make_shared<wl::wlr_t>();
if(wlr->init(hwdevice_type, display_name, framerate)) {
return nullptr;
}
return wlr;
}
std::vector<std::string> wl_display_names() {
std::vector<std::string> display_names;
wl::display_t display;
if(display.init()) {
return {};
}
wl::interface_t interface;
interface.listen(display.registry());
display.roundtrip();
if(!interface[wl::interface_t::XDG_OUTPUT]) {
BOOST_LOG(warning) << "Missing Wayland wire for xdg_output"sv;
return {};
}
if(!interface[wl::interface_t::WLR_EXPORT_DMABUF]) {
BOOST_LOG(warning) << "Missing Wayland wire for wlr-export-dmabuf"sv;
return {};
}
wl::env_width = 0;
wl::env_height = 0;
for(auto &monitor : interface.monitors) {
monitor->listen(interface.output_manager);
}
display.roundtrip();
for(int x = 0; x < interface.monitors.size(); ++x) {
auto monitor = interface.monitors[x].get();
wl::env_width = std::max(wl::env_width, (int)(monitor->viewport.offset_x + monitor->viewport.width));
wl::env_height = std::max(wl::env_height, (int)(monitor->viewport.offset_y + monitor->viewport.height));
display_names.emplace_back(std::to_string(x));
}
return display_names;
}
} // namespace platf

@ -1 +0,0 @@
Subproject commit 7dffa6f346ae32f7888177d74a45459997059767

View File

@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_export_dmabuf_unstable_v1">
<copyright>
Copyright © 2018 Rostislav Pehlivanov
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="a protocol for low overhead screen content capturing">
An interface to capture surfaces in an efficient way by exporting DMA-BUFs.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwlr_export_dmabuf_manager_v1" version="1">
<description summary="manager to inform clients and begin capturing">
This object is a manager with which to start capturing from sources.
</description>
<request name="capture_output">
<description summary="capture a frame from an output">
Capture the next frame of a an entire output.
</description>
<arg name="frame" type="new_id" interface="zwlr_export_dmabuf_frame_v1"/>
<arg name="overlay_cursor" type="int"
summary="include custom client hardware cursor on top of the frame"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
</description>
</request>
</interface>
<interface name="zwlr_export_dmabuf_frame_v1" version="1">
<description summary="a DMA-BUF frame">
This object represents a single DMA-BUF frame.
If the capture is successful, the compositor will first send a "frame"
event, followed by one or several "object". When the frame is available
for readout, the "ready" event is sent.
If the capture failed, the "cancel" event is sent. This can happen anytime
before the "ready" event.
Once either a "ready" or a "cancel" event is received, the client should
destroy the frame. Once an "object" event is received, the client is
responsible for closing the associated file descriptor.
All frames are read-only and may not be written into or altered.
</description>
<enum name="flags">
<description summary="frame flags">
Special flags that should be respected by the client.
</description>
<entry name="transient" value="0x1"
summary="clients should copy frame before processing"/>
</enum>
<event name="frame">
<description summary="a frame description">
Main event supplying the client with information about the frame. If the
capture didn't fail, this event is always emitted first before any other
events.
This event is followed by a number of "object" as specified by the
"num_objects" argument.
</description>
<arg name="width" type="uint"
summary="frame width in pixels"/>
<arg name="height" type="uint"
summary="frame height in pixels"/>
<arg name="offset_x" type="uint"
summary="crop offset for the x axis"/>
<arg name="offset_y" type="uint"
summary="crop offset for the y axis"/>
<arg name="buffer_flags" type="uint"
summary="flags which indicate properties (invert, interlacing),
has the same values as zwp_linux_buffer_params_v1:flags"/>
<arg name="flags" type="uint" enum="flags"
summary="indicates special frame features"/>
<arg name="format" type="uint"
summary="format of the frame (DRM_FORMAT_*)"/>
<arg name="mod_high" type="uint"
summary="drm format modifier, high"/>
<arg name="mod_low" type="uint"
summary="drm format modifier, low"/>
<arg name="num_objects" type="uint"
summary="indicates how many objects (FDs) the frame has (max 4)"/>
</event>
<event name="object">
<description summary="an object description">
Event which serves to supply the client with the file descriptors
containing the data for each object.
After receiving this event, the client must always close the file
descriptor as soon as they're done with it and even if the frame fails.
</description>
<arg name="index" type="uint"
summary="index of the current object"/>
<arg name="fd" type="fd"
summary="fd of the current object"/>
<arg name="size" type="uint"
summary="size in bytes for the current object"/>
<arg name="offset" type="uint"
summary="starting point for the data in the object's fd"/>
<arg name="stride" type="uint"
summary="line size in bytes"/>
<arg name="plane_index" type="uint"
summary="index of the the plane the data in the object applies to"/>
</event>
<event name="ready">
<description summary="indicates frame is available for reading">
This event is sent as soon as the frame is presented, indicating it is
available for reading. This event includes the time at which
presentation happened at.
The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
each component being an unsigned 32-bit value. Whole seconds are in
tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo,
and the additional fractional part in tv_nsec as nanoseconds. Hence,
for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part
may have an arbitrary offset at start.
After receiving this event, the client should destroy this object.
</description>
<arg name="tv_sec_hi" type="uint"
summary="high 32 bits of the seconds part of the timestamp"/>
<arg name="tv_sec_lo" type="uint"
summary="low 32 bits of the seconds part of the timestamp"/>
<arg name="tv_nsec" type="uint"
summary="nanoseconds part of the timestamp"/>
</event>
<enum name="cancel_reason">
<description summary="cancel reason">
Indicates reason for cancelling the frame.
</description>
<entry name="temporary" value="0"
summary="temporary error, source will produce more frames"/>
<entry name="permanent" value="1"
summary="fatal error, source will not produce frames"/>
<entry name="resizing" value="2"
summary="temporary error, source will produce more frames"/>
</enum>
<event name="cancel">
<description summary="indicates the frame is no longer valid">
If the capture failed or if the frame is no longer valid after the
"frame" event has been emitted, this event will be used to inform the
client to scrap the frame.
If the failure is temporary, the client may capture again the same
source. If the failure is permanent, any further attempts to capture the
same source will fail again.
After receiving this event, the client should destroy this object.
</description>
<arg name="reason" type="uint" enum="cancel_reason"
summary="indicates a reason for cancelling this frame capture"/>
</event>
<request name="destroy" type="destructor">
<description summary="delete this object, used or not">
Unreferences the frame. This request must be called as soon as its no
longer used.
It can be called at any time by the client. The client will still have
to close any FDs it has been given.
</description>
</request>
</interface>
</protocol>

View File

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_output_unstable_v1">
<copyright>
Copyright © 2017 Red Hat Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="Protocol to describe output regions">
This protocol aims at describing outputs in a way which is more in line
with the concept of an output on desktop oriented systems.
Some information are more specific to the concept of an output for
a desktop oriented system and may not make sense in other applications,
such as IVI systems for example.
Typically, the global compositor space on a desktop system is made of
a contiguous or overlapping set of rectangular regions.
Some of the information provided in this protocol might be identical
to their counterparts already available from wl_output, in which case
the information provided by this protocol should be preferred to their
equivalent in wl_output. The goal is to move the desktop specific
concepts (such as output location within the global compositor space,
the connector name and types, etc.) out of the core wl_output protocol.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible
changes may be added together with the corresponding interface
version bump.
Backward incompatible changes are done by bumping the version
number in the protocol and interface names and resetting the
interface version. Once the protocol is to be declared stable,
the 'z' prefix and the version number in the protocol and
interface names are removed and the interface version number is
reset.
</description>
<interface name="zxdg_output_manager_v1" version="3">
<description summary="manage xdg_output objects">
A global factory interface for xdg_output objects.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the xdg_output_manager object">
Using this request a client can tell the server that it is not
going to use the xdg_output_manager object anymore.
Any objects already created through this instance are not affected.
</description>
</request>
<request name="get_xdg_output">
<description summary="create an xdg output from a wl_output">
This creates a new xdg_output object for the given wl_output.
</description>
<arg name="id" type="new_id" interface="zxdg_output_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
</interface>
<interface name="zxdg_output_v1" version="3">
<description summary="compositor logical output region">
An xdg_output describes part of the compositor geometry.
This typically corresponds to a monitor that displays part of the
compositor space.
For objects version 3 onwards, after all xdg_output properties have been
sent (when the object is created and when properties are updated), a
wl_output.done event is sent. This allows changes to the output
properties to be seen as atomic, even if they happen via multiple events.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the xdg_output object">
Using this request a client can tell the server that it is not
going to use the xdg_output object anymore.
</description>
</request>
<event name="logical_position">
<description summary="position of the output within the global compositor space">
The position event describes the location of the wl_output within
the global compositor space.
The logical_position event is sent after creating an xdg_output
(see xdg_output_manager.get_xdg_output) and whenever the location
of the output changes within the global compositor space.
</description>
<arg name="x" type="int"
summary="x position within the global compositor space"/>
<arg name="y" type="int"
summary="y position within the global compositor space"/>
</event>
<event name="logical_size">
<description summary="size of the output in the global compositor space">
The logical_size event describes the size of the output in the
global compositor space.
For example, a surface without any buffer scale, transformation
nor rotation set, with the size matching the logical_size will
have the same size as the corresponding output when displayed.
Most regular Wayland clients should not pay attention to the
logical size and would rather rely on xdg_shell interfaces.
Some clients such as Xwayland, however, need this to configure
their surfaces in the global compositor space as the compositor
may apply a different scale from what is advertised by the output
scaling property (to achieve fractional scaling, for example).
For example, for a wl_output mode 3840×2160 and a scale factor 2:
- A compositor not scaling the surface buffers will advertise a
logical size of 3840×2160,
- A compositor automatically scaling the surface buffers will
advertise a logical size of 1920×1080,
- A compositor using a fractional scale of 1.5 will advertise a
logical size of 2560×1440.
For example, for a wl_output mode 1920×1080 and a 90 degree rotation,
the compositor will advertise a logical size of 1080x1920.
The logical_size event is sent after creating an xdg_output
(see xdg_output_manager.get_xdg_output) and whenever the logical
size of the output changes, either as a result of a change in the
applied scale or because of a change in the corresponding output
mode(see wl_output.mode) or transform (see wl_output.transform).
</description>
<arg name="width" type="int"
summary="width in global compositor space"/>
<arg name="height" type="int"
summary="height in global compositor space"/>
</event>
<event name="done">
<description summary="all information about the output have been sent">
This event is sent after all other properties of an xdg_output
have been sent.
This allows changes to the xdg_output properties to be seen as
atomic, even if they happen via multiple events.
For objects version 3 onwards, this event is deprecated. Compositors
are not required to send it anymore and must send wl_output.done
instead.
</description>
</event>
<!-- Version 2 additions -->
<event name="name" since="2">
<description summary="name of this output">
Many compositors will assign names to their outputs, show them to the
user, allow them to be configured by name, etc. The client may wish to
know this name as well to offer the user similar behaviors.
The naming convention is compositor defined, but limited to
alphanumeric characters and dashes (-). Each name is unique among all
wl_output globals, but if a wl_output global is destroyed the same name
may be reused later. The names will also remain consistent across
sessions with the same hardware and software configuration.
Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
not assume that the name is a reflection of an underlying DRM
connector, X11 connection, etc.
The name event is sent after creating an xdg_output (see
xdg_output_manager.get_xdg_output). This event is only sent once per
xdg_output, and the name does not change over the lifetime of the
wl_output global.
</description>
<arg name="name" type="string" summary="output name"/>
</event>
<event name="description" since="2">
<description summary="human-readable description of this output">
Many compositors can produce human-readable descriptions of their
outputs. The client may wish to know this description as well, to
communicate the user for various purposes.
The description is a UTF-8 string with no convention defined for its
contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11
output via :1'.
The description event is sent after creating an xdg_output (see
xdg_output_manager.get_xdg_output) and whenever the description
changes. The description is optional, and may not be sent at all.
For objects of version 2 and lower, this event is only sent once per
xdg_output, and the description does not change over the lifetime of
the wl_output global.
</description>
<arg name="description" type="string" summary="output description"/>
</event>
</interface>
</protocol>