fix(linux): headless monitors on wayland (#3783)
This commit is contained in:
parent
86d7663927
commit
b43a9b8efb
7 changed files with 359 additions and 130 deletions
|
|
@ -137,7 +137,8 @@ if(WAYLAND_FOUND)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
GEN_WAYLAND("${WAYLAND_PROTOCOLS_DIR}" "unstable/xdg-output" xdg-output-unstable-v1)
|
GEN_WAYLAND("${WAYLAND_PROTOCOLS_DIR}" "unstable/xdg-output" xdg-output-unstable-v1)
|
||||||
GEN_WAYLAND("${CMAKE_SOURCE_DIR}/third-party/wlr-protocols" "unstable" wlr-export-dmabuf-unstable-v1)
|
GEN_WAYLAND("${WAYLAND_PROTOCOLS_DIR}" "unstable/linux-dmabuf" linux-dmabuf-unstable-v1)
|
||||||
|
GEN_WAYLAND("${CMAKE_SOURCE_DIR}/third-party/wlr-protocols" "unstable" wlr-screencopy-unstable-v1)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
SYSTEM
|
SYSTEM
|
||||||
|
|
@ -145,7 +146,7 @@ if(WAYLAND_FOUND)
|
||||||
${CMAKE_BINARY_DIR}/generated-src
|
${CMAKE_BINARY_DIR}/generated-src
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND PLATFORM_LIBRARIES ${WAYLAND_LIBRARIES})
|
list(APPEND PLATFORM_LIBRARIES ${WAYLAND_LIBRARIES} gbm)
|
||||||
list(APPEND PLATFORM_TARGET_FILES
|
list(APPEND PLATFORM_TARGET_FILES
|
||||||
"${CMAKE_SOURCE_DIR}/src/platform/linux/wlgrab.cpp"
|
"${CMAKE_SOURCE_DIR}/src/platform/linux/wlgrab.cpp"
|
||||||
"${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.h"
|
"${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.h"
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ BuildRequires: libXrandr-devel
|
||||||
BuildRequires: libXtst-devel
|
BuildRequires: libXtst-devel
|
||||||
BuildRequires: git
|
BuildRequires: git
|
||||||
BuildRequires: mesa-libGL-devel
|
BuildRequires: mesa-libGL-devel
|
||||||
|
BuildRequires: mesa-libgbm-devel
|
||||||
BuildRequires: miniupnpc-devel
|
BuildRequires: miniupnpc-devel
|
||||||
BuildRequires: npm
|
BuildRequires: npm
|
||||||
BuildRequires: numactl-devel
|
BuildRequires: numactl-devel
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ class @PROJECT_NAME@ < Formula
|
||||||
depends_on "libxinerama"
|
depends_on "libxinerama"
|
||||||
depends_on "libxrandr"
|
depends_on "libxrandr"
|
||||||
depends_on "libxtst"
|
depends_on "libxtst"
|
||||||
|
depends_on "mesa"
|
||||||
depends_on "numactl"
|
depends_on "numactl"
|
||||||
depends_on "pulseaudio"
|
depends_on "pulseaudio"
|
||||||
depends_on "systemd"
|
depends_on "systemd"
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ function add_debain_based_deps() {
|
||||||
"libcurl4-openssl-dev"
|
"libcurl4-openssl-dev"
|
||||||
"libdrm-dev" # KMS
|
"libdrm-dev" # KMS
|
||||||
"libevdev-dev"
|
"libevdev-dev"
|
||||||
|
"libgbm-dev"
|
||||||
"libminiupnpc-dev"
|
"libminiupnpc-dev"
|
||||||
"libnotify-dev"
|
"libnotify-dev"
|
||||||
"libnuma-dev"
|
"libnuma-dev"
|
||||||
|
|
@ -175,6 +176,7 @@ function add_fedora_deps() {
|
||||||
"libXrandr-devel" # X11
|
"libXrandr-devel" # X11
|
||||||
"libXtst-devel" # X11
|
"libXtst-devel" # X11
|
||||||
"mesa-libGL-devel"
|
"mesa-libGL-devel"
|
||||||
|
"mesa-libgbm-devel"
|
||||||
"miniupnpc-devel"
|
"miniupnpc-devel"
|
||||||
"ninja-build"
|
"ninja-build"
|
||||||
"npm"
|
"npm"
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,14 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
// platform includes
|
// platform includes
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <gbm.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-util.h>
|
#include <wayland-util.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
|
||||||
// local includes
|
// local includes
|
||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
|
|
@ -37,6 +42,12 @@ namespace wl {
|
||||||
|
|
||||||
#define CLASS_CALL(c, m) classCall<c, decltype(&c::m), &c::m>
|
#define CLASS_CALL(c, m) classCall<c, decltype(&c::m), &c::m>
|
||||||
|
|
||||||
|
// Define buffer params listener
|
||||||
|
static const struct zwp_linux_buffer_params_v1_listener params_listener = {
|
||||||
|
.created = dmabuf_t::buffer_params_created,
|
||||||
|
.failed = dmabuf_t::buffer_params_failed
|
||||||
|
};
|
||||||
|
|
||||||
int display_t::init(const char *display_name) {
|
int display_t::init(const char *display_name) {
|
||||||
if (!display_name) {
|
if (!display_name) {
|
||||||
display_name = std::getenv("WAYLAND_DISPLAY");
|
display_name = std::getenv("WAYLAND_DISPLAY");
|
||||||
|
|
@ -136,7 +147,13 @@ namespace wl {
|
||||||
BOOST_LOG(info) << "Logical size: "sv << width << 'x' << 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.width = width;
|
||||||
viewport.height = height;
|
viewport.height = height;
|
||||||
|
|
||||||
|
|
@ -151,6 +168,8 @@ namespace wl {
|
||||||
|
|
||||||
interface_t::interface_t() noexcept
|
interface_t::interface_t() noexcept
|
||||||
:
|
:
|
||||||
|
screencopy_manager {nullptr},
|
||||||
|
dmabuf_interface {nullptr},
|
||||||
output_manager {nullptr},
|
output_manager {nullptr},
|
||||||
listener {
|
listener {
|
||||||
&CLASS_CALL(interface_t, add_interface),
|
&CLASS_CALL(interface_t, add_interface),
|
||||||
|
|
@ -162,7 +181,12 @@ namespace wl {
|
||||||
wl_registry_add_listener(registry, &listener, this);
|
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;
|
BOOST_LOG(debug) << "Available interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||||
|
|
||||||
if (!std::strcmp(interface, wl_output_interface.name)) {
|
if (!std::strcmp(interface, wl_output_interface.name)) {
|
||||||
|
|
@ -177,11 +201,16 @@ namespace wl {
|
||||||
output_manager = (zxdg_output_manager_v1 *) wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version);
|
output_manager = (zxdg_output_manager_v1 *) wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version);
|
||||||
|
|
||||||
this->interface[XDG_OUTPUT] = true;
|
this->interface[XDG_OUTPUT] = true;
|
||||||
} else if (!std::strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) {
|
} else if (!std::strcmp(interface, zwlr_screencopy_manager_v1_interface.name)) {
|
||||||
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
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);
|
screencopy_manager = (zwlr_screencopy_manager_v1 *) wl_registry_bind(registry, id, &zwlr_screencopy_manager_v1_interface, version);
|
||||||
|
|
||||||
this->interface[WLR_EXPORT_DMABUF] = true;
|
this->interface[WLR_EXPORT_DMABUF] = true;
|
||||||
|
} else if (!std::strcmp(interface, zwp_linux_dmabuf_v1_interface.name)) {
|
||||||
|
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
|
||||||
|
dmabuf_interface = (zwp_linux_dmabuf_v1 *) wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, version);
|
||||||
|
|
||||||
|
this->interface[LINUX_DMABUF] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,94 +218,306 @@ namespace wl {
|
||||||
BOOST_LOG(info) << "Delete: "sv << id;
|
BOOST_LOG(info) << "Delete: "sv << id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize GBM
|
||||||
|
bool dmabuf_t::init_gbm() {
|
||||||
|
if (gbm_device) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find render node
|
||||||
|
drmDevice *devices[16];
|
||||||
|
int n = drmGetDevices2(0, devices, 16);
|
||||||
|
if (n <= 0) {
|
||||||
|
BOOST_LOG(error) << "No DRM devices found"sv;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int drm_fd = -1;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (devices[i]->available_nodes & (1 << DRM_NODE_RENDER)) {
|
||||||
|
drm_fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDWR);
|
||||||
|
if (drm_fd >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drmFreeDevices(devices, n);
|
||||||
|
|
||||||
|
if (drm_fd < 0) {
|
||||||
|
BOOST_LOG(error) << "Failed to open DRM render node"sv;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gbm_device = gbm_create_device(drm_fd);
|
||||||
|
if (!gbm_device) {
|
||||||
|
close(drm_fd);
|
||||||
|
BOOST_LOG(error) << "Failed to create GBM device"sv;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup GBM
|
||||||
|
void dmabuf_t::cleanup_gbm() {
|
||||||
|
if (current_bo) {
|
||||||
|
gbm_bo_destroy(current_bo);
|
||||||
|
current_bo = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_wl_buffer) {
|
||||||
|
wl_buffer_destroy(current_wl_buffer);
|
||||||
|
current_wl_buffer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dmabuf_t::dmabuf_t():
|
dmabuf_t::dmabuf_t():
|
||||||
status {READY},
|
status {READY},
|
||||||
frames {},
|
frames {},
|
||||||
current_frame {&frames[0]},
|
current_frame {&frames[0]},
|
||||||
listener {
|
listener {
|
||||||
&CLASS_CALL(dmabuf_t, frame),
|
&CLASS_CALL(dmabuf_t, buffer),
|
||||||
&CLASS_CALL(dmabuf_t, object),
|
&CLASS_CALL(dmabuf_t, flags),
|
||||||
&CLASS_CALL(dmabuf_t, ready),
|
&CLASS_CALL(dmabuf_t, ready),
|
||||||
&CLASS_CALL(dmabuf_t, cancel)
|
&CLASS_CALL(dmabuf_t, failed),
|
||||||
|
&CLASS_CALL(dmabuf_t, damage),
|
||||||
|
&CLASS_CALL(dmabuf_t, linux_dmabuf),
|
||||||
|
&CLASS_CALL(dmabuf_t, buffer_done),
|
||||||
} {
|
} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) {
|
// Start capture
|
||||||
auto frame = zwlr_export_dmabuf_manager_v1_capture_output(dmabuf_manager, blend_cursor, output);
|
void dmabuf_t::listen(
|
||||||
zwlr_export_dmabuf_frame_v1_add_listener(frame, &listener, this);
|
zwlr_screencopy_manager_v1 *screencopy_manager,
|
||||||
|
zwp_linux_dmabuf_v1 *dmabuf_interface,
|
||||||
|
wl_output *output,
|
||||||
|
bool blend_cursor
|
||||||
|
) {
|
||||||
|
this->dmabuf_interface = dmabuf_interface;
|
||||||
|
// Reset state
|
||||||
|
shm_info.supported = false;
|
||||||
|
dmabuf_info.supported = false;
|
||||||
|
|
||||||
|
// Create new frame
|
||||||
|
auto frame = zwlr_screencopy_manager_v1_capture_output(
|
||||||
|
screencopy_manager,
|
||||||
|
blend_cursor ? 1 : 0,
|
||||||
|
output
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store frame data pointer for callbacks
|
||||||
|
zwlr_screencopy_frame_v1_set_user_data(frame, this);
|
||||||
|
|
||||||
|
// Add listener
|
||||||
|
zwlr_screencopy_frame_v1_add_listener(frame, &listener, this);
|
||||||
|
|
||||||
status = WAITING;
|
status = WAITING;
|
||||||
}
|
}
|
||||||
|
|
||||||
dmabuf_t::~dmabuf_t() {
|
dmabuf_t::~dmabuf_t() {
|
||||||
|
cleanup_gbm();
|
||||||
|
|
||||||
for (auto &frame : frames) {
|
for (auto &frame : frames) {
|
||||||
frame.destroy();
|
frame.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gbm_device) {
|
||||||
|
// We should close the DRM FD, but it's owned by GBM
|
||||||
|
gbm_device_destroy(gbm_device);
|
||||||
|
gbm_device = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dmabuf_t::frame(
|
// Buffer format callback
|
||||||
zwlr_export_dmabuf_frame_v1 *frame,
|
void dmabuf_t::buffer(
|
||||||
std::uint32_t width,
|
zwlr_screencopy_frame_v1 *frame,
|
||||||
std::uint32_t height,
|
uint32_t format,
|
||||||
std::uint32_t x,
|
uint32_t width,
|
||||||
std::uint32_t y,
|
uint32_t height,
|
||||||
std::uint32_t buffer_flags,
|
uint32_t stride
|
||||||
std::uint32_t flags,
|
) {
|
||||||
|
shm_info.supported = true;
|
||||||
|
shm_info.format = format;
|
||||||
|
shm_info.width = width;
|
||||||
|
shm_info.height = height;
|
||||||
|
shm_info.stride = stride;
|
||||||
|
|
||||||
|
BOOST_LOG(debug) << "Screencopy supports SHM format: "sv << format;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DMA-BUF format callback
|
||||||
|
void dmabuf_t::linux_dmabuf(
|
||||||
|
zwlr_screencopy_frame_v1 *frame,
|
||||||
std::uint32_t format,
|
std::uint32_t format,
|
||||||
std::uint32_t high,
|
std::uint32_t width,
|
||||||
std::uint32_t low,
|
std::uint32_t height
|
||||||
std::uint32_t obj_count
|
|
||||||
) {
|
) {
|
||||||
auto next_frame = get_next_frame();
|
dmabuf_info.supported = true;
|
||||||
|
dmabuf_info.format = format;
|
||||||
|
dmabuf_info.width = width;
|
||||||
|
dmabuf_info.height = height;
|
||||||
|
|
||||||
next_frame->sd.fourcc = format;
|
BOOST_LOG(debug) << "Screencopy supports DMA-BUF format: "sv << format;
|
||||||
next_frame->sd.width = width;
|
|
||||||
next_frame->sd.height = height;
|
|
||||||
next_frame->sd.modifier = (((std::uint64_t) high) << 32) | low;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dmabuf_t::object(
|
// Flags callback
|
||||||
zwlr_export_dmabuf_frame_v1 *frame,
|
void dmabuf_t::flags(zwlr_screencopy_frame_v1 *frame, std::uint32_t flags) {
|
||||||
std::uint32_t index,
|
y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
|
||||||
std::int32_t fd,
|
BOOST_LOG(debug) << "Frame flags: "sv << flags << (y_invert ? " (y_invert)" : "");
|
||||||
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->sd.fds[plane_index] = fd;
|
|
||||||
next_frame->sd.pitches[plane_index] = stride;
|
|
||||||
next_frame->sd.offsets[plane_index] = offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DMA-BUF creation helper
|
||||||
|
void dmabuf_t::create_and_copy_dmabuf(zwlr_screencopy_frame_v1 *frame) {
|
||||||
|
if (!init_gbm()) {
|
||||||
|
BOOST_LOG(error) << "Failed to initialize GBM"sv;
|
||||||
|
zwlr_screencopy_frame_v1_destroy(frame);
|
||||||
|
status = REINIT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create GBM buffer
|
||||||
|
current_bo = gbm_bo_create(gbm_device, dmabuf_info.width, dmabuf_info.height, dmabuf_info.format, GBM_BO_USE_RENDERING);
|
||||||
|
if (!current_bo) {
|
||||||
|
BOOST_LOG(error) << "Failed to create GBM buffer"sv;
|
||||||
|
zwlr_screencopy_frame_v1_destroy(frame);
|
||||||
|
status = REINIT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get buffer info
|
||||||
|
int fd = gbm_bo_get_fd(current_bo);
|
||||||
|
if (fd < 0) {
|
||||||
|
BOOST_LOG(error) << "Failed to get buffer FD"sv;
|
||||||
|
gbm_bo_destroy(current_bo);
|
||||||
|
current_bo = nullptr;
|
||||||
|
zwlr_screencopy_frame_v1_destroy(frame);
|
||||||
|
status = REINIT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t stride = gbm_bo_get_stride(current_bo);
|
||||||
|
uint64_t modifier = gbm_bo_get_modifier(current_bo);
|
||||||
|
|
||||||
|
// Store in surface descriptor for later use
|
||||||
|
auto next_frame = get_next_frame();
|
||||||
|
next_frame->sd.fds[0] = fd;
|
||||||
|
next_frame->sd.pitches[0] = stride;
|
||||||
|
next_frame->sd.offsets[0] = 0;
|
||||||
|
next_frame->sd.modifier = modifier;
|
||||||
|
|
||||||
|
// Create linux-dmabuf buffer
|
||||||
|
auto params = zwp_linux_dmabuf_v1_create_params(dmabuf_interface);
|
||||||
|
zwp_linux_buffer_params_v1_add(params, fd, 0, 0, stride, modifier >> 32, modifier & 0xffffffff);
|
||||||
|
|
||||||
|
// Add listener for buffer creation
|
||||||
|
zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, frame);
|
||||||
|
|
||||||
|
// Create Wayland buffer (async - callback will handle copy)
|
||||||
|
zwp_linux_buffer_params_v1_create(params, dmabuf_info.width, dmabuf_info.height, dmabuf_info.format, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer done callback - time to create buffer
|
||||||
|
void dmabuf_t::buffer_done(zwlr_screencopy_frame_v1 *frame) {
|
||||||
|
auto next_frame = get_next_frame();
|
||||||
|
|
||||||
|
// Prefer DMA-BUF if supported
|
||||||
|
if (dmabuf_info.supported && dmabuf_interface) {
|
||||||
|
// Store format info first
|
||||||
|
next_frame->sd.fourcc = dmabuf_info.format;
|
||||||
|
next_frame->sd.width = dmabuf_info.width;
|
||||||
|
next_frame->sd.height = dmabuf_info.height;
|
||||||
|
|
||||||
|
// Create and start copy
|
||||||
|
create_and_copy_dmabuf(frame);
|
||||||
|
} else if (shm_info.supported) {
|
||||||
|
// SHM fallback would go here
|
||||||
|
BOOST_LOG(warning) << "SHM capture not implemented"sv;
|
||||||
|
zwlr_screencopy_frame_v1_destroy(frame);
|
||||||
|
status = REINIT;
|
||||||
|
} else {
|
||||||
|
BOOST_LOG(error) << "No supported buffer types"sv;
|
||||||
|
zwlr_screencopy_frame_v1_destroy(frame);
|
||||||
|
status = REINIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer params created callback
|
||||||
|
void dmabuf_t::buffer_params_created(
|
||||||
|
void *data,
|
||||||
|
struct zwp_linux_buffer_params_v1 *params,
|
||||||
|
struct wl_buffer *buffer
|
||||||
|
) {
|
||||||
|
auto frame = static_cast<zwlr_screencopy_frame_v1 *>(data);
|
||||||
|
auto self = static_cast<dmabuf_t *>(zwlr_screencopy_frame_v1_get_user_data(frame));
|
||||||
|
|
||||||
|
// Store for cleanup
|
||||||
|
self->current_wl_buffer = buffer;
|
||||||
|
|
||||||
|
// Start the actual copy
|
||||||
|
zwlr_screencopy_frame_v1_copy(frame, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer params failed callback
|
||||||
|
void dmabuf_t::buffer_params_failed(
|
||||||
|
void *data,
|
||||||
|
struct zwp_linux_buffer_params_v1 *params
|
||||||
|
) {
|
||||||
|
auto frame = static_cast<zwlr_screencopy_frame_v1 *>(data);
|
||||||
|
auto self = static_cast<dmabuf_t *>(zwlr_screencopy_frame_v1_get_user_data(frame));
|
||||||
|
|
||||||
|
BOOST_LOG(error) << "Failed to create buffer from params"sv;
|
||||||
|
self->cleanup_gbm();
|
||||||
|
|
||||||
|
zwlr_screencopy_frame_v1_destroy(frame);
|
||||||
|
self->status = REINIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ready callback
|
||||||
void dmabuf_t::ready(
|
void dmabuf_t::ready(
|
||||||
zwlr_export_dmabuf_frame_v1 *frame,
|
zwlr_screencopy_frame_v1 *frame,
|
||||||
std::uint32_t tv_sec_hi,
|
std::uint32_t tv_sec_hi,
|
||||||
std::uint32_t tv_sec_lo,
|
std::uint32_t tv_sec_lo,
|
||||||
std::uint32_t tv_nsec
|
std::uint32_t tv_nsec
|
||||||
) {
|
) {
|
||||||
zwlr_export_dmabuf_frame_v1_destroy(frame);
|
BOOST_LOG(debug) << "Frame ready"sv;
|
||||||
|
|
||||||
|
// Frame is ready for use, GBM buffer now contains screen content
|
||||||
current_frame->destroy();
|
current_frame->destroy();
|
||||||
current_frame = get_next_frame();
|
current_frame = get_next_frame();
|
||||||
|
|
||||||
|
// Keep the GBM buffer alive but destroy the Wayland objects
|
||||||
|
if (current_wl_buffer) {
|
||||||
|
wl_buffer_destroy(current_wl_buffer);
|
||||||
|
current_wl_buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_gbm();
|
||||||
|
|
||||||
|
zwlr_screencopy_frame_v1_destroy(frame);
|
||||||
status = READY;
|
status = READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dmabuf_t::cancel(
|
// Failed callback
|
||||||
zwlr_export_dmabuf_frame_v1 *frame,
|
void dmabuf_t::failed(zwlr_screencopy_frame_v1 *frame) {
|
||||||
std::uint32_t reason
|
BOOST_LOG(error) << "Frame capture failed"sv;
|
||||||
) {
|
|
||||||
zwlr_export_dmabuf_frame_v1_destroy(frame);
|
|
||||||
|
|
||||||
|
// Clean up resources
|
||||||
|
cleanup_gbm();
|
||||||
auto next_frame = get_next_frame();
|
auto next_frame = get_next_frame();
|
||||||
next_frame->destroy();
|
next_frame->destroy();
|
||||||
|
|
||||||
|
zwlr_screencopy_frame_v1_destroy(frame);
|
||||||
status = REINIT;
|
status = REINIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dmabuf_t::damage(
|
||||||
|
zwlr_screencopy_frame_v1 *frame,
|
||||||
|
std::uint32_t x,
|
||||||
|
std::uint32_t y,
|
||||||
|
std::uint32_t width,
|
||||||
|
std::uint32_t height
|
||||||
|
) {};
|
||||||
|
|
||||||
void frame_t::destroy() {
|
void frame_t::destroy() {
|
||||||
for (auto x = 0; x < 4; ++x) {
|
for (auto x = 0; x < 4; ++x) {
|
||||||
if (sd.fds[x] >= 0) {
|
if (sd.fds[x] >= 0) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
|
||||||
#ifdef SUNSHINE_BUILD_WAYLAND
|
#ifdef SUNSHINE_BUILD_WAYLAND
|
||||||
#include <wlr-export-dmabuf-unstable-v1.h>
|
#include <linux-dmabuf-unstable-v1.h>
|
||||||
|
#include <wlr-screencopy-unstable-v1.h>
|
||||||
#include <xdg-output-unstable-v1.h>
|
#include <xdg-output-unstable-v1.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -27,9 +28,9 @@ namespace wl {
|
||||||
class frame_t {
|
class frame_t {
|
||||||
public:
|
public:
|
||||||
frame_t();
|
frame_t();
|
||||||
egl::surface_descriptor_t sd;
|
|
||||||
|
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
|
egl::surface_descriptor_t sd;
|
||||||
};
|
};
|
||||||
|
|
||||||
class dmabuf_t {
|
class dmabuf_t {
|
||||||
|
|
@ -40,104 +41,91 @@ namespace wl {
|
||||||
REINIT, ///< Reinitialize the frame
|
REINIT, ///< Reinitialize the frame
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dmabuf_t();
|
||||||
|
~dmabuf_t();
|
||||||
|
|
||||||
dmabuf_t(dmabuf_t &&) = delete;
|
dmabuf_t(dmabuf_t &&) = delete;
|
||||||
dmabuf_t(const dmabuf_t &) = delete;
|
dmabuf_t(const dmabuf_t &) = delete;
|
||||||
|
|
||||||
dmabuf_t &operator=(const dmabuf_t &) = delete;
|
dmabuf_t &operator=(const dmabuf_t &) = delete;
|
||||||
dmabuf_t &operator=(dmabuf_t &&) = delete;
|
dmabuf_t &operator=(dmabuf_t &&) = delete;
|
||||||
|
|
||||||
dmabuf_t();
|
void listen(zwlr_screencopy_manager_v1 *screencopy_manager, zwp_linux_dmabuf_v1 *dmabuf_interface, wl_output *output, bool blend_cursor = false);
|
||||||
|
static void buffer_params_created(void *data, struct zwp_linux_buffer_params_v1 *params, struct wl_buffer *wl_buffer);
|
||||||
|
static void buffer_params_failed(void *data, struct zwp_linux_buffer_params_v1 *params);
|
||||||
|
void buffer(zwlr_screencopy_frame_v1 *frame, std::uint32_t format, std::uint32_t width, std::uint32_t height, std::uint32_t stride);
|
||||||
|
void linux_dmabuf(zwlr_screencopy_frame_v1 *frame, std::uint32_t format, std::uint32_t width, std::uint32_t height);
|
||||||
|
void buffer_done(zwlr_screencopy_frame_v1 *frame);
|
||||||
|
void flags(zwlr_screencopy_frame_v1 *frame, std::uint32_t flags);
|
||||||
|
void damage(zwlr_screencopy_frame_v1 *frame, std::uint32_t x, std::uint32_t y, std::uint32_t width, std::uint32_t height);
|
||||||
|
void ready(zwlr_screencopy_frame_v1 *frame, std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec);
|
||||||
|
void failed(zwlr_screencopy_frame_v1 *frame);
|
||||||
|
|
||||||
void listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false);
|
frame_t *get_next_frame() {
|
||||||
|
|
||||||
~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,
|
|
||||||
std::uint32_t reason
|
|
||||||
);
|
|
||||||
|
|
||||||
inline frame_t *get_next_frame() {
|
|
||||||
return current_frame == &frames[0] ? &frames[1] : &frames[0];
|
return current_frame == &frames[0] ? &frames[1] : &frames[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
status_e status;
|
status_e status;
|
||||||
|
|
||||||
std::array<frame_t, 2> frames;
|
std::array<frame_t, 2> frames;
|
||||||
frame_t *current_frame;
|
frame_t *current_frame;
|
||||||
|
zwlr_screencopy_frame_v1_listener listener;
|
||||||
|
|
||||||
zwlr_export_dmabuf_frame_v1_listener listener;
|
private:
|
||||||
|
bool init_gbm();
|
||||||
|
void cleanup_gbm();
|
||||||
|
void create_and_copy_dmabuf(zwlr_screencopy_frame_v1 *frame);
|
||||||
|
|
||||||
|
zwp_linux_dmabuf_v1 *dmabuf_interface {nullptr};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool supported {false};
|
||||||
|
std::uint32_t format;
|
||||||
|
std::uint32_t width;
|
||||||
|
std::uint32_t height;
|
||||||
|
std::uint32_t stride;
|
||||||
|
} shm_info;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool supported {false};
|
||||||
|
std::uint32_t format;
|
||||||
|
std::uint32_t width;
|
||||||
|
std::uint32_t height;
|
||||||
|
} dmabuf_info;
|
||||||
|
|
||||||
|
struct gbm_device *gbm_device {nullptr};
|
||||||
|
struct gbm_bo *current_bo {nullptr};
|
||||||
|
struct wl_buffer *current_wl_buffer {nullptr};
|
||||||
|
bool y_invert {false};
|
||||||
};
|
};
|
||||||
|
|
||||||
class monitor_t {
|
class monitor_t {
|
||||||
public:
|
public:
|
||||||
|
explicit monitor_t(wl_output *output);
|
||||||
|
|
||||||
monitor_t(monitor_t &&) = delete;
|
monitor_t(monitor_t &&) = delete;
|
||||||
monitor_t(const monitor_t &) = delete;
|
monitor_t(const monitor_t &) = delete;
|
||||||
|
|
||||||
monitor_t &operator=(const monitor_t &) = delete;
|
monitor_t &operator=(const monitor_t &) = delete;
|
||||||
monitor_t &operator=(monitor_t &&) = delete;
|
monitor_t &operator=(monitor_t &&) = delete;
|
||||||
|
|
||||||
monitor_t(wl_output *output);
|
void listen(zxdg_output_manager_v1 *output_manager);
|
||||||
|
|
||||||
void xdg_name(zxdg_output_v1 *, const char *name);
|
void xdg_name(zxdg_output_v1 *, const char *name);
|
||||||
void xdg_description(zxdg_output_v1 *, const char *description);
|
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_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_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height);
|
||||||
|
|
||||||
void xdg_done(zxdg_output_v1 *) {
|
void xdg_done(zxdg_output_v1 *) {}
|
||||||
}
|
|
||||||
|
|
||||||
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_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_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_done(wl_output *wl_output) {}
|
||||||
}
|
|
||||||
|
|
||||||
void wl_scale(wl_output *wl_output, std::int32_t factor) {
|
void wl_scale(wl_output *wl_output, std::int32_t factor) {}
|
||||||
}
|
|
||||||
|
|
||||||
void listen(zxdg_output_manager_v1 *output_manager);
|
|
||||||
|
|
||||||
wl_output *output;
|
wl_output *output;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
|
||||||
platf::touch_port_t viewport;
|
platf::touch_port_t viewport;
|
||||||
|
|
||||||
wl_output_listener wl_listener;
|
wl_output_listener wl_listener;
|
||||||
zxdg_output_v1_listener xdg_listener;
|
zxdg_output_v1_listener xdg_listener;
|
||||||
};
|
};
|
||||||
|
|
@ -151,35 +139,34 @@ namespace wl {
|
||||||
public:
|
public:
|
||||||
enum interface_e {
|
enum interface_e {
|
||||||
XDG_OUTPUT, ///< xdg-output
|
XDG_OUTPUT, ///< xdg-output
|
||||||
WLR_EXPORT_DMABUF, ///< Export dmabuf
|
WLR_EXPORT_DMABUF, ///< screencopy manager
|
||||||
|
LINUX_DMABUF, ///< linux-dmabuf protocol
|
||||||
MAX_INTERFACES, ///< Maximum number of interfaces
|
MAX_INTERFACES, ///< Maximum number of interfaces
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface_t() noexcept;
|
||||||
|
|
||||||
interface_t(interface_t &&) = delete;
|
interface_t(interface_t &&) = delete;
|
||||||
interface_t(const interface_t &) = delete;
|
interface_t(const interface_t &) = delete;
|
||||||
|
|
||||||
interface_t &operator=(const interface_t &) = delete;
|
interface_t &operator=(const interface_t &) = delete;
|
||||||
interface_t &operator=(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];
|
return interface[bit];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<monitor_t>> monitors;
|
||||||
|
zwlr_screencopy_manager_v1 *screencopy_manager {nullptr};
|
||||||
|
zwp_linux_dmabuf_v1 *dmabuf_interface {nullptr};
|
||||||
|
zxdg_output_manager_v1 *output_manager {nullptr};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version);
|
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 del_interface(wl_registry *registry, uint32_t id);
|
||||||
|
|
||||||
std::bitset<MAX_INTERFACES> interface;
|
std::bitset<MAX_INTERFACES> interface;
|
||||||
|
|
||||||
wl_registry_listener listener;
|
wl_registry_listener listener;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -212,7 +199,6 @@ namespace wl {
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
} // namespace wl
|
||||||
#else
|
#else
|
||||||
|
|
@ -223,21 +209,18 @@ struct zxdg_output_manager_v1;
|
||||||
namespace wl {
|
namespace wl {
|
||||||
class monitor_t {
|
class monitor_t {
|
||||||
public:
|
public:
|
||||||
|
monitor_t(wl_output *output);
|
||||||
|
|
||||||
monitor_t(monitor_t &&) = delete;
|
monitor_t(monitor_t &&) = delete;
|
||||||
monitor_t(const monitor_t &) = delete;
|
monitor_t(const monitor_t &) = delete;
|
||||||
|
|
||||||
monitor_t &operator=(const monitor_t &) = delete;
|
monitor_t &operator=(const monitor_t &) = delete;
|
||||||
monitor_t &operator=(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;
|
wl_output *output;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
|
||||||
platf::touch_port_t viewport;
|
platf::touch_port_t viewport;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ namespace wl {
|
||||||
auto to = std::chrono::steady_clock::now() + timeout;
|
auto to = std::chrono::steady_clock::now() + timeout;
|
||||||
|
|
||||||
// Dispatch events until we get a new frame or the timeout expires
|
// Dispatch events until we get a new frame or the timeout expires
|
||||||
dmabuf.listen(interface.dmabuf_manager, output, cursor);
|
dmabuf.listen(interface.screencopy_manager, interface.dmabuf_interface, output, cursor);
|
||||||
do {
|
do {
|
||||||
auto remaining_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(to - std::chrono::steady_clock::now());
|
auto remaining_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(to - std::chrono::steady_clock::now());
|
||||||
if (remaining_time_ms.count() < 0 || !display.dispatch(remaining_time_ms)) {
|
if (remaining_time_ms.count() < 0 || !display.dispatch(remaining_time_ms)) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue