Merge branch 'master' into cancel-grab-on-focus-change
This commit is contained in:
commit
e8c0996b93
15
.build.yml
15
.build.yml
|
@ -1,15 +1,16 @@
|
||||||
image: archlinux
|
image: archlinux
|
||||||
packages:
|
packages:
|
||||||
- meson
|
- clang
|
||||||
- wayland
|
- ffmpeg
|
||||||
- wayland-protocols
|
- libcap
|
||||||
- mesa
|
|
||||||
- libinput
|
- libinput
|
||||||
- libxkbcommon
|
- libxkbcommon
|
||||||
- xcb-util-image
|
- mesa
|
||||||
- libcap
|
- meson
|
||||||
- pixman
|
- pixman
|
||||||
- clang
|
- wayland
|
||||||
|
- wayland-protocols
|
||||||
|
- xcb-util-image
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/swaywm/wlroots
|
- https://github.com/swaywm/wlroots
|
||||||
tasks:
|
tasks:
|
||||||
|
|
|
@ -254,6 +254,25 @@ static uint32_t drm_connector_get_gamma_size(struct wlr_output *output) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool drm_connector_export_dmabuf(struct wlr_output *output,
|
||||||
|
struct wlr_dmabuf_attributes *attribs) {
|
||||||
|
struct wlr_drm_connector *conn = (struct wlr_drm_connector *)output;
|
||||||
|
struct wlr_drm_backend *drm = (struct wlr_drm_backend *)output->backend;
|
||||||
|
|
||||||
|
if (!drm->session->active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_drm_crtc *crtc = conn->crtc;
|
||||||
|
if (!crtc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct wlr_drm_plane *plane = crtc->primary;
|
||||||
|
struct wlr_drm_surface *surf = &plane->surf;
|
||||||
|
|
||||||
|
return export_drm_bo(surf->back, attribs);
|
||||||
|
}
|
||||||
|
|
||||||
static void drm_connector_start_renderer(struct wlr_drm_connector *conn) {
|
static void drm_connector_start_renderer(struct wlr_drm_connector *conn) {
|
||||||
if (conn->state != WLR_DRM_CONN_CONNECTED) {
|
if (conn->state != WLR_DRM_CONN_CONNECTED) {
|
||||||
return;
|
return;
|
||||||
|
@ -742,6 +761,7 @@ static const struct wlr_output_impl output_impl = {
|
||||||
.swap_buffers = drm_connector_swap_buffers,
|
.swap_buffers = drm_connector_swap_buffers,
|
||||||
.set_gamma = drm_connector_set_gamma,
|
.set_gamma = drm_connector_set_gamma,
|
||||||
.get_gamma_size = drm_connector_get_gamma_size,
|
.get_gamma_size = drm_connector_get_gamma_size,
|
||||||
|
.export_dmabuf = drm_connector_export_dmabuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wlr_output_is_drm(struct wlr_output *output) {
|
bool wlr_output_is_drm(struct wlr_output *output) {
|
||||||
|
|
|
@ -160,6 +160,34 @@ void post_drm_surface(struct wlr_drm_surface *surf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
|
||||||
|
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
|
||||||
|
|
||||||
|
attribs->n_planes = gbm_bo_get_plane_count(bo);
|
||||||
|
if (attribs->n_planes > WLR_DMABUF_MAX_PLANES) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
attribs->width = gbm_bo_get_width(bo);
|
||||||
|
attribs->height = gbm_bo_get_height(bo);
|
||||||
|
attribs->format = gbm_bo_get_format(bo);
|
||||||
|
attribs->modifier = gbm_bo_get_modifier(bo);
|
||||||
|
|
||||||
|
for (int i = 0; i < attribs->n_planes; ++i) {
|
||||||
|
attribs->offset[i] = gbm_bo_get_offset(bo, i);
|
||||||
|
attribs->stride[i] = gbm_bo_get_stride_for_plane(bo, i);
|
||||||
|
attribs->fd[i] = gbm_bo_get_fd(bo);
|
||||||
|
if (attribs->fd[i] < 0) {
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
close(attribs->fd[j]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct tex {
|
struct tex {
|
||||||
struct wlr_egl *egl;
|
struct wlr_egl *egl;
|
||||||
EGLImageKHR img;
|
EGLImageKHR img;
|
||||||
|
@ -186,16 +214,11 @@ static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_dmabuf_attributes attribs = {
|
struct wlr_dmabuf_attributes attribs;
|
||||||
.n_planes = 1,
|
if (!export_drm_bo(bo, &attribs)) {
|
||||||
.width = gbm_bo_get_width(bo),
|
free(tex);
|
||||||
.height = gbm_bo_get_height(bo),
|
return NULL;
|
||||||
.format = gbm_bo_get_format(bo),
|
}
|
||||||
.modifier = DRM_FORMAT_MOD_LINEAR,
|
|
||||||
};
|
|
||||||
attribs.offset[0] = 0;
|
|
||||||
attribs.stride[0] = gbm_bo_get_stride_for_plane(bo, 0);
|
|
||||||
attribs.fd[0] = gbm_bo_get_fd(bo);
|
|
||||||
|
|
||||||
tex->tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs);
|
tex->tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs);
|
||||||
if (tex->tex == NULL) {
|
if (tex->tex == NULL) {
|
||||||
|
|
|
@ -55,8 +55,8 @@ static bool backend_start(struct wlr_backend *_backend) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Let user customize seat used
|
if (libinput_udev_assign_seat(backend->libinput_context,
|
||||||
if (libinput_udev_assign_seat(backend->libinput_context, "seat0") != 0) {
|
backend->session->seat) != 0) {
|
||||||
wlr_log(L_ERROR, "Failed to assign libinput seat");
|
wlr_log(L_ERROR, "Failed to assign libinput seat");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ static void communicate(int sock) {
|
||||||
int drm_fd = -1;
|
int drm_fd = -1;
|
||||||
bool running = true;
|
bool running = true;
|
||||||
|
|
||||||
while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) >= 0) {
|
while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) > 0) {
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case MSG_OPEN:
|
case MSG_OPEN:
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/kd.h>
|
#include <linux/kd.h>
|
||||||
#include <linux/major.h>
|
#include <linux/major.h>
|
||||||
|
@ -76,15 +77,22 @@ static void direct_session_close(struct wlr_session *base, int fd) {
|
||||||
|
|
||||||
static bool direct_change_vt(struct wlr_session *base, unsigned vt) {
|
static bool direct_change_vt(struct wlr_session *base, unsigned vt) {
|
||||||
struct direct_session *session = wl_container_of(base, session, base);
|
struct direct_session *session = wl_container_of(base, session, base);
|
||||||
|
|
||||||
|
// Only seat0 has VTs associated with it
|
||||||
|
if (strcmp(session->base.seat, "seat0") != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0;
|
return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void direct_session_destroy(struct wlr_session *base) {
|
static void direct_session_destroy(struct wlr_session *base) {
|
||||||
struct direct_session *session = wl_container_of(base, session, base);
|
struct direct_session *session = wl_container_of(base, session, base);
|
||||||
|
|
||||||
|
if (strcmp(session->base.seat, "seat0") == 0) {
|
||||||
struct vt_mode mode = {
|
struct vt_mode mode = {
|
||||||
.mode = VT_AUTO,
|
.mode = VT_AUTO,
|
||||||
};
|
};
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode);
|
ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode);
|
||||||
|
@ -95,11 +103,13 @@ static void direct_session_destroy(struct wlr_session *base) {
|
||||||
wlr_log(L_ERROR, "Failed to restore tty");
|
wlr_log(L_ERROR, "Failed to restore tty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wl_event_source_remove(session->vt_source);
|
||||||
|
close(session->tty_fd);
|
||||||
|
}
|
||||||
|
|
||||||
direct_ipc_finish(session->sock, session->child);
|
direct_ipc_finish(session->sock, session->child);
|
||||||
close(session->sock);
|
close(session->sock);
|
||||||
|
|
||||||
wl_event_source_remove(session->vt_source);
|
|
||||||
close(session->tty_fd);
|
|
||||||
free(session);
|
free(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,19 +148,19 @@ static int vt_handler(int signo, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool setup_tty(struct direct_session *session, struct wl_display *display) {
|
static bool setup_tty(struct direct_session *session, struct wl_display *display) {
|
||||||
int fd = dup(STDIN_FILENO);
|
int fd = open("/dev/tty", O_RDWR);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
wlr_log_errno(L_ERROR, "Cannot open tty");
|
wlr_log_errno(L_ERROR, "Cannot open /dev/tty");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat st;
|
struct vt_stat vt_stat;
|
||||||
if (fstat(fd, &st) == -1 || major(st.st_rdev) != TTY_MAJOR || minor(st.st_rdev) == 0) {
|
if (ioctl(fd, VT_GETSTATE, &vt_stat)) {
|
||||||
wlr_log(L_ERROR, "Not running from a virtual terminal");
|
wlr_log_errno(L_ERROR, "Could not get current tty number");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tty = minor(st.st_rdev);
|
int tty = vt_stat.v_active;
|
||||||
int ret, kd_mode, old_kbmode;
|
int ret, kd_mode, old_kbmode;
|
||||||
|
|
||||||
ret = ioctl(fd, KDGETMODE, &kd_mode);
|
ret = ioctl(fd, KDGETMODE, &kd_mode);
|
||||||
|
@ -224,20 +234,24 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) {
|
||||||
goto error_session;
|
goto error_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!setup_tty(session, disp)) {
|
|
||||||
goto error_ipc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: Is it okay to trust the environment like this?
|
|
||||||
const char *seat = getenv("XDG_SEAT");
|
const char *seat = getenv("XDG_SEAT");
|
||||||
if (!seat) {
|
if (!seat) {
|
||||||
seat = "seat0";
|
seat = "seat0";
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_log(L_INFO, "Successfully loaded direct session");
|
if (strcmp(seat, "seat0") == 0) {
|
||||||
|
if (!setup_tty(session, disp)) {
|
||||||
|
goto error_ipc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session->base.vtnr = 0;
|
||||||
|
session->tty_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
|
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
|
||||||
session->base.impl = &session_direct;
|
session->base.impl = &session_direct;
|
||||||
|
|
||||||
|
wlr_log(L_INFO, "Successfully loaded direct session");
|
||||||
return &session->base;
|
return &session->base;
|
||||||
|
|
||||||
error_ipc:
|
error_ipc:
|
||||||
|
|
|
@ -109,6 +109,11 @@ static void logind_release_device(struct wlr_session *base, int fd) {
|
||||||
static bool logind_change_vt(struct wlr_session *base, unsigned vt) {
|
static bool logind_change_vt(struct wlr_session *base, unsigned vt) {
|
||||||
struct logind_session *session = wl_container_of(base, session, base);
|
struct logind_session *session = wl_container_of(base, session, base);
|
||||||
|
|
||||||
|
// Only seat0 has VTs associated with it
|
||||||
|
if (strcmp(session->base.seat, "seat0") != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
sd_bus_message *msg = NULL;
|
sd_bus_message *msg = NULL;
|
||||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
|
@ -19,9 +19,7 @@ extern const struct session_impl session_logind;
|
||||||
extern const struct session_impl session_direct;
|
extern const struct session_impl session_direct;
|
||||||
|
|
||||||
static const struct session_impl *impls[] = {
|
static const struct session_impl *impls[] = {
|
||||||
#ifdef WLR_HAS_SYSTEMD
|
#if defined(WLR_HAS_SYSTEMD) || defined(WLR_HAS_ELOGIND)
|
||||||
&session_logind,
|
|
||||||
#elif defined(WLR_HAS_ELOGIND)
|
|
||||||
&session_logind,
|
&session_logind,
|
||||||
#endif
|
#endif
|
||||||
&session_direct,
|
&session_direct,
|
||||||
|
|
|
@ -0,0 +1,743 @@
|
||||||
|
#define _XOPEN_SOURCE 700
|
||||||
|
#define _POSIX_C_SOURCE 199309L
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavutil/display.h>
|
||||||
|
#include <libavutil/hwcontext_drm.h>
|
||||||
|
#include <libavutil/pixdesc.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <libdrm/drm_fourcc.h>
|
||||||
|
#include "wlr-export-dmabuf-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
struct wayland_output {
|
||||||
|
struct wl_list link;
|
||||||
|
uint32_t id;
|
||||||
|
struct wl_output *output;
|
||||||
|
char *make;
|
||||||
|
char *model;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
AVRational framerate;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct capture_context {
|
||||||
|
AVClass *class; /* For pretty logging */
|
||||||
|
struct wl_display *display;
|
||||||
|
struct wl_registry *registry;
|
||||||
|
struct zwlr_export_dmabuf_manager_v1 *export_manager;
|
||||||
|
|
||||||
|
struct wl_list output_list;
|
||||||
|
|
||||||
|
/* Target */
|
||||||
|
struct wl_output *target_output;
|
||||||
|
|
||||||
|
/* Main frame callback */
|
||||||
|
struct zwlr_export_dmabuf_frame_v1 *frame_callback;
|
||||||
|
|
||||||
|
/* If something happens during capture */
|
||||||
|
int err;
|
||||||
|
int quit;
|
||||||
|
|
||||||
|
/* FFmpeg specific parts */
|
||||||
|
AVFrame *current_frame;
|
||||||
|
AVBufferRef *drm_device_ref;
|
||||||
|
AVBufferRef *drm_frames_ref;
|
||||||
|
|
||||||
|
AVBufferRef *mapped_device_ref;
|
||||||
|
AVBufferRef *mapped_frames_ref;
|
||||||
|
|
||||||
|
AVFormatContext *avf;
|
||||||
|
AVCodecContext *avctx;
|
||||||
|
|
||||||
|
int64_t start_pts;
|
||||||
|
|
||||||
|
/* Config */
|
||||||
|
enum AVPixelFormat software_format;
|
||||||
|
enum AVHWDeviceType hw_device_type;
|
||||||
|
AVDictionary *encoder_opts;
|
||||||
|
int is_software_encoder;
|
||||||
|
char *hardware_device;
|
||||||
|
char *out_filename;
|
||||||
|
char *encoder_name;
|
||||||
|
float out_bitrate;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void output_handle_geometry(void *data, struct wl_output *wl_output,
|
||||||
|
int32_t x, int32_t y, int32_t phys_width, int32_t phys_height,
|
||||||
|
int32_t subpixel, const char *make, const char *model,
|
||||||
|
int32_t transform) {
|
||||||
|
struct wayland_output *output = data;
|
||||||
|
output->make = av_strdup(make);
|
||||||
|
output->model = av_strdup(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handle_mode(void *data, struct wl_output *wl_output,
|
||||||
|
uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
|
||||||
|
if (flags & WL_OUTPUT_MODE_CURRENT) {
|
||||||
|
struct wayland_output *output = data;
|
||||||
|
output->width = width;
|
||||||
|
output->height = height;
|
||||||
|
output->framerate = (AVRational){ refresh, 1000 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handle_done(void* data, struct wl_output *wl_output) {
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handle_scale(void* data, struct wl_output *wl_output,
|
||||||
|
int32_t factor) {
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_output_listener output_listener = {
|
||||||
|
.geometry = output_handle_geometry,
|
||||||
|
.mode = output_handle_mode,
|
||||||
|
.done = output_handle_done,
|
||||||
|
.scale = output_handle_scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void registry_handle_add(void *data, struct wl_registry *reg,
|
||||||
|
uint32_t id, const char *interface, uint32_t ver) {
|
||||||
|
struct capture_context *ctx = data;
|
||||||
|
|
||||||
|
if (!strcmp(interface, wl_output_interface.name)) {
|
||||||
|
struct wayland_output *output = av_mallocz(sizeof(*output));
|
||||||
|
|
||||||
|
output->id = id;
|
||||||
|
output->output = wl_registry_bind(reg, id, &wl_output_interface, 1);
|
||||||
|
|
||||||
|
wl_output_add_listener(output->output, &output_listener, output);
|
||||||
|
wl_list_insert(&ctx->output_list, &output->link);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) {
|
||||||
|
ctx->export_manager = wl_registry_bind(reg, id,
|
||||||
|
&zwlr_export_dmabuf_manager_v1_interface, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_output(struct wayland_output *out) {
|
||||||
|
wl_list_remove(&out->link);
|
||||||
|
av_free(out->make);
|
||||||
|
av_free(out->model);
|
||||||
|
av_free(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wayland_output *find_output(struct capture_context *ctx,
|
||||||
|
struct wl_output *out, uint32_t id) {
|
||||||
|
struct wayland_output *output, *tmp;
|
||||||
|
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
|
||||||
|
if ((output->output == out) || (output->id == id)) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void registry_handle_remove(void *data, struct wl_registry *reg,
|
||||||
|
uint32_t id) {
|
||||||
|
remove_output(find_output((struct capture_context *)data, NULL, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = registry_handle_add,
|
||||||
|
.global_remove = registry_handle_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void frame_free(void *opaque, uint8_t *data) {
|
||||||
|
AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)data;
|
||||||
|
|
||||||
|
for (int i = 0; i < desc->nb_objects; ++i) {
|
||||||
|
close(desc->objects[i].fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
zwlr_export_dmabuf_frame_v1_destroy(opaque);
|
||||||
|
|
||||||
|
av_free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
|
||||||
|
uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y,
|
||||||
|
uint32_t buffer_flags, uint32_t flags, uint32_t format,
|
||||||
|
uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) {
|
||||||
|
struct capture_context *ctx = data;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* Allocate DRM specific struct */
|
||||||
|
AVDRMFrameDescriptor *desc = av_mallocz(sizeof(*desc));
|
||||||
|
if (!desc) {
|
||||||
|
err = AVERROR(ENOMEM);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc->nb_objects = num_objects;
|
||||||
|
desc->objects[0].format_modifier = ((uint64_t)mod_high << 32) | mod_low;
|
||||||
|
|
||||||
|
desc->nb_layers = 1;
|
||||||
|
desc->layers[0].format = format;
|
||||||
|
|
||||||
|
/* Allocate a frame */
|
||||||
|
AVFrame *f = av_frame_alloc();
|
||||||
|
if (!f) {
|
||||||
|
err = AVERROR(ENOMEM);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set base frame properties */
|
||||||
|
ctx->current_frame = f;
|
||||||
|
f->width = width;
|
||||||
|
f->height = height;
|
||||||
|
f->format = AV_PIX_FMT_DRM_PRIME;
|
||||||
|
|
||||||
|
/* Set the frame data to the DRM specific struct */
|
||||||
|
f->buf[0] = av_buffer_create((uint8_t*)desc, sizeof(*desc),
|
||||||
|
&frame_free, frame, 0);
|
||||||
|
if (!f->buf[0]) {
|
||||||
|
err = AVERROR(ENOMEM);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->data[0] = (uint8_t*)desc;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
ctx->err = err;
|
||||||
|
frame_free(frame, (uint8_t *)desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
|
||||||
|
uint32_t index, int32_t fd, uint32_t size, uint32_t offset,
|
||||||
|
uint32_t stride, uint32_t plane_index) {
|
||||||
|
struct capture_context *ctx = data;
|
||||||
|
AVFrame *f = ctx->current_frame;
|
||||||
|
AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0];
|
||||||
|
|
||||||
|
desc->objects[index].fd = fd;
|
||||||
|
desc->objects[index].size = size;
|
||||||
|
|
||||||
|
desc->layers[0].planes[plane_index].object_index = index;
|
||||||
|
desc->layers[0].planes[plane_index].offset = offset;
|
||||||
|
desc->layers[0].planes[plane_index].pitch = stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum AVPixelFormat drm_fmt_to_pixfmt(uint32_t fmt) {
|
||||||
|
switch (fmt) {
|
||||||
|
case DRM_FORMAT_NV12: return AV_PIX_FMT_NV12;
|
||||||
|
case DRM_FORMAT_ARGB8888: return AV_PIX_FMT_BGRA;
|
||||||
|
case DRM_FORMAT_XRGB8888: return AV_PIX_FMT_BGR0;
|
||||||
|
case DRM_FORMAT_ABGR8888: return AV_PIX_FMT_RGBA;
|
||||||
|
case DRM_FORMAT_XBGR8888: return AV_PIX_FMT_RGB0;
|
||||||
|
case DRM_FORMAT_RGBA8888: return AV_PIX_FMT_ABGR;
|
||||||
|
case DRM_FORMAT_RGBX8888: return AV_PIX_FMT_0BGR;
|
||||||
|
case DRM_FORMAT_BGRA8888: return AV_PIX_FMT_ARGB;
|
||||||
|
case DRM_FORMAT_BGRX8888: return AV_PIX_FMT_0RGB;
|
||||||
|
default: return AV_PIX_FMT_NONE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static int attach_drm_frames_ref(struct capture_context *ctx, AVFrame *f,
|
||||||
|
enum AVPixelFormat sw_format) {
|
||||||
|
int err = 0;
|
||||||
|
AVHWFramesContext *hwfc;
|
||||||
|
|
||||||
|
if (ctx->drm_frames_ref) {
|
||||||
|
hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data;
|
||||||
|
if (hwfc->width == f->width && hwfc->height == f->height &&
|
||||||
|
hwfc->sw_format == sw_format) {
|
||||||
|
goto attach;
|
||||||
|
}
|
||||||
|
av_buffer_unref(&ctx->drm_frames_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->drm_frames_ref = av_hwframe_ctx_alloc(ctx->drm_device_ref);
|
||||||
|
if (!ctx->drm_frames_ref) {
|
||||||
|
err = AVERROR(ENOMEM);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data;
|
||||||
|
|
||||||
|
hwfc->format = f->format;
|
||||||
|
hwfc->sw_format = sw_format;
|
||||||
|
hwfc->width = f->width;
|
||||||
|
hwfc->height = f->height;
|
||||||
|
|
||||||
|
err = av_hwframe_ctx_init(ctx->drm_frames_ref);
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "AVHWFramesContext init failed: %s!\n",
|
||||||
|
av_err2str(err));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach:
|
||||||
|
/* Set frame hardware context referencce */
|
||||||
|
f->hw_frames_ctx = av_buffer_ref(ctx->drm_frames_ref);
|
||||||
|
if (!f->hw_frames_ctx) {
|
||||||
|
err = AVERROR(ENOMEM);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
av_buffer_unref(&ctx->drm_frames_ref);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_cb(struct capture_context *ctx);
|
||||||
|
|
||||||
|
static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
|
||||||
|
uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
||||||
|
struct capture_context *ctx = data;
|
||||||
|
AVFrame *f = ctx->current_frame;
|
||||||
|
AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0];
|
||||||
|
enum AVPixelFormat pix_fmt = drm_fmt_to_pixfmt(desc->layers[0].format);
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
/* Attach the hardware frame context to the frame */
|
||||||
|
err = attach_drm_frames_ref(ctx, f, pix_fmt);
|
||||||
|
if (err) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: support multiplane stuff */
|
||||||
|
desc->layers[0].nb_planes = av_pix_fmt_count_planes(pix_fmt);
|
||||||
|
|
||||||
|
AVFrame *mapped_frame = av_frame_alloc();
|
||||||
|
if (!mapped_frame) {
|
||||||
|
err = AVERROR(ENOMEM);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVHWFramesContext *mapped_hwfc;
|
||||||
|
mapped_hwfc = (AVHWFramesContext *)ctx->mapped_frames_ref->data;
|
||||||
|
mapped_frame->format = mapped_hwfc->format;
|
||||||
|
|
||||||
|
/* Set frame hardware context referencce */
|
||||||
|
mapped_frame->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref);
|
||||||
|
if (!mapped_frame->hw_frames_ctx) {
|
||||||
|
err = AVERROR(ENOMEM);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = av_hwframe_map(mapped_frame, f, 0);
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Error mapping: %s!\n", av_err2str(err));
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVFrame *enc_input = mapped_frame;
|
||||||
|
|
||||||
|
if (ctx->is_software_encoder) {
|
||||||
|
AVFrame *soft_frame = av_frame_alloc();
|
||||||
|
av_hwframe_transfer_data(soft_frame, mapped_frame, 0);
|
||||||
|
av_frame_free(&mapped_frame);
|
||||||
|
enc_input = soft_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nanoseconds */
|
||||||
|
enc_input->pts = (((uint64_t)tv_sec_hi) << 32) | tv_sec_lo;
|
||||||
|
enc_input->pts *= 1000000000;
|
||||||
|
enc_input->pts += tv_nsec;
|
||||||
|
|
||||||
|
if (!ctx->start_pts) {
|
||||||
|
ctx->start_pts = enc_input->pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
enc_input->pts -= ctx->start_pts;
|
||||||
|
|
||||||
|
enc_input->pts = av_rescale_q(enc_input->pts, (AVRational){ 1, 1000000000 },
|
||||||
|
ctx->avctx->time_base);
|
||||||
|
|
||||||
|
do {
|
||||||
|
err = avcodec_send_frame(ctx->avctx, enc_input);
|
||||||
|
|
||||||
|
av_frame_free(&enc_input);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", av_err2str(err));
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
AVPacket pkt;
|
||||||
|
av_init_packet(&pkt);
|
||||||
|
|
||||||
|
int ret = avcodec_receive_packet(ctx->avctx, &pkt);
|
||||||
|
if (ret == AVERROR(EAGAIN)) {
|
||||||
|
break;
|
||||||
|
} else if (ret == AVERROR_EOF) {
|
||||||
|
av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n");
|
||||||
|
ctx->quit = 2;
|
||||||
|
goto end;
|
||||||
|
} else if (ret) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n",
|
||||||
|
av_err2str(ret));
|
||||||
|
err = ret;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt.stream_index = 0;
|
||||||
|
err = av_interleaved_write_frame(ctx->avf, &pkt);
|
||||||
|
|
||||||
|
av_packet_unref(&pkt);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n",
|
||||||
|
av_err2str(err));
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} while (ctx->quit);
|
||||||
|
|
||||||
|
av_log(NULL, AV_LOG_INFO, "Encoded frame %i!\n", ctx->avctx->frame_number);
|
||||||
|
|
||||||
|
register_cb(ctx);
|
||||||
|
|
||||||
|
end:
|
||||||
|
ctx->err = err;
|
||||||
|
av_frame_free(&ctx->current_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
|
||||||
|
uint32_t reason) {
|
||||||
|
struct capture_context *ctx = data;
|
||||||
|
av_log(ctx, AV_LOG_WARNING, "Frame cancelled!\n");
|
||||||
|
av_frame_free(&ctx->current_frame);
|
||||||
|
if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Permanent failure, exiting\n");
|
||||||
|
ctx->err = 1;
|
||||||
|
} else {
|
||||||
|
register_cb(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = {
|
||||||
|
.frame = frame_start,
|
||||||
|
.object = frame_object,
|
||||||
|
.ready = frame_ready,
|
||||||
|
.cancel = frame_cancel,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void register_cb(struct capture_context *ctx) {
|
||||||
|
ctx->frame_callback = zwlr_export_dmabuf_manager_v1_capture_output(
|
||||||
|
ctx->export_manager, 0, ctx->target_output);
|
||||||
|
|
||||||
|
zwlr_export_dmabuf_frame_v1_add_listener(ctx->frame_callback,
|
||||||
|
&frame_listener, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_lavu_hwcontext(struct capture_context *ctx) {
|
||||||
|
/* DRM hwcontext */
|
||||||
|
ctx->drm_device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM);
|
||||||
|
if (!ctx->drm_device_ref)
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
AVHWDeviceContext *ref_data = (AVHWDeviceContext*)ctx->drm_device_ref->data;
|
||||||
|
AVDRMDeviceContext *hwctx = ref_data->hwctx;
|
||||||
|
|
||||||
|
/* We don't need a device (we don't even know it and can't open it) */
|
||||||
|
hwctx->fd = -1;
|
||||||
|
|
||||||
|
av_hwdevice_ctx_init(ctx->drm_device_ref);
|
||||||
|
|
||||||
|
/* Mapped hwcontext */
|
||||||
|
int err = av_hwdevice_ctx_create(&ctx->mapped_device_ref,
|
||||||
|
ctx->hw_device_type, ctx->hardware_device, NULL, 0);
|
||||||
|
if (err < 0) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Failed to create a hardware device: %s\n",
|
||||||
|
av_err2str(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_hwframe_ctx(struct capture_context *ctx,
|
||||||
|
AVBufferRef *hw_device_ctx) {
|
||||||
|
AVHWFramesContext *frames_ctx = NULL;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (!(ctx->mapped_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) {
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
AVHWFramesConstraints *cst =
|
||||||
|
av_hwdevice_get_hwframe_constraints(ctx->mapped_device_ref, NULL);
|
||||||
|
if (!cst) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Failed to get hw device constraints!\n");
|
||||||
|
av_buffer_unref(&ctx->mapped_frames_ref);
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
frames_ctx = (AVHWFramesContext *)(ctx->mapped_frames_ref->data);
|
||||||
|
frames_ctx->format = cst->valid_hw_formats[0];
|
||||||
|
frames_ctx->sw_format = ctx->avctx->pix_fmt;
|
||||||
|
frames_ctx->width = ctx->avctx->width;
|
||||||
|
frames_ctx->height = ctx->avctx->height;
|
||||||
|
|
||||||
|
av_hwframe_constraints_free(&cst);
|
||||||
|
|
||||||
|
if ((err = av_hwframe_ctx_init(ctx->mapped_frames_ref))) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Failed to initialize hw frame context: %s!\n",
|
||||||
|
av_err2str(err));
|
||||||
|
av_buffer_unref(&ctx->mapped_frames_ref);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->is_software_encoder) {
|
||||||
|
ctx->avctx->pix_fmt = frames_ctx->format;
|
||||||
|
ctx->avctx->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref);
|
||||||
|
if (!ctx->avctx->hw_frames_ctx) {
|
||||||
|
av_buffer_unref(&ctx->mapped_frames_ref);
|
||||||
|
err = AVERROR(ENOMEM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_encoding(struct capture_context *ctx) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* lavf init */
|
||||||
|
err = avformat_alloc_output_context2(&ctx->avf, NULL,
|
||||||
|
NULL, ctx->out_filename);
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Unable to init lavf context!\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVStream *st = avformat_new_stream(ctx->avf, NULL);
|
||||||
|
if (!st) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Unable to alloc stream!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find encoder */
|
||||||
|
AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name);
|
||||||
|
if (!out_codec) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n");
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
}
|
||||||
|
ctx->avf->oformat->video_codec = out_codec->id;
|
||||||
|
ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE);
|
||||||
|
|
||||||
|
ctx->avctx = avcodec_alloc_context3(out_codec);
|
||||||
|
if (!ctx->avctx)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ctx->avctx->opaque = ctx;
|
||||||
|
ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f;
|
||||||
|
ctx->avctx->pix_fmt = ctx->software_format;
|
||||||
|
ctx->avctx->time_base = (AVRational){ 1, 1000 };
|
||||||
|
ctx->avctx->compression_level = 7;
|
||||||
|
ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width;
|
||||||
|
ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height;
|
||||||
|
|
||||||
|
if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) {
|
||||||
|
ctx->avctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
st->id = 0;
|
||||||
|
st->time_base = ctx->avctx->time_base;
|
||||||
|
st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate;
|
||||||
|
|
||||||
|
/* Init hw frames context */
|
||||||
|
err = set_hwframe_ctx(ctx, ctx->mapped_device_ref);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = avcodec_open2(ctx->avctx, out_codec, &ctx->encoder_opts);
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Cannot open encoder: %s!\n",
|
||||||
|
av_err2str(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avcodec_parameters_from_context(st->codecpar, ctx->avctx) < 0) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Couldn't copy codec params: %s!\n",
|
||||||
|
av_err2str(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debug print */
|
||||||
|
av_dump_format(ctx->avf, 0, ctx->out_filename, 1);
|
||||||
|
|
||||||
|
/* Open for writing */
|
||||||
|
err = avio_open(&ctx->avf->pb, ctx->out_filename, AVIO_FLAG_WRITE);
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Couldn't open %s: %s!\n", ctx->out_filename,
|
||||||
|
av_err2str(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = avformat_write_header(ctx->avf, NULL);
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Couldn't write header: %s!\n", av_err2str(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct capture_context *q_ctx = NULL;
|
||||||
|
|
||||||
|
void on_quit_signal(int signo) {
|
||||||
|
printf("\r");
|
||||||
|
q_ctx->quit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int main_loop(struct capture_context *ctx) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
q_ctx = ctx;
|
||||||
|
|
||||||
|
if (signal(SIGINT, on_quit_signal) == SIG_ERR) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Unable to install signal handler!\n");
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = init_lavu_hwcontext(ctx);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = init_encoding(ctx);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start the frame callback */
|
||||||
|
register_cb(ctx);
|
||||||
|
|
||||||
|
while (wl_display_dispatch(ctx->display) != -1 && !ctx->err &&
|
||||||
|
ctx->quit < 2) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
err = av_write_trailer(ctx->avf);
|
||||||
|
if (err) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Error writing trailer: %s!\n",
|
||||||
|
av_err2str(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_log(ctx, AV_LOG_INFO, "Wrote trailer!\n");
|
||||||
|
|
||||||
|
return ctx->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init(struct capture_context *ctx) {
|
||||||
|
ctx->display = wl_display_connect(NULL);
|
||||||
|
if (!ctx->display) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Failed to connect to display!\n");
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_init(&ctx->output_list);
|
||||||
|
|
||||||
|
ctx->registry = wl_display_get_registry(ctx->display);
|
||||||
|
wl_registry_add_listener(ctx->registry, ®istry_listener, ctx);
|
||||||
|
|
||||||
|
wl_display_roundtrip(ctx->display);
|
||||||
|
wl_display_dispatch(ctx->display);
|
||||||
|
|
||||||
|
if (!ctx->export_manager) {
|
||||||
|
av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n",
|
||||||
|
zwlr_export_dmabuf_manager_v1_interface.name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uninit(struct capture_context *ctx);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int err;
|
||||||
|
struct capture_context ctx = { 0 };
|
||||||
|
ctx.class = &((AVClass) {
|
||||||
|
.class_name = "dmabuf-capture",
|
||||||
|
.item_name = av_default_item_name,
|
||||||
|
.version = LIBAVUTIL_VERSION_INT,
|
||||||
|
});
|
||||||
|
|
||||||
|
err = init(&ctx);
|
||||||
|
if (err) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wayland_output *o, *tmp_o;
|
||||||
|
wl_list_for_each_reverse_safe(o, tmp_o, &ctx.output_list, link) {
|
||||||
|
printf("Capturable output: %s Model: %s: ID: %i\n",
|
||||||
|
o->make, o->model, o->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc != 8) {
|
||||||
|
printf("Invalid number of arguments! Usage and example:\n"
|
||||||
|
"./dmabuf-capture <source id> <hardware device type> <device> "
|
||||||
|
"<encoder name> <pixel format> <bitrate in Mbps> <file path>\n"
|
||||||
|
"./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 "
|
||||||
|
"dmabuf_recording_01.mkv\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int o_id = strtol(argv[1], NULL, 10);
|
||||||
|
o = find_output(&ctx, NULL, o_id);
|
||||||
|
if (!o) {
|
||||||
|
printf("Unable to find output with ID %i!\n", o_id);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.target_output = o->output;
|
||||||
|
ctx.hw_device_type = av_hwdevice_find_type_by_name(argv[2]);
|
||||||
|
ctx.hardware_device = argv[3];
|
||||||
|
|
||||||
|
ctx.encoder_name = argv[4];
|
||||||
|
ctx.software_format = av_get_pix_fmt(argv[5]);
|
||||||
|
ctx.out_bitrate = strtof(argv[6], NULL);
|
||||||
|
ctx.out_filename = argv[7];
|
||||||
|
|
||||||
|
av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0);
|
||||||
|
|
||||||
|
err = main_loop(&ctx);
|
||||||
|
if (err) {
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
uninit(&ctx);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uninit(struct capture_context *ctx) {
|
||||||
|
struct wayland_output *output, *tmp_o;
|
||||||
|
wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) {
|
||||||
|
remove_output(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->export_manager) {
|
||||||
|
zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
av_buffer_unref(&ctx->drm_frames_ref);
|
||||||
|
av_buffer_unref(&ctx->drm_device_ref);
|
||||||
|
av_buffer_unref(&ctx->mapped_frames_ref);
|
||||||
|
av_buffer_unref(&ctx->mapped_device_ref);
|
||||||
|
|
||||||
|
av_dict_free(&ctx->encoder_opts);
|
||||||
|
|
||||||
|
avcodec_close(ctx->avctx);
|
||||||
|
if (ctx->avf) {
|
||||||
|
avio_closep(&ctx->avf->pb);
|
||||||
|
}
|
||||||
|
avformat_free_context(ctx->avf);
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
threads = dependency('threads')
|
threads = dependency('threads')
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
|
|
||||||
|
libavutil = dependency('libavutil', required: false)
|
||||||
|
libavcodec = dependency('libavcodec', required: false)
|
||||||
|
libavformat = dependency('libavformat', required: false)
|
||||||
|
|
||||||
executable('simple', 'simple.c', dependencies: wlroots)
|
executable('simple', 'simple.c', dependencies: wlroots)
|
||||||
executable('pointer', 'pointer.c', dependencies: wlroots)
|
executable('pointer', 'pointer.c', dependencies: wlroots)
|
||||||
executable('touch', 'touch.c', 'cat.c', dependencies: wlroots)
|
executable('touch', 'touch.c', 'cat.c', dependencies: wlroots)
|
||||||
|
@ -38,3 +42,11 @@ executable(
|
||||||
'input-inhibitor.c',
|
'input-inhibitor.c',
|
||||||
dependencies: [wayland_cursor, wayland_client, wlr_protos, wlroots]
|
dependencies: [wayland_cursor, wayland_client, wlr_protos, wlroots]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if libavutil.found() and libavcodec.found() and libavformat.found()
|
||||||
|
executable(
|
||||||
|
'dmabuf-capture',
|
||||||
|
'dmabuf-capture.c',
|
||||||
|
dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat, wlroots]
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
|
@ -52,5 +52,6 @@ struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf);
|
||||||
void post_drm_surface(struct wlr_drm_surface *surf);
|
void post_drm_surface(struct wlr_drm_surface *surf);
|
||||||
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
|
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
|
||||||
struct gbm_bo *src);
|
struct gbm_bo *src);
|
||||||
|
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,6 +25,14 @@ struct wlr_gles2_pixel_format {
|
||||||
bool has_alpha;
|
bool has_alpha;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wlr_gles2_tex_shader {
|
||||||
|
GLuint program;
|
||||||
|
GLint proj;
|
||||||
|
GLint invert_y;
|
||||||
|
GLint tex;
|
||||||
|
GLint alpha;
|
||||||
|
};
|
||||||
|
|
||||||
struct wlr_gles2_renderer {
|
struct wlr_gles2_renderer {
|
||||||
struct wlr_renderer wlr_renderer;
|
struct wlr_renderer wlr_renderer;
|
||||||
|
|
||||||
|
@ -32,11 +40,19 @@ struct wlr_gles2_renderer {
|
||||||
const char *exts_str;
|
const char *exts_str;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
GLuint quad;
|
struct {
|
||||||
GLuint ellipse;
|
GLuint program;
|
||||||
GLuint tex_rgba;
|
GLint proj;
|
||||||
GLuint tex_rgbx;
|
GLint color;
|
||||||
GLuint tex_ext;
|
} quad;
|
||||||
|
struct {
|
||||||
|
GLuint program;
|
||||||
|
GLint proj;
|
||||||
|
GLint color;
|
||||||
|
} ellipse;
|
||||||
|
struct wlr_gles2_tex_shader tex_rgba;
|
||||||
|
struct wlr_gles2_tex_shader tex_rgbx;
|
||||||
|
struct wlr_gles2_tex_shader tex_ext;
|
||||||
} shaders;
|
} shaders;
|
||||||
|
|
||||||
uint32_t viewport_width, viewport_height;
|
uint32_t viewport_width, viewport_height;
|
||||||
|
|
|
@ -45,6 +45,7 @@ struct roots_desktop {
|
||||||
struct wlr_xdg_shell *xdg_shell;
|
struct wlr_xdg_shell *xdg_shell;
|
||||||
struct wlr_gamma_control_manager *gamma_control_manager;
|
struct wlr_gamma_control_manager *gamma_control_manager;
|
||||||
struct wlr_screenshooter *screenshooter;
|
struct wlr_screenshooter *screenshooter;
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1;
|
||||||
struct wlr_server_decoration_manager *server_decoration_manager;
|
struct wlr_server_decoration_manager *server_decoration_manager;
|
||||||
struct wlr_primary_selection_device_manager *primary_selection_device_manager;
|
struct wlr_primary_selection_device_manager *primary_selection_device_manager;
|
||||||
struct wlr_idle *idle;
|
struct wlr_idle *idle;
|
||||||
|
|
|
@ -25,8 +25,12 @@ struct wlr_session {
|
||||||
struct wl_signal session_signal;
|
struct wl_signal session_signal;
|
||||||
bool active;
|
bool active;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0 if virtual terminals are not supported
|
||||||
|
* i.e. seat != "seat0"
|
||||||
|
*/
|
||||||
unsigned vtnr;
|
unsigned vtnr;
|
||||||
char seat[8];
|
char seat[256];
|
||||||
|
|
||||||
struct udev *udev;
|
struct udev *udev;
|
||||||
struct udev_monitor *mon;
|
struct udev_monitor *mon;
|
||||||
|
|
|
@ -23,6 +23,8 @@ struct wlr_output_impl {
|
||||||
void (*set_gamma)(struct wlr_output *output,
|
void (*set_gamma)(struct wlr_output *output,
|
||||||
uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b);
|
uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b);
|
||||||
uint32_t (*get_gamma_size)(struct wlr_output *output);
|
uint32_t (*get_gamma_size)(struct wlr_output *output);
|
||||||
|
bool (*export_dmabuf)(struct wlr_output *output,
|
||||||
|
struct wlr_dmabuf_attributes *attribs);
|
||||||
};
|
};
|
||||||
|
|
||||||
void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
|
void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef WLR_RENDER_DMABUF_H
|
#ifndef WLR_RENDER_DMABUF_H
|
||||||
#define WLR_RENDER_DMABUF_H
|
#define WLR_RENDER_DMABUF_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
// So we don't have to pull in linux specific drm headers
|
// So we don't have to pull in linux specific drm headers
|
||||||
#ifndef DRM_FORMAT_MOD_INVALID
|
#ifndef DRM_FORMAT_MOD_INVALID
|
||||||
#define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1)
|
#define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1)
|
||||||
|
@ -26,4 +28,9 @@ struct wlr_dmabuf_attributes {
|
||||||
int fd[WLR_DMABUF_MAX_PLANES];
|
int fd[WLR_DMABUF_MAX_PLANES];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes all file descriptors in the DMA-BUF attributes.
|
||||||
|
*/
|
||||||
|
void wlr_dmabuf_attributes_finish(struct wlr_dmabuf_attributes *attribs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,9 +18,10 @@ struct wlr_egl {
|
||||||
struct {
|
struct {
|
||||||
bool bind_wayland_display_wl;
|
bool bind_wayland_display_wl;
|
||||||
bool buffer_age_ext;
|
bool buffer_age_ext;
|
||||||
bool image_dmabuf_import_modifiers_ext;
|
|
||||||
bool image_dmabuf_import_ext;
|
|
||||||
bool image_base_khr;
|
bool image_base_khr;
|
||||||
|
bool image_dma_buf_export_mesa;
|
||||||
|
bool image_dmabuf_import_ext;
|
||||||
|
bool image_dmabuf_import_modifiers_ext;
|
||||||
bool swap_buffers_with_damage_ext;
|
bool swap_buffers_with_damage_ext;
|
||||||
bool swap_buffers_with_damage_khr;
|
bool swap_buffers_with_damage_khr;
|
||||||
} exts;
|
} exts;
|
||||||
|
@ -80,6 +81,10 @@ int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int **formats);
|
||||||
int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, int format,
|
int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, int format,
|
||||||
uint64_t **modifiers);
|
uint64_t **modifiers);
|
||||||
|
|
||||||
|
bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image,
|
||||||
|
int32_t width, int32_t height, uint32_t flags,
|
||||||
|
struct wlr_dmabuf_attributes *attribs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys an EGL image created with the given wlr_egl.
|
* Destroys an EGL image created with the given wlr_egl.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -60,6 +60,8 @@ struct wlr_texture_impl {
|
||||||
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
|
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
|
||||||
uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
|
uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
|
||||||
uint32_t dst_y, const void *data);
|
uint32_t dst_y, const void *data);
|
||||||
|
bool (*to_dmabuf)(struct wlr_texture *texture,
|
||||||
|
struct wlr_dmabuf_attributes *attribs);
|
||||||
void (*destroy)(struct wlr_texture *texture);
|
void (*destroy)(struct wlr_texture *texture);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@ bool wlr_texture_write_pixels(struct wlr_texture *texture,
|
||||||
uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
|
uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
|
||||||
const void *data);
|
const void *data);
|
||||||
|
|
||||||
|
bool wlr_texture_to_dmabuf(struct wlr_texture *texture,
|
||||||
|
struct wlr_dmabuf_attributes *attribs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys this wlr_texture.
|
* Destroys this wlr_texture.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef WLR_TYPES_WLR_BUFFER_H
|
||||||
|
#define WLR_TYPES_WLR_BUFFER_H
|
||||||
|
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A client buffer.
|
||||||
|
*/
|
||||||
|
struct wlr_buffer {
|
||||||
|
/**
|
||||||
|
* The buffer resource, if any. Will be NULL if the client destroys it.
|
||||||
|
*/
|
||||||
|
struct wl_resource *resource;
|
||||||
|
/**
|
||||||
|
* The buffer's texture, if any. A buffer will not have a texture if the
|
||||||
|
* client destroys the buffer before it has been released.
|
||||||
|
*/
|
||||||
|
struct wlr_texture *texture;
|
||||||
|
bool released;
|
||||||
|
size_t n_refs;
|
||||||
|
|
||||||
|
struct wl_listener resource_destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_renderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a resource is a wl_buffer resource.
|
||||||
|
*/
|
||||||
|
bool wlr_resource_is_buffer(struct wl_resource *resource);
|
||||||
|
/**
|
||||||
|
* Get the size of a wl_buffer resource.
|
||||||
|
*/
|
||||||
|
bool wlr_buffer_get_resource_size(struct wl_resource *resource,
|
||||||
|
struct wlr_renderer *renderer, int *width, int *height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload a buffer to the GPU and reference it.
|
||||||
|
*/
|
||||||
|
struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer,
|
||||||
|
struct wl_resource *resource);
|
||||||
|
/**
|
||||||
|
* Reference the buffer.
|
||||||
|
*/
|
||||||
|
struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer);
|
||||||
|
/**
|
||||||
|
* Unreference the buffer. After this call, `buffer` may not be accessed
|
||||||
|
* anymore.
|
||||||
|
*/
|
||||||
|
void wlr_buffer_unref(struct wlr_buffer *buffer);
|
||||||
|
/**
|
||||||
|
* Try to update the buffer's content. On success, returns the updated buffer
|
||||||
|
* and destroys the provided `buffer`. On error, `buffer` is intact and NULL is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* Fails if there's more than one reference to the buffer or if the texture
|
||||||
|
* isn't mutable.
|
||||||
|
*/
|
||||||
|
struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer,
|
||||||
|
struct wl_resource *resource, pixman_region32_t *damage);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef WLR_TYPES_WLR_EXPORT_DMABUF_V1_H
|
||||||
|
#define WLR_TYPES_WLR_EXPORT_DMABUF_V1_H
|
||||||
|
|
||||||
|
#include <wayland-server.h>
|
||||||
|
#include <wlr/render/dmabuf.h>
|
||||||
|
|
||||||
|
struct wlr_export_dmabuf_manager_v1;
|
||||||
|
|
||||||
|
struct wlr_export_dmabuf_frame_v1 {
|
||||||
|
struct wl_resource *resource;
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *manager;
|
||||||
|
struct wl_list link;
|
||||||
|
|
||||||
|
struct wlr_dmabuf_attributes attribs;
|
||||||
|
struct wlr_output *output;
|
||||||
|
|
||||||
|
struct wl_listener output_swap_buffers;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_export_dmabuf_manager_v1 {
|
||||||
|
struct wl_global *global;
|
||||||
|
struct wl_list resources;
|
||||||
|
struct wl_list frames;
|
||||||
|
|
||||||
|
struct wl_listener display_destroy;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct wl_signal destroy;
|
||||||
|
} events;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create(
|
||||||
|
struct wl_display *display);
|
||||||
|
void wlr_export_dmabuf_manager_v1_destroy(
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *manager);
|
||||||
|
|
||||||
|
#endif
|
|
@ -6,6 +6,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
#include <wayland-util.h>
|
#include <wayland-util.h>
|
||||||
|
#include <wlr/types/wlr_linux_dmabuf.h>
|
||||||
|
|
||||||
struct wlr_output_mode {
|
struct wlr_output_mode {
|
||||||
uint32_t flags; // enum wl_output_mode
|
uint32_t flags; // enum wl_output_mode
|
||||||
|
@ -81,7 +82,7 @@ struct wlr_output {
|
||||||
struct {
|
struct {
|
||||||
struct wl_signal frame;
|
struct wl_signal frame;
|
||||||
struct wl_signal needs_swap;
|
struct wl_signal needs_swap;
|
||||||
struct wl_signal swap_buffers;
|
struct wl_signal swap_buffers; // wlr_output_event_swap_buffers
|
||||||
struct wl_signal enable;
|
struct wl_signal enable;
|
||||||
struct wl_signal mode;
|
struct wl_signal mode;
|
||||||
struct wl_signal scale;
|
struct wl_signal scale;
|
||||||
|
@ -107,6 +108,12 @@ struct wlr_output {
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wlr_output_event_swap_buffers {
|
||||||
|
struct wlr_output *output;
|
||||||
|
struct timespec *when;
|
||||||
|
pixman_region32_t *damage;
|
||||||
|
};
|
||||||
|
|
||||||
struct wlr_surface;
|
struct wlr_surface;
|
||||||
|
|
||||||
void wlr_output_enable(struct wlr_output *output, bool enable);
|
void wlr_output_enable(struct wlr_output *output, bool enable);
|
||||||
|
@ -162,6 +169,8 @@ void wlr_output_schedule_frame(struct wlr_output *output);
|
||||||
void wlr_output_set_gamma(struct wlr_output *output,
|
void wlr_output_set_gamma(struct wlr_output *output,
|
||||||
uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b);
|
uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b);
|
||||||
uint32_t wlr_output_get_gamma_size(struct wlr_output *output);
|
uint32_t wlr_output_get_gamma_size(struct wlr_output *output);
|
||||||
|
bool wlr_output_export_dmabuf(struct wlr_output *output,
|
||||||
|
struct wlr_dmabuf_attributes *attribs);
|
||||||
void wlr_output_set_fullscreen_surface(struct wlr_output *output,
|
void wlr_output_set_fullscreen_surface(struct wlr_output *output,
|
||||||
struct wlr_surface *surface);
|
struct wlr_surface *surface);
|
||||||
struct wlr_output *wlr_output_from_resource(struct wl_resource *resource);
|
struct wlr_output *wlr_output_from_resource(struct wl_resource *resource);
|
||||||
|
|
|
@ -8,11 +8,6 @@
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
|
|
||||||
struct wlr_frame_callback {
|
|
||||||
struct wl_resource *resource;
|
|
||||||
struct wl_list link;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define WLR_SURFACE_INVALID_BUFFER 1
|
#define WLR_SURFACE_INVALID_BUFFER 1
|
||||||
#define WLR_SURFACE_INVALID_SURFACE_DAMAGE 2
|
#define WLR_SURFACE_INVALID_SURFACE_DAMAGE 2
|
||||||
#define WLR_SURFACE_INVALID_BUFFER_DAMAGE 4
|
#define WLR_SURFACE_INVALID_BUFFER_DAMAGE 4
|
||||||
|
@ -69,13 +64,16 @@ struct wlr_subsurface {
|
||||||
struct wlr_surface {
|
struct wlr_surface {
|
||||||
struct wl_resource *resource;
|
struct wl_resource *resource;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct wlr_texture *texture;
|
/**
|
||||||
|
* The surface's buffer, if any. A surface has an attached buffer when it
|
||||||
|
* commits with a non-null buffer in its pending state. A surface will not
|
||||||
|
* have a buffer if it has never committed one, has committed a null buffer,
|
||||||
|
* or something went wrong with uploading the buffer.
|
||||||
|
*/
|
||||||
|
struct wlr_buffer *buffer;
|
||||||
struct wlr_surface_state *current, *pending;
|
struct wlr_surface_state *current, *pending;
|
||||||
const char *role; // the lifetime-bound role or null
|
const char *role; // the lifetime-bound role or null
|
||||||
|
|
||||||
float buffer_to_surface_matrix[9];
|
|
||||||
float surface_to_buffer_matrix[9];
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct wl_signal commit;
|
struct wl_signal commit;
|
||||||
struct wl_signal new_subsurface;
|
struct wl_signal new_subsurface;
|
||||||
|
@ -124,6 +122,13 @@ int wlr_surface_set_role(struct wlr_surface *surface, const char *role,
|
||||||
*/
|
*/
|
||||||
bool wlr_surface_has_buffer(struct wlr_surface *surface);
|
bool wlr_surface_has_buffer(struct wlr_surface *surface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the texture of the buffer currently attached to this surface. Returns
|
||||||
|
* NULL if no buffer is currently attached or if something went wrong with
|
||||||
|
* uploading the buffer.
|
||||||
|
*/
|
||||||
|
struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new subsurface resource with the provided new ID. If `resource_list`
|
* Create a new subsurface resource with the provided new ID. If `resource_list`
|
||||||
* is non-NULL, adds the subsurface's resource to the list.
|
* is non-NULL, adds the subsurface's resource to the list.
|
||||||
|
|
|
@ -39,8 +39,9 @@ protocols = [
|
||||||
'screenshooter.xml',
|
'screenshooter.xml',
|
||||||
'server-decoration.xml',
|
'server-decoration.xml',
|
||||||
'virtual-keyboard-unstable-v1.xml',
|
'virtual-keyboard-unstable-v1.xml',
|
||||||
'wlr-layer-shell-unstable-v1.xml',
|
'wlr-export-dmabuf-unstable-v1.xml',
|
||||||
'wlr-input-inhibitor-unstable-v1.xml',
|
'wlr-input-inhibitor-unstable-v1.xml',
|
||||||
|
'wlr-layer-shell-unstable-v1.xml',
|
||||||
]
|
]
|
||||||
|
|
||||||
client_protocols = [
|
client_protocols = [
|
||||||
|
@ -49,6 +50,7 @@ client_protocols = [
|
||||||
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
|
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
|
||||||
'idle.xml',
|
'idle.xml',
|
||||||
'screenshooter.xml',
|
'screenshooter.xml',
|
||||||
|
'wlr-export-dmabuf-unstable-v1.xml',
|
||||||
'wlr-layer-shell-unstable-v1.xml',
|
'wlr-layer-shell-unstable-v1.xml',
|
||||||
'wlr-input-inhibitor-unstable-v1.xml',
|
'wlr-input-inhibitor-unstable-v1.xml',
|
||||||
]
|
]
|
||||||
|
|
|
@ -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>
|
|
@ -0,0 +1,10 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wlr/render/dmabuf.h>
|
||||||
|
|
||||||
|
void wlr_dmabuf_attributes_finish( struct wlr_dmabuf_attributes *attribs) {
|
||||||
|
for (int i = 0; i < attribs->n_planes; ++i) {
|
||||||
|
close(attribs->fd[i]);
|
||||||
|
attribs->fd[i] = -1;
|
||||||
|
}
|
||||||
|
attribs->n_planes = 0;
|
||||||
|
}
|
36
render/egl.c
36
render/egl.c
|
@ -164,6 +164,11 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display,
|
||||||
egl->exts.image_dmabuf_import_modifiers_ext =
|
egl->exts.image_dmabuf_import_modifiers_ext =
|
||||||
check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import_modifiers")
|
check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import_modifiers")
|
||||||
&& eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT;
|
&& eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT;
|
||||||
|
|
||||||
|
egl->exts.image_dma_buf_export_mesa =
|
||||||
|
check_egl_ext(egl->exts_str, "EGL_MESA_image_dma_buf_export") &&
|
||||||
|
eglExportDMABUFImageQueryMESA && eglExportDMABUFImageMESA;
|
||||||
|
|
||||||
print_dmabuf_formats(egl);
|
print_dmabuf_formats(egl);
|
||||||
|
|
||||||
egl->exts.bind_wayland_display_wl =
|
egl->exts.bind_wayland_display_wl =
|
||||||
|
@ -511,6 +516,37 @@ int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl,
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image,
|
||||||
|
int32_t width, int32_t height, uint32_t flags,
|
||||||
|
struct wlr_dmabuf_attributes *attribs) {
|
||||||
|
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
|
||||||
|
|
||||||
|
if (!egl->exts.image_dma_buf_export_mesa) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only one set of modifiers is returned for all planes
|
||||||
|
if (!eglExportDMABUFImageQueryMESA(egl->display, image,
|
||||||
|
(int *)&attribs->format, &attribs->n_planes, &attribs->modifier)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (attribs->n_planes > WLR_DMABUF_MAX_PLANES) {
|
||||||
|
wlr_log(L_ERROR, "EGL returned %d planes, but only %d are supported",
|
||||||
|
attribs->n_planes, WLR_DMABUF_MAX_PLANES);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!eglExportDMABUFImageMESA(egl->display, image, attribs->fd,
|
||||||
|
(EGLint *)attribs->stride, (EGLint *)attribs->offset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
attribs->width = width;
|
||||||
|
attribs->height = height;
|
||||||
|
attribs->flags = flags;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool wlr_egl_destroy_surface(struct wlr_egl *egl, EGLSurface surface) {
|
bool wlr_egl_destroy_surface(struct wlr_egl *egl, EGLSurface surface) {
|
||||||
if (!surface) {
|
if (!surface) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -10,6 +10,8 @@ eglCreatePlatformWindowSurfaceEXT
|
||||||
-eglSwapBuffersWithDamageKHR
|
-eglSwapBuffersWithDamageKHR
|
||||||
-eglQueryDmaBufFormatsEXT
|
-eglQueryDmaBufFormatsEXT
|
||||||
-eglQueryDmaBufModifiersEXT
|
-eglQueryDmaBufModifiersEXT
|
||||||
|
-eglExportDMABUFImageQueryMESA
|
||||||
|
-eglExportDMABUFImageMESA
|
||||||
-eglDebugMessageControlKHR
|
-eglDebugMessageControlKHR
|
||||||
-glDebugMessageCallbackKHR
|
-glDebugMessageCallbackKHR
|
||||||
-glDebugMessageControlKHR
|
-glDebugMessageControlKHR
|
||||||
|
|
|
@ -118,18 +118,22 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer,
|
||||||
struct wlr_gles2_texture *texture =
|
struct wlr_gles2_texture *texture =
|
||||||
get_gles2_texture_in_context(wlr_texture);
|
get_gles2_texture_in_context(wlr_texture);
|
||||||
|
|
||||||
GLuint prog = 0;
|
struct wlr_gles2_tex_shader *shader = NULL;
|
||||||
GLenum target = 0;
|
GLenum target = 0;
|
||||||
|
|
||||||
switch (texture->type) {
|
switch (texture->type) {
|
||||||
case WLR_GLES2_TEXTURE_GLTEX:
|
case WLR_GLES2_TEXTURE_GLTEX:
|
||||||
case WLR_GLES2_TEXTURE_WL_DRM_GL:
|
case WLR_GLES2_TEXTURE_WL_DRM_GL:
|
||||||
prog = texture->has_alpha ? renderer->shaders.tex_rgba :
|
if (texture->has_alpha) {
|
||||||
renderer->shaders.tex_rgbx;
|
shader = &renderer->shaders.tex_rgba;
|
||||||
|
} else {
|
||||||
|
shader = &renderer->shaders.tex_rgbx;
|
||||||
|
}
|
||||||
target = GL_TEXTURE_2D;
|
target = GL_TEXTURE_2D;
|
||||||
break;
|
break;
|
||||||
case WLR_GLES2_TEXTURE_WL_DRM_EXT:
|
case WLR_GLES2_TEXTURE_WL_DRM_EXT:
|
||||||
case WLR_GLES2_TEXTURE_DMABUF:
|
case WLR_GLES2_TEXTURE_DMABUF:
|
||||||
prog = renderer->shaders.tex_ext;
|
shader = &renderer->shaders.tex_ext;
|
||||||
target = GL_TEXTURE_EXTERNAL_OES;
|
target = GL_TEXTURE_EXTERNAL_OES;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -149,11 +153,12 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer,
|
||||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
glUseProgram(prog);
|
glUseProgram(shader->program);
|
||||||
|
|
||||||
glUniformMatrix3fv(0, 1, GL_FALSE, transposition);
|
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, transposition);
|
||||||
glUniform1i(1, texture->inverted_y);
|
glUniform1i(shader->invert_y, texture->inverted_y);
|
||||||
glUniform1f(3, alpha);
|
glUniform1i(shader->tex, 0);
|
||||||
|
glUniform1f(shader->alpha, alpha);
|
||||||
|
|
||||||
draw_quad();
|
draw_quad();
|
||||||
|
|
||||||
|
@ -173,9 +178,10 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer,
|
||||||
wlr_matrix_transpose(transposition, matrix);
|
wlr_matrix_transpose(transposition, matrix);
|
||||||
|
|
||||||
PUSH_GLES2_DEBUG;
|
PUSH_GLES2_DEBUG;
|
||||||
glUseProgram(renderer->shaders.quad);
|
glUseProgram(renderer->shaders.quad.program);
|
||||||
glUniformMatrix3fv(0, 1, GL_FALSE, transposition);
|
|
||||||
glUniform4f(1, color[0], color[1], color[2], color[3]);
|
glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition);
|
||||||
|
glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]);
|
||||||
draw_quad();
|
draw_quad();
|
||||||
POP_GLES2_DEBUG;
|
POP_GLES2_DEBUG;
|
||||||
}
|
}
|
||||||
|
@ -191,9 +197,10 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer,
|
||||||
wlr_matrix_transpose(transposition, matrix);
|
wlr_matrix_transpose(transposition, matrix);
|
||||||
|
|
||||||
PUSH_GLES2_DEBUG;
|
PUSH_GLES2_DEBUG;
|
||||||
glUseProgram(renderer->shaders.ellipse);
|
glUseProgram(renderer->shaders.ellipse.program);
|
||||||
glUniformMatrix3fv(0, 1, GL_FALSE, transposition);
|
|
||||||
glUniform4f(1, color[0], color[1], color[2], color[3]);
|
glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition);
|
||||||
|
glUniform4f(renderer->shaders.ellipse.color, color[0], color[1], color[2], color[3]);
|
||||||
draw_quad();
|
draw_quad();
|
||||||
POP_GLES2_DEBUG;
|
POP_GLES2_DEBUG;
|
||||||
}
|
}
|
||||||
|
@ -313,11 +320,11 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) {
|
||||||
wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL);
|
wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL);
|
||||||
|
|
||||||
PUSH_GLES2_DEBUG;
|
PUSH_GLES2_DEBUG;
|
||||||
glDeleteProgram(renderer->shaders.quad);
|
glDeleteProgram(renderer->shaders.quad.program);
|
||||||
glDeleteProgram(renderer->shaders.ellipse);
|
glDeleteProgram(renderer->shaders.ellipse.program);
|
||||||
glDeleteProgram(renderer->shaders.tex_rgba);
|
glDeleteProgram(renderer->shaders.tex_rgba.program);
|
||||||
glDeleteProgram(renderer->shaders.tex_rgbx);
|
glDeleteProgram(renderer->shaders.tex_rgbx.program);
|
||||||
glDeleteProgram(renderer->shaders.tex_ext);
|
glDeleteProgram(renderer->shaders.tex_ext.program);
|
||||||
POP_GLES2_DEBUG;
|
POP_GLES2_DEBUG;
|
||||||
|
|
||||||
if (glDebugMessageCallbackKHR) {
|
if (glDebugMessageCallbackKHR) {
|
||||||
|
@ -486,31 +493,53 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
||||||
|
|
||||||
PUSH_GLES2_DEBUG;
|
PUSH_GLES2_DEBUG;
|
||||||
|
|
||||||
renderer->shaders.quad = link_program(quad_vertex_src, quad_fragment_src);
|
GLuint prog;
|
||||||
if (!renderer->shaders.quad) {
|
renderer->shaders.quad.program = prog =
|
||||||
|
link_program(quad_vertex_src, quad_fragment_src);
|
||||||
|
if (!renderer->shaders.quad.program) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
renderer->shaders.ellipse =
|
renderer->shaders.quad.proj = glGetUniformLocation(prog, "proj");
|
||||||
|
renderer->shaders.quad.color = glGetUniformLocation(prog, "color");
|
||||||
|
|
||||||
|
renderer->shaders.ellipse.program = prog =
|
||||||
link_program(quad_vertex_src, ellipse_fragment_src);
|
link_program(quad_vertex_src, ellipse_fragment_src);
|
||||||
if (!renderer->shaders.ellipse) {
|
if (!renderer->shaders.ellipse.program) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
renderer->shaders.tex_rgba =
|
renderer->shaders.ellipse.proj = glGetUniformLocation(prog, "proj");
|
||||||
|
renderer->shaders.ellipse.color = glGetUniformLocation(prog, "color");
|
||||||
|
|
||||||
|
renderer->shaders.tex_rgba.program = prog =
|
||||||
link_program(tex_vertex_src, tex_fragment_src_rgba);
|
link_program(tex_vertex_src, tex_fragment_src_rgba);
|
||||||
if (!renderer->shaders.tex_rgba) {
|
if (!renderer->shaders.tex_rgba.program) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
renderer->shaders.tex_rgbx =
|
renderer->shaders.tex_rgba.proj = glGetUniformLocation(prog, "proj");
|
||||||
|
renderer->shaders.tex_rgba.invert_y = glGetUniformLocation(prog, "invert_y");
|
||||||
|
renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex");
|
||||||
|
renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha");
|
||||||
|
|
||||||
|
renderer->shaders.tex_rgbx.program = prog =
|
||||||
link_program(tex_vertex_src, tex_fragment_src_rgbx);
|
link_program(tex_vertex_src, tex_fragment_src_rgbx);
|
||||||
if (!renderer->shaders.tex_rgbx) {
|
if (!renderer->shaders.tex_rgbx.program) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
renderer->shaders.tex_rgbx.proj = glGetUniformLocation(prog, "proj");
|
||||||
|
renderer->shaders.tex_rgbx.invert_y = glGetUniformLocation(prog, "invert_y");
|
||||||
|
renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex");
|
||||||
|
renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha");
|
||||||
|
|
||||||
if (glEGLImageTargetTexture2DOES) {
|
if (glEGLImageTargetTexture2DOES) {
|
||||||
renderer->shaders.tex_ext =
|
renderer->shaders.tex_ext.program = prog =
|
||||||
link_program(tex_vertex_src, tex_fragment_src_external);
|
link_program(tex_vertex_src, tex_fragment_src_external);
|
||||||
if (!renderer->shaders.tex_ext) {
|
if (!renderer->shaders.tex_ext.program) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
renderer->shaders.tex_ext.proj = glGetUniformLocation(prog, "proj");
|
||||||
|
renderer->shaders.tex_ext.invert_y = glGetUniformLocation(prog, "invert_y");
|
||||||
|
renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex");
|
||||||
|
renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha");
|
||||||
}
|
}
|
||||||
|
|
||||||
POP_GLES2_DEBUG;
|
POP_GLES2_DEBUG;
|
||||||
|
@ -518,11 +547,11 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
|
||||||
return &renderer->wlr_renderer;
|
return &renderer->wlr_renderer;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
glDeleteProgram(renderer->shaders.quad);
|
glDeleteProgram(renderer->shaders.quad.program);
|
||||||
glDeleteProgram(renderer->shaders.ellipse);
|
glDeleteProgram(renderer->shaders.ellipse.program);
|
||||||
glDeleteProgram(renderer->shaders.tex_rgba);
|
glDeleteProgram(renderer->shaders.tex_rgba.program);
|
||||||
glDeleteProgram(renderer->shaders.tex_rgbx);
|
glDeleteProgram(renderer->shaders.tex_rgbx.program);
|
||||||
glDeleteProgram(renderer->shaders.tex_ext);
|
glDeleteProgram(renderer->shaders.tex_ext.program);
|
||||||
|
|
||||||
POP_GLES2_DEBUG;
|
POP_GLES2_DEBUG;
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,34 @@ static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool gles2_texture_to_dmabuf(struct wlr_texture *wlr_texture,
|
||||||
|
struct wlr_dmabuf_attributes *attribs) {
|
||||||
|
struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture);
|
||||||
|
|
||||||
|
if (!texture->image) {
|
||||||
|
assert(texture->type == WLR_GLES2_TEXTURE_GLTEX);
|
||||||
|
|
||||||
|
if (!eglCreateImageKHR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture->image = eglCreateImageKHR(texture->egl->display,
|
||||||
|
texture->egl->context, EGL_GL_TEXTURE_2D_KHR,
|
||||||
|
(EGLClientBuffer)(uintptr_t)texture->gl_tex, NULL);
|
||||||
|
if (texture->image == EGL_NO_IMAGE_KHR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t flags = 0;
|
||||||
|
if (texture->inverted_y) {
|
||||||
|
flags |= WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wlr_egl_export_image_to_dmabuf(texture->egl, texture->image,
|
||||||
|
texture->width, texture->height, flags, attribs);
|
||||||
|
}
|
||||||
|
|
||||||
static void gles2_texture_destroy(struct wlr_texture *wlr_texture) {
|
static void gles2_texture_destroy(struct wlr_texture *wlr_texture) {
|
||||||
if (wlr_texture == NULL) {
|
if (wlr_texture == NULL) {
|
||||||
return;
|
return;
|
||||||
|
@ -102,6 +130,7 @@ static void gles2_texture_destroy(struct wlr_texture *wlr_texture) {
|
||||||
static const struct wlr_texture_impl texture_impl = {
|
static const struct wlr_texture_impl texture_impl = {
|
||||||
.get_size = gles2_texture_get_size,
|
.get_size = gles2_texture_get_size,
|
||||||
.write_pixels = gles2_texture_write_pixels,
|
.write_pixels = gles2_texture_write_pixels,
|
||||||
|
.to_dmabuf = gles2_texture_to_dmabuf,
|
||||||
.destroy = gles2_texture_destroy,
|
.destroy = gles2_texture_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ glapi = custom_target('glapi',
|
||||||
lib_wlr_render = static_library(
|
lib_wlr_render = static_library(
|
||||||
'wlr_render',
|
'wlr_render',
|
||||||
files(
|
files(
|
||||||
|
'dmabuf.c',
|
||||||
'egl.c',
|
'egl.c',
|
||||||
'gles2/pixel_format.c',
|
'gles2/pixel_format.c',
|
||||||
'gles2/renderer.c',
|
'gles2/renderer.c',
|
||||||
|
|
|
@ -54,3 +54,11 @@ bool wlr_texture_write_pixels(struct wlr_texture *texture,
|
||||||
return texture->impl->write_pixels(texture, wl_fmt, stride, width, height,
|
return texture->impl->write_pixels(texture, wl_fmt, stride, width, height,
|
||||||
src_x, src_y, dst_x, dst_y, data);
|
src_x, src_y, dst_x, dst_y, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wlr_texture_to_dmabuf(struct wlr_texture *texture,
|
||||||
|
struct wlr_dmabuf_attributes *attribs) {
|
||||||
|
if (!texture->impl->to_dmabuf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return texture->impl->to_dmabuf(texture, attribs);
|
||||||
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
#include <wlr/types/wlr_box.h>
|
#include <wlr/types/wlr_box.h>
|
||||||
#include <wlr/types/wlr_compositor.h>
|
#include <wlr/types/wlr_compositor.h>
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
|
#include <wlr/types/wlr_export_dmabuf_v1.h>
|
||||||
#include <wlr/types/wlr_gamma_control.h>
|
#include <wlr/types/wlr_gamma_control.h>
|
||||||
#include <wlr/types/wlr_idle.h>
|
|
||||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
||||||
|
#include <wlr/types/wlr_idle.h>
|
||||||
#include <wlr/types/wlr_input_inhibitor.h>
|
#include <wlr/types/wlr_input_inhibitor.h>
|
||||||
#include <wlr/types/wlr_layer_shell.h>
|
#include <wlr/types/wlr_layer_shell.h>
|
||||||
#include <wlr/types/wlr_linux_dmabuf.h>
|
#include <wlr/types/wlr_linux_dmabuf.h>
|
||||||
|
@ -18,9 +19,9 @@
|
||||||
#include <wlr/types/wlr_server_decoration.h>
|
#include <wlr/types/wlr_server_decoration.h>
|
||||||
#include <wlr/types/wlr_wl_shell.h>
|
#include <wlr/types/wlr_wl_shell.h>
|
||||||
#include <wlr/types/wlr_xcursor_manager.h>
|
#include <wlr/types/wlr_xcursor_manager.h>
|
||||||
|
#include <wlr/types/wlr_xdg_output.h>
|
||||||
#include <wlr/types/wlr_xdg_shell_v6.h>
|
#include <wlr/types/wlr_xdg_shell_v6.h>
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/types/wlr_xdg_output.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "rootston/layers.h"
|
#include "rootston/layers.h"
|
||||||
#include "rootston/seat.h"
|
#include "rootston/seat.h"
|
||||||
|
@ -845,6 +846,8 @@ struct roots_desktop *desktop_create(struct roots_server *server,
|
||||||
desktop->gamma_control_manager = wlr_gamma_control_manager_create(
|
desktop->gamma_control_manager = wlr_gamma_control_manager_create(
|
||||||
server->wl_display);
|
server->wl_display);
|
||||||
desktop->screenshooter = wlr_screenshooter_create(server->wl_display);
|
desktop->screenshooter = wlr_screenshooter_create(server->wl_display);
|
||||||
|
desktop->export_dmabuf_manager_v1 =
|
||||||
|
wlr_export_dmabuf_manager_v1_create(server->wl_display);
|
||||||
desktop->server_decoration_manager =
|
desktop->server_decoration_manager =
|
||||||
wlr_server_decoration_manager_create(server->wl_display);
|
wlr_server_decoration_manager_create(server->wl_display);
|
||||||
wlr_server_decoration_manager_set_default_mode(
|
wlr_server_decoration_manager_set_default_mode(
|
||||||
|
|
|
@ -189,7 +189,8 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy,
|
||||||
struct roots_output *output = data->output;
|
struct roots_output *output = data->output;
|
||||||
float rotation = data->layout.rotation;
|
float rotation = data->layout.rotation;
|
||||||
|
|
||||||
if (!wlr_surface_has_buffer(surface)) {
|
struct wlr_texture *texture = wlr_surface_get_texture(surface);
|
||||||
|
if (texture == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,8 +231,7 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy,
|
||||||
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
|
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
|
||||||
for (int i = 0; i < nrects; ++i) {
|
for (int i = 0; i < nrects; ++i) {
|
||||||
scissor_output(output, &rects[i]);
|
scissor_output(output, &rects[i]);
|
||||||
wlr_render_texture_with_matrix(renderer, surface->texture, matrix,
|
wlr_render_texture_with_matrix(renderer, texture, matrix, data->alpha);
|
||||||
data->alpha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
damage_finish:
|
damage_finish:
|
||||||
|
|
|
@ -20,8 +20,10 @@ lib_wlr_types = static_library(
|
||||||
'xdg_shell_v6/wlr_xdg_surface_v6.c',
|
'xdg_shell_v6/wlr_xdg_surface_v6.c',
|
||||||
'xdg_shell_v6/wlr_xdg_toplevel_v6.c',
|
'xdg_shell_v6/wlr_xdg_toplevel_v6.c',
|
||||||
'wlr_box.c',
|
'wlr_box.c',
|
||||||
|
'wlr_buffer.c',
|
||||||
'wlr_compositor.c',
|
'wlr_compositor.c',
|
||||||
'wlr_cursor.c',
|
'wlr_cursor.c',
|
||||||
|
'wlr_export_dmabuf_v1.c',
|
||||||
'wlr_gamma_control.c',
|
'wlr_gamma_control.c',
|
||||||
'wlr_idle_inhibit_v1.c',
|
'wlr_idle_inhibit_v1.c',
|
||||||
'wlr_idle.c',
|
'wlr_idle.c',
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <wlr/types/wlr_buffer.h>
|
||||||
|
#include <wlr/types/wlr_linux_dmabuf.h>
|
||||||
|
#include <wlr/render/wlr_renderer.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
|
bool wlr_resource_is_buffer(struct wl_resource *resource) {
|
||||||
|
return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wlr_buffer_get_resource_size(struct wl_resource *resource,
|
||||||
|
struct wlr_renderer *renderer, int *width, int *height) {
|
||||||
|
assert(wlr_resource_is_buffer(resource));
|
||||||
|
|
||||||
|
struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource);
|
||||||
|
if (shm_buf != NULL) {
|
||||||
|
*width = wl_shm_buffer_get_width(shm_buf);
|
||||||
|
*height = wl_shm_buffer_get_height(shm_buf);
|
||||||
|
} else if (wlr_renderer_resource_is_wl_drm_buffer(renderer,
|
||||||
|
resource)) {
|
||||||
|
wlr_renderer_wl_drm_buffer_get_size(renderer, resource,
|
||||||
|
width, height);
|
||||||
|
} else if (wlr_dmabuf_resource_is_buffer(resource)) {
|
||||||
|
struct wlr_dmabuf_buffer *dmabuf =
|
||||||
|
wlr_dmabuf_buffer_from_buffer_resource(resource);
|
||||||
|
*width = dmabuf->attributes.width;
|
||||||
|
*height = dmabuf->attributes.height;
|
||||||
|
} else {
|
||||||
|
*width = *height = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void buffer_resource_handle_destroy(struct wl_listener *listener,
|
||||||
|
void *data) {
|
||||||
|
struct wlr_buffer *buffer =
|
||||||
|
wl_container_of(listener, buffer, resource_destroy);
|
||||||
|
wl_list_remove(&buffer->resource_destroy.link);
|
||||||
|
wl_list_init(&buffer->resource_destroy.link);
|
||||||
|
buffer->resource = NULL;
|
||||||
|
|
||||||
|
// At this point, if the wl_buffer comes from linux-dmabuf or wl_drm, we
|
||||||
|
// still haven't released it (ie. we'll read it in the future) but the
|
||||||
|
// client destroyed it. Reading the texture itself should be fine because
|
||||||
|
// we still hold a reference to the DMA-BUF via the texture. However the
|
||||||
|
// client could decide to re-use the same DMA-BUF for something else, in
|
||||||
|
// which case we'll read garbage. We decide to accept this risk.
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer,
|
||||||
|
struct wl_resource *resource) {
|
||||||
|
assert(wlr_resource_is_buffer(resource));
|
||||||
|
|
||||||
|
struct wlr_texture *texture = NULL;
|
||||||
|
bool released = false;
|
||||||
|
|
||||||
|
struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource);
|
||||||
|
if (shm_buf != NULL) {
|
||||||
|
enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf);
|
||||||
|
int32_t stride = wl_shm_buffer_get_stride(shm_buf);
|
||||||
|
int32_t width = wl_shm_buffer_get_width(shm_buf);
|
||||||
|
int32_t height = wl_shm_buffer_get_height(shm_buf);
|
||||||
|
|
||||||
|
wl_shm_buffer_begin_access(shm_buf);
|
||||||
|
void *data = wl_shm_buffer_get_data(shm_buf);
|
||||||
|
texture = wlr_texture_from_pixels(renderer, fmt, stride,
|
||||||
|
width, height, data);
|
||||||
|
wl_shm_buffer_end_access(shm_buf);
|
||||||
|
|
||||||
|
// We have uploaded the data, we don't need to access the wl_buffer
|
||||||
|
// anymore
|
||||||
|
wl_buffer_send_release(resource);
|
||||||
|
released = true;
|
||||||
|
} else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, resource)) {
|
||||||
|
texture = wlr_texture_from_wl_drm(renderer, resource);
|
||||||
|
} else if (wlr_dmabuf_resource_is_buffer(resource)) {
|
||||||
|
struct wlr_dmabuf_buffer *dmabuf =
|
||||||
|
wlr_dmabuf_buffer_from_buffer_resource(resource);
|
||||||
|
texture = wlr_texture_from_dmabuf(renderer, &dmabuf->attributes);
|
||||||
|
|
||||||
|
// We have imported the DMA-BUF, but we need to prevent the client from
|
||||||
|
// re-using the same DMA-BUF for the next frames, so we don't release
|
||||||
|
// the buffer yet.
|
||||||
|
} else {
|
||||||
|
wlr_log(L_ERROR, "Cannot upload texture: unknown buffer type");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture == NULL) {
|
||||||
|
wlr_log(L_ERROR, "Failed to upload texture");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer));
|
||||||
|
if (buffer == NULL) {
|
||||||
|
wlr_texture_destroy(texture);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buffer->resource = resource;
|
||||||
|
buffer->texture = texture;
|
||||||
|
buffer->released = released;
|
||||||
|
buffer->n_refs = 1;
|
||||||
|
|
||||||
|
wl_resource_add_destroy_listener(resource, &buffer->resource_destroy);
|
||||||
|
buffer->resource_destroy.notify = buffer_resource_handle_destroy;
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) {
|
||||||
|
buffer->n_refs++;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wlr_buffer_unref(struct wlr_buffer *buffer) {
|
||||||
|
if (buffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(buffer->n_refs > 0);
|
||||||
|
buffer->n_refs--;
|
||||||
|
if (buffer->n_refs > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffer->released && buffer->resource != NULL) {
|
||||||
|
wl_buffer_send_release(buffer->resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_remove(&buffer->resource_destroy.link);
|
||||||
|
wlr_texture_destroy(buffer->texture);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer,
|
||||||
|
struct wl_resource *resource, pixman_region32_t *damage) {
|
||||||
|
assert(wlr_resource_is_buffer(resource));
|
||||||
|
|
||||||
|
if (buffer->n_refs > 1) {
|
||||||
|
// Someone else still has a reference to the buffer
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource);
|
||||||
|
if (shm_buf == NULL) {
|
||||||
|
// Uploading only damaged regions only works for wl_shm buffers
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf);
|
||||||
|
int32_t stride = wl_shm_buffer_get_stride(shm_buf);
|
||||||
|
int32_t width = wl_shm_buffer_get_width(shm_buf);
|
||||||
|
int32_t height = wl_shm_buffer_get_height(shm_buf);
|
||||||
|
|
||||||
|
int32_t texture_width, texture_height;
|
||||||
|
wlr_texture_get_size(buffer->texture, &texture_width, &texture_height);
|
||||||
|
if (width != texture_width || height != texture_height) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_shm_buffer_begin_access(shm_buf);
|
||||||
|
void *data = wl_shm_buffer_get_data(shm_buf);
|
||||||
|
|
||||||
|
int n;
|
||||||
|
pixman_box32_t *rects = pixman_region32_rectangles(damage, &n);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
pixman_box32_t *r = &rects[i];
|
||||||
|
if (!wlr_texture_write_pixels(buffer->texture, fmt, stride,
|
||||||
|
r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1,
|
||||||
|
r->x1, r->y1, data)) {
|
||||||
|
wl_shm_buffer_end_access(shm_buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_shm_buffer_end_access(shm_buf);
|
||||||
|
|
||||||
|
// We have uploaded the data, we don't need to access the wl_buffer
|
||||||
|
// anymore
|
||||||
|
wl_buffer_send_release(resource);
|
||||||
|
|
||||||
|
wl_list_remove(&buffer->resource_destroy.link);
|
||||||
|
wl_resource_add_destroy_listener(resource, &buffer->resource_destroy);
|
||||||
|
buffer->resource_destroy.notify = buffer_resource_handle_destroy;
|
||||||
|
|
||||||
|
buffer->resource = resource;
|
||||||
|
buffer->released = true;
|
||||||
|
return buffer;
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/types/wlr_export_dmabuf_v1.h>
|
||||||
|
#include <wlr/types/wlr_linux_dmabuf.h>
|
||||||
|
#include <wlr/types/wlr_output.h>
|
||||||
|
#include "wlr-export-dmabuf-unstable-v1-protocol.h"
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
|
#define EXPORT_DMABUF_MANAGER_VERSION 1
|
||||||
|
|
||||||
|
|
||||||
|
static const struct zwlr_export_dmabuf_frame_v1_interface frame_impl;
|
||||||
|
|
||||||
|
static struct wlr_export_dmabuf_frame_v1 *frame_from_resource(
|
||||||
|
struct wl_resource *resource) {
|
||||||
|
assert(wl_resource_instance_of(resource,
|
||||||
|
&zwlr_export_dmabuf_frame_v1_interface, &frame_impl));
|
||||||
|
return wl_resource_get_user_data(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_handle_destroy(struct wl_client *client,
|
||||||
|
struct wl_resource *resource) {
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwlr_export_dmabuf_frame_v1_interface frame_impl = {
|
||||||
|
.destroy = frame_handle_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void frame_handle_resource_destroy(struct wl_resource *resource) {
|
||||||
|
struct wlr_export_dmabuf_frame_v1 *frame = frame_from_resource(resource);
|
||||||
|
wl_list_remove(&frame->link);
|
||||||
|
wl_list_remove(&frame->output_swap_buffers.link);
|
||||||
|
wlr_dmabuf_attributes_finish(&frame->attribs);
|
||||||
|
free(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_output_handle_swap_buffers(struct wl_listener *listener,
|
||||||
|
void *data) {
|
||||||
|
struct wlr_export_dmabuf_frame_v1 *frame =
|
||||||
|
wl_container_of(listener, frame, output_swap_buffers);
|
||||||
|
struct wlr_output_event_swap_buffers *event = data;
|
||||||
|
|
||||||
|
wl_list_remove(&frame->output_swap_buffers.link);
|
||||||
|
wl_list_init(&frame->output_swap_buffers.link);
|
||||||
|
|
||||||
|
uint32_t tv_sec_hi = event->when->tv_sec >> 32;
|
||||||
|
uint32_t tv_sec_lo = event->when->tv_sec & 0xFFFFFFFF;
|
||||||
|
zwlr_export_dmabuf_frame_v1_send_ready(frame->resource,
|
||||||
|
tv_sec_hi, tv_sec_lo, event->when->tv_nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl;
|
||||||
|
|
||||||
|
static struct wlr_export_dmabuf_manager_v1 *manager_from_resource(
|
||||||
|
struct wl_resource *resource) {
|
||||||
|
assert(wl_resource_instance_of(resource,
|
||||||
|
&zwlr_export_dmabuf_manager_v1_interface, &manager_impl));
|
||||||
|
return wl_resource_get_user_data(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void manager_handle_capture_output(struct wl_client *client,
|
||||||
|
struct wl_resource *manager_resource, uint32_t id,
|
||||||
|
int32_t overlay_cursor, struct wl_resource *output_resource) {
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *manager =
|
||||||
|
manager_from_resource(manager_resource);
|
||||||
|
struct wlr_output *output = wlr_output_from_resource(output_resource);
|
||||||
|
|
||||||
|
struct wlr_export_dmabuf_frame_v1 *frame =
|
||||||
|
calloc(1, sizeof(struct wlr_export_dmabuf_frame_v1));
|
||||||
|
if (frame == NULL) {
|
||||||
|
wl_resource_post_no_memory(manager_resource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frame->manager = manager;
|
||||||
|
frame->output = output;
|
||||||
|
wl_list_init(&frame->output_swap_buffers.link);
|
||||||
|
|
||||||
|
uint32_t version = wl_resource_get_version(manager_resource);
|
||||||
|
frame->resource = wl_resource_create(client,
|
||||||
|
&zwlr_export_dmabuf_frame_v1_interface, version, id);
|
||||||
|
if (frame->resource == NULL) {
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(frame->resource, &frame_impl, frame,
|
||||||
|
frame_handle_resource_destroy);
|
||||||
|
|
||||||
|
wl_list_insert(&manager->frames, &frame->link);
|
||||||
|
|
||||||
|
if (!output->impl->export_dmabuf) {
|
||||||
|
zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource,
|
||||||
|
ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_dmabuf_attributes *attribs = &frame->attribs;
|
||||||
|
if (!wlr_output_export_dmabuf(output, attribs)) {
|
||||||
|
zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource,
|
||||||
|
ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t frame_flags = ZWLR_EXPORT_DMABUF_FRAME_V1_FLAGS_TRANSIENT;
|
||||||
|
uint32_t mod_high = attribs->modifier >> 32;
|
||||||
|
uint32_t mod_low = attribs->modifier & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
zwlr_export_dmabuf_frame_v1_send_frame(frame->resource,
|
||||||
|
output->width, output->height, 0, 0, attribs->flags, frame_flags,
|
||||||
|
attribs->format, mod_high, mod_low, attribs->n_planes);
|
||||||
|
|
||||||
|
for (int i = 0; i < attribs->n_planes; ++i) {
|
||||||
|
off_t size = lseek(attribs->fd[i], 0, SEEK_END);
|
||||||
|
|
||||||
|
zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i,
|
||||||
|
attribs->fd[i], size, attribs->offset[i], attribs->stride[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_remove(&frame->output_swap_buffers.link);
|
||||||
|
wl_signal_add(&output->events.swap_buffers, &frame->output_swap_buffers);
|
||||||
|
frame->output_swap_buffers.notify = frame_output_handle_swap_buffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl = {
|
||||||
|
.capture_output = manager_handle_capture_output,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void manager_handle_resource_destroy(struct wl_resource *resource) {
|
||||||
|
wl_list_remove(wl_resource_get_link(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void manager_bind(struct wl_client *client, void *data, uint32_t version,
|
||||||
|
uint32_t id) {
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *manager = data;
|
||||||
|
|
||||||
|
struct wl_resource *resource = wl_resource_create(client,
|
||||||
|
&zwlr_export_dmabuf_manager_v1_interface, version, id);
|
||||||
|
if (resource == NULL) {
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_resource_set_implementation(resource, &manager_impl, manager,
|
||||||
|
manager_handle_resource_destroy);
|
||||||
|
|
||||||
|
wl_list_insert(&manager->resources, wl_resource_get_link(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *manager =
|
||||||
|
wl_container_of(listener, manager, display_destroy);
|
||||||
|
wlr_export_dmabuf_manager_v1_destroy(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create(
|
||||||
|
struct wl_display *display) {
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *manager =
|
||||||
|
calloc(1, sizeof(struct wlr_export_dmabuf_manager_v1));
|
||||||
|
if (manager == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
wl_list_init(&manager->resources);
|
||||||
|
wl_list_init(&manager->frames);
|
||||||
|
|
||||||
|
manager->global = wl_global_create(display,
|
||||||
|
&zwlr_export_dmabuf_manager_v1_interface, EXPORT_DMABUF_MANAGER_VERSION,
|
||||||
|
manager, manager_bind);
|
||||||
|
if (manager->global == NULL) {
|
||||||
|
free(manager);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager->display_destroy.notify = handle_display_destroy;
|
||||||
|
wl_display_add_destroy_listener(display, &manager->display_destroy);
|
||||||
|
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wlr_export_dmabuf_manager_v1_destroy(
|
||||||
|
struct wlr_export_dmabuf_manager_v1 *manager) {
|
||||||
|
if (manager == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_list_remove(&manager->display_destroy.link);
|
||||||
|
wl_global_destroy(manager->global);
|
||||||
|
struct wl_resource *resource, *resource_tmp;
|
||||||
|
wl_resource_for_each_safe(resource, resource_tmp, &manager->resources) {
|
||||||
|
wl_resource_destroy(resource);
|
||||||
|
}
|
||||||
|
struct wlr_export_dmabuf_frame_v1 *frame, *frame_tmp;
|
||||||
|
wl_list_for_each_safe(frame, frame_tmp, &manager->frames, link) {
|
||||||
|
wl_resource_destroy(frame->resource);
|
||||||
|
}
|
||||||
|
free(manager);
|
||||||
|
}
|
|
@ -176,7 +176,9 @@ static void layer_surface_unmap(struct wlr_layer_surface *surface) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void layer_surface_destroy(struct wlr_layer_surface *surface) {
|
static void layer_surface_destroy(struct wlr_layer_surface *surface) {
|
||||||
|
if (surface->configured && surface->mapped) {
|
||||||
layer_surface_unmap(surface);
|
layer_surface_unmap(surface);
|
||||||
|
}
|
||||||
wlr_signal_emit_safe(&surface->events.destroy, surface);
|
wlr_signal_emit_safe(&surface->events.destroy, surface);
|
||||||
wl_resource_set_user_data(surface->resource, NULL);
|
wl_resource_set_user_data(surface->resource, NULL);
|
||||||
wl_list_remove(&surface->surface_destroy_listener.link);
|
wl_list_remove(&surface->surface_destroy_listener.link);
|
||||||
|
|
|
@ -52,11 +52,7 @@ struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_buffer_resource(
|
||||||
}
|
}
|
||||||
|
|
||||||
static void linux_dmabuf_buffer_destroy(struct wlr_dmabuf_buffer *buffer) {
|
static void linux_dmabuf_buffer_destroy(struct wlr_dmabuf_buffer *buffer) {
|
||||||
for (int i = 0; i < buffer->attributes.n_planes; i++) {
|
wlr_dmabuf_attributes_finish(&buffer->attributes);
|
||||||
close(buffer->attributes.fd[i]);
|
|
||||||
buffer->attributes.fd[i] = -1;
|
|
||||||
}
|
|
||||||
buffer->attributes.n_planes = 0;
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -367,7 +367,8 @@ static void output_fullscreen_surface_render(struct wlr_output *output,
|
||||||
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
|
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
|
||||||
assert(renderer);
|
assert(renderer);
|
||||||
|
|
||||||
if (!wlr_surface_has_buffer(surface)) {
|
struct wlr_texture *texture = wlr_surface_get_texture(surface);
|
||||||
|
if (texture == NULL) {
|
||||||
wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1});
|
wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -386,8 +387,7 @@ static void output_fullscreen_surface_render(struct wlr_output *output,
|
||||||
for (int i = 0; i < nrects; ++i) {
|
for (int i = 0; i < nrects; ++i) {
|
||||||
output_scissor(output, &rects[i]);
|
output_scissor(output, &rects[i]);
|
||||||
wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1});
|
wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1});
|
||||||
wlr_render_texture_with_matrix(surface->renderer, surface->texture,
|
wlr_render_texture_with_matrix(surface->renderer, texture, matrix, 1.0f);
|
||||||
matrix, 1.0f);
|
|
||||||
}
|
}
|
||||||
wlr_renderer_scissor(renderer, NULL);
|
wlr_renderer_scissor(renderer, NULL);
|
||||||
|
|
||||||
|
@ -418,7 +418,7 @@ static void output_cursor_render(struct wlr_output_cursor *cursor,
|
||||||
|
|
||||||
struct wlr_texture *texture = cursor->texture;
|
struct wlr_texture *texture = cursor->texture;
|
||||||
if (cursor->surface != NULL) {
|
if (cursor->surface != NULL) {
|
||||||
texture = cursor->surface->texture;
|
texture = wlr_surface_get_texture(cursor->surface);
|
||||||
}
|
}
|
||||||
if (texture == NULL) {
|
if (texture == NULL) {
|
||||||
return;
|
return;
|
||||||
|
@ -467,7 +467,12 @@ bool wlr_output_swap_buffers(struct wlr_output *output, struct timespec *when,
|
||||||
output->idle_frame = NULL;
|
output->idle_frame = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_signal_emit_safe(&output->events.swap_buffers, damage);
|
struct wlr_output_event_swap_buffers event = {
|
||||||
|
.output = output,
|
||||||
|
.when = when,
|
||||||
|
.damage = damage,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&output->events.swap_buffers, &event);
|
||||||
|
|
||||||
int width, height;
|
int width, height;
|
||||||
wlr_output_transformed_resolution(output, &width, &height);
|
wlr_output_transformed_resolution(output, &width, &height);
|
||||||
|
@ -560,6 +565,14 @@ uint32_t wlr_output_get_gamma_size(struct wlr_output *output) {
|
||||||
return output->impl->get_gamma_size(output);
|
return output->impl->get_gamma_size(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wlr_output_export_dmabuf(struct wlr_output *output,
|
||||||
|
struct wlr_dmabuf_attributes *attribs) {
|
||||||
|
if (!output->impl->export_dmabuf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return output->impl->export_dmabuf(output, attribs);
|
||||||
|
}
|
||||||
|
|
||||||
void wlr_output_update_needs_swap(struct wlr_output *output) {
|
void wlr_output_update_needs_swap(struct wlr_output *output) {
|
||||||
output->needs_swap = true;
|
output->needs_swap = true;
|
||||||
wlr_signal_emit_safe(&output->events.needs_swap, output);
|
wlr_signal_emit_safe(&output->events.needs_swap, output);
|
||||||
|
@ -700,7 +713,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) {
|
||||||
enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
struct wlr_texture *texture = cursor->texture;
|
struct wlr_texture *texture = cursor->texture;
|
||||||
if (cursor->surface != NULL) {
|
if (cursor->surface != NULL) {
|
||||||
texture = cursor->surface->texture;
|
texture = wlr_surface_get_texture(cursor->surface);
|
||||||
scale = cursor->surface->current->scale;
|
scale = cursor->surface->current->scale;
|
||||||
transform = cursor->surface->current->transform;
|
transform = cursor->surface->current->transform;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <wlr/render/interface.h>
|
#include <wlr/render/interface.h>
|
||||||
|
#include <wlr/types/wlr_buffer.h>
|
||||||
#include <wlr/types/wlr_compositor.h>
|
#include <wlr/types/wlr_compositor.h>
|
||||||
#include <wlr/types/wlr_linux_dmabuf.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_region.h>
|
#include <wlr/types/wlr_region.h>
|
||||||
#include <wlr/types/wlr_surface.h>
|
#include <wlr/types/wlr_surface.h>
|
||||||
|
@ -46,13 +45,6 @@ static void surface_handle_buffer_destroy(struct wl_listener *listener,
|
||||||
surface_state_reset_buffer(state);
|
surface_state_reset_buffer(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void surface_state_release_buffer(struct wlr_surface_state *state) {
|
|
||||||
if (state->buffer) {
|
|
||||||
wl_buffer_send_release(state->buffer);
|
|
||||||
surface_state_reset_buffer(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void surface_state_set_buffer(struct wlr_surface_state *state,
|
static void surface_state_set_buffer(struct wlr_surface_state *state,
|
||||||
struct wl_resource *buffer) {
|
struct wl_resource *buffer) {
|
||||||
surface_state_reset_buffer(state);
|
surface_state_reset_buffer(state);
|
||||||
|
@ -94,40 +86,25 @@ static void surface_damage(struct wl_client *client,
|
||||||
x, y, width, height);
|
x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_frame_callback *frame_callback_from_resource(
|
|
||||||
struct wl_resource *resource) {
|
|
||||||
assert(wl_resource_instance_of(resource, &wl_callback_interface, NULL));
|
|
||||||
return wl_resource_get_user_data(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void callback_handle_resource_destroy(struct wl_resource *resource) {
|
static void callback_handle_resource_destroy(struct wl_resource *resource) {
|
||||||
struct wlr_frame_callback *cb = frame_callback_from_resource(resource);
|
wl_list_remove(wl_resource_get_link(resource));
|
||||||
wl_list_remove(&cb->link);
|
|
||||||
free(cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void surface_frame(struct wl_client *client,
|
static void surface_frame(struct wl_client *client,
|
||||||
struct wl_resource *resource, uint32_t callback) {
|
struct wl_resource *resource, uint32_t callback) {
|
||||||
struct wlr_surface *surface = wlr_surface_from_resource(resource);
|
struct wlr_surface *surface = wlr_surface_from_resource(resource);
|
||||||
|
|
||||||
struct wlr_frame_callback *cb =
|
struct wl_resource *callback_resource = wl_resource_create(client,
|
||||||
calloc(1, sizeof(struct wlr_frame_callback));
|
&wl_callback_interface, CALLBACK_VERSION, callback);
|
||||||
if (cb == NULL) {
|
if (callback_resource == NULL) {
|
||||||
wl_resource_post_no_memory(resource);
|
wl_resource_post_no_memory(resource);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
wl_resource_set_implementation(callback_resource, NULL, NULL,
|
||||||
cb->resource = wl_resource_create(client, &wl_callback_interface,
|
|
||||||
CALLBACK_VERSION, callback);
|
|
||||||
if (cb->resource == NULL) {
|
|
||||||
free(cb);
|
|
||||||
wl_resource_post_no_memory(resource);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wl_resource_set_implementation(cb->resource, NULL, cb,
|
|
||||||
callback_handle_resource_destroy);
|
callback_handle_resource_destroy);
|
||||||
|
|
||||||
wl_list_insert(surface->pending->frame_callback_list.prev, &cb->link);
|
wl_list_insert(surface->pending->frame_callback_list.prev,
|
||||||
|
wl_resource_get_link(callback_resource));
|
||||||
|
|
||||||
surface->pending->invalid |= WLR_SURFACE_INVALID_FRAME_CALLBACK_LIST;
|
surface->pending->invalid |= WLR_SURFACE_INVALID_FRAME_CALLBACK_LIST;
|
||||||
}
|
}
|
||||||
|
@ -175,24 +152,8 @@ static bool surface_update_size(struct wlr_surface *surface,
|
||||||
int scale = state->scale;
|
int scale = state->scale;
|
||||||
enum wl_output_transform transform = state->transform;
|
enum wl_output_transform transform = state->transform;
|
||||||
|
|
||||||
struct wl_shm_buffer *buf = wl_shm_buffer_get(state->buffer);
|
wlr_buffer_get_resource_size(state->buffer, surface->renderer,
|
||||||
if (buf != NULL) {
|
|
||||||
state->buffer_width = wl_shm_buffer_get_width(buf);
|
|
||||||
state->buffer_height = wl_shm_buffer_get_height(buf);
|
|
||||||
} else if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer,
|
|
||||||
state->buffer)) {
|
|
||||||
wlr_renderer_wl_drm_buffer_get_size(surface->renderer, state->buffer,
|
|
||||||
&state->buffer_width, &state->buffer_height);
|
&state->buffer_width, &state->buffer_height);
|
||||||
} else if (wlr_dmabuf_resource_is_buffer(state->buffer)) {
|
|
||||||
struct wlr_dmabuf_buffer *dmabuf =
|
|
||||||
wlr_dmabuf_buffer_from_buffer_resource(state->buffer);
|
|
||||||
state->buffer_width = dmabuf->attributes.width;
|
|
||||||
state->buffer_height = dmabuf->attributes.height;
|
|
||||||
} else {
|
|
||||||
wlr_log(L_ERROR, "Unknown buffer handle attached");
|
|
||||||
state->buffer_width = 0;
|
|
||||||
state->buffer_height = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = state->buffer_width / scale;
|
int width = state->buffer_width / scale;
|
||||||
int height = state->buffer_height / scale;
|
int height = state->buffer_height / scale;
|
||||||
|
@ -243,7 +204,6 @@ static void surface_move_state(struct wlr_surface *surface,
|
||||||
update_size = true;
|
update_size = true;
|
||||||
}
|
}
|
||||||
if ((next->invalid & WLR_SURFACE_INVALID_BUFFER)) {
|
if ((next->invalid & WLR_SURFACE_INVALID_BUFFER)) {
|
||||||
surface_state_release_buffer(state);
|
|
||||||
surface_state_set_buffer(state, next->buffer);
|
surface_state_set_buffer(state, next->buffer);
|
||||||
surface_state_reset_buffer(next);
|
surface_state_reset_buffer(next);
|
||||||
state->sx = next->sx;
|
state->sx = next->sx;
|
||||||
|
@ -350,93 +310,54 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void surface_apply_damage(struct wlr_surface *surface,
|
static void surface_apply_damage(struct wlr_surface *surface) {
|
||||||
bool invalid_buffer, bool reupload_buffer) {
|
|
||||||
struct wl_resource *resource = surface->current->buffer;
|
struct wl_resource *resource = surface->current->buffer;
|
||||||
if (resource == NULL) {
|
if (resource == NULL) {
|
||||||
|
// NULL commit
|
||||||
|
wlr_buffer_unref(surface->buffer);
|
||||||
|
surface->buffer = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wl_shm_buffer *buf = wl_shm_buffer_get(resource);
|
if (surface->buffer != NULL && surface->buffer->released) {
|
||||||
if (buf != NULL) {
|
|
||||||
wl_shm_buffer_begin_access(buf);
|
|
||||||
|
|
||||||
enum wl_shm_format fmt = wl_shm_buffer_get_format(buf);
|
|
||||||
int32_t stride = wl_shm_buffer_get_stride(buf);
|
|
||||||
int32_t width = wl_shm_buffer_get_width(buf);
|
|
||||||
int32_t height = wl_shm_buffer_get_height(buf);
|
|
||||||
void *data = wl_shm_buffer_get_data(buf);
|
|
||||||
|
|
||||||
if (surface->texture == NULL || reupload_buffer) {
|
|
||||||
wlr_texture_destroy(surface->texture);
|
|
||||||
surface->texture = wlr_texture_from_pixels(surface->renderer, fmt,
|
|
||||||
stride, width, height, data);
|
|
||||||
} else {
|
|
||||||
pixman_region32_t damage;
|
pixman_region32_t damage;
|
||||||
pixman_region32_init(&damage);
|
pixman_region32_init(&damage);
|
||||||
pixman_region32_copy(&damage, &surface->current->buffer_damage);
|
pixman_region32_copy(&damage, &surface->current->buffer_damage);
|
||||||
pixman_region32_intersect_rect(&damage, &damage, 0, 0,
|
pixman_region32_intersect_rect(&damage, &damage, 0, 0,
|
||||||
surface->current->buffer_width,
|
surface->current->buffer_width, surface->current->buffer_height);
|
||||||
surface->current->buffer_height);
|
|
||||||
|
|
||||||
int n;
|
struct wlr_buffer *updated_buffer =
|
||||||
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n);
|
wlr_buffer_apply_damage(surface->buffer, resource, &damage);
|
||||||
for (int i = 0; i < n; ++i) {
|
|
||||||
pixman_box32_t *r = &rects[i];
|
|
||||||
if (!wlr_texture_write_pixels(surface->texture, fmt, stride,
|
|
||||||
r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1,
|
|
||||||
r->x1, r->y1, data)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pixman_region32_fini(&damage);
|
pixman_region32_fini(&damage);
|
||||||
|
|
||||||
|
if (updated_buffer != NULL) {
|
||||||
|
surface->buffer = updated_buffer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_shm_buffer_end_access(buf);
|
wlr_buffer_unref(surface->buffer);
|
||||||
|
surface->buffer = NULL;
|
||||||
|
|
||||||
// We've uploaded the wl_shm_buffer data to the GPU, we won't access the
|
struct wlr_buffer *buffer = wlr_buffer_create(surface->renderer, resource);
|
||||||
// wl_buffer anymore
|
if (buffer == NULL) {
|
||||||
surface_state_release_buffer(surface->current);
|
wlr_log(L_ERROR, "Failed to upload buffer");
|
||||||
} else if (invalid_buffer || reupload_buffer) {
|
return;
|
||||||
wlr_texture_destroy(surface->texture);
|
|
||||||
|
|
||||||
if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, resource)) {
|
|
||||||
surface->texture =
|
|
||||||
wlr_texture_from_wl_drm(surface->renderer, resource);
|
|
||||||
} else if (wlr_dmabuf_resource_is_buffer(resource)) {
|
|
||||||
struct wlr_dmabuf_buffer *dmabuf =
|
|
||||||
wlr_dmabuf_buffer_from_buffer_resource(resource);
|
|
||||||
surface->texture =
|
|
||||||
wlr_texture_from_dmabuf(surface->renderer, &dmabuf->attributes);
|
|
||||||
} else {
|
|
||||||
surface->texture = NULL;
|
|
||||||
wlr_log(L_ERROR, "Unknown buffer handle attached");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't release the wl_buffer yet: since the texture is shared with the
|
surface->buffer = buffer;
|
||||||
// client, we'll access the wl_buffer when rendering
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void surface_commit_pending(struct wlr_surface *surface) {
|
static void surface_commit_pending(struct wlr_surface *surface) {
|
||||||
int32_t oldw = surface->current->buffer_width;
|
|
||||||
int32_t oldh = surface->current->buffer_height;
|
|
||||||
|
|
||||||
bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER;
|
bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER;
|
||||||
bool null_buffer_commit = invalid_buffer && surface->pending->buffer == NULL;
|
|
||||||
|
|
||||||
surface_move_state(surface, surface->pending, surface->current);
|
surface_move_state(surface, surface->pending, surface->current);
|
||||||
|
|
||||||
if (null_buffer_commit) {
|
if (invalid_buffer) {
|
||||||
wlr_texture_destroy(surface->texture);
|
surface_apply_damage(surface);
|
||||||
surface->texture = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool reupload_buffer = oldw != surface->current->buffer_width ||
|
|
||||||
oldh != surface->current->buffer_height;
|
|
||||||
surface_apply_damage(surface, invalid_buffer, reupload_buffer);
|
|
||||||
|
|
||||||
// commit subsurface order
|
// commit subsurface order
|
||||||
struct wlr_subsurface *subsurface;
|
struct wlr_subsurface *subsurface;
|
||||||
wl_list_for_each_reverse(subsurface, &surface->subsurface_pending_list,
|
wl_list_for_each_reverse(subsurface, &surface->subsurface_pending_list,
|
||||||
|
@ -613,9 +534,9 @@ static struct wlr_surface_state *surface_state_create(void) {
|
||||||
|
|
||||||
static void surface_state_destroy(struct wlr_surface_state *state) {
|
static void surface_state_destroy(struct wlr_surface_state *state) {
|
||||||
surface_state_reset_buffer(state);
|
surface_state_reset_buffer(state);
|
||||||
struct wlr_frame_callback *cb, *tmp;
|
struct wl_resource *resource, *tmp;
|
||||||
wl_list_for_each_safe(cb, tmp, &state->frame_callback_list, link) {
|
wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) {
|
||||||
wl_resource_destroy(cb->resource);
|
wl_resource_destroy(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
pixman_region32_fini(&state->surface_damage);
|
pixman_region32_fini(&state->surface_damage);
|
||||||
|
@ -657,10 +578,9 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) {
|
||||||
wl_list_remove(wl_resource_get_link(surface->resource));
|
wl_list_remove(wl_resource_get_link(surface->resource));
|
||||||
|
|
||||||
wl_list_remove(&surface->renderer_destroy.link);
|
wl_list_remove(&surface->renderer_destroy.link);
|
||||||
wlr_texture_destroy(surface->texture);
|
|
||||||
surface_state_destroy(surface->pending);
|
surface_state_destroy(surface->pending);
|
||||||
surface_state_destroy(surface->current);
|
surface_state_destroy(surface->current);
|
||||||
|
wlr_buffer_unref(surface->buffer);
|
||||||
free(surface);
|
free(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,8 +637,15 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client,
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) {
|
||||||
|
if (surface->buffer == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return surface->buffer->texture;
|
||||||
|
}
|
||||||
|
|
||||||
bool wlr_surface_has_buffer(struct wlr_surface *surface) {
|
bool wlr_surface_has_buffer(struct wlr_surface *surface) {
|
||||||
return surface->texture != NULL;
|
return wlr_surface_get_texture(surface) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wlr_surface_set_role(struct wlr_surface *surface, const char *role,
|
int wlr_surface_set_role(struct wlr_surface *surface, const char *role,
|
||||||
|
@ -1023,11 +950,11 @@ static inline int64_t timespec_to_msec(const struct timespec *a) {
|
||||||
|
|
||||||
void wlr_surface_send_frame_done(struct wlr_surface *surface,
|
void wlr_surface_send_frame_done(struct wlr_surface *surface,
|
||||||
const struct timespec *when) {
|
const struct timespec *when) {
|
||||||
struct wlr_frame_callback *cb, *cnext;
|
struct wl_resource *resource, *tmp;
|
||||||
wl_list_for_each_safe(cb, cnext, &surface->current->frame_callback_list,
|
wl_resource_for_each_safe(resource, tmp,
|
||||||
link) {
|
&surface->current->frame_callback_list) {
|
||||||
wl_callback_send_done(cb->resource, timespec_to_msec(when));
|
wl_callback_send_done(resource, timespec_to_msec(when));
|
||||||
wl_resource_destroy(cb->resource);
|
wl_resource_destroy(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ bool compare_xdg_surface_toplevel_state(struct wlr_xdg_toplevel *state) {
|
||||||
// last configure is actually the current state, just use it
|
// last configure is actually the current state, just use it
|
||||||
configured.state = state->current;
|
configured.state = state->current;
|
||||||
configured.width = state->base->surface->current->width;
|
configured.width = state->base->surface->current->width;
|
||||||
configured.height = state->base->surface->current->width;
|
configured.height = state->base->surface->current->height;
|
||||||
} else {
|
} else {
|
||||||
struct wlr_xdg_surface_configure *configure =
|
struct wlr_xdg_surface_configure *configure =
|
||||||
wl_container_of(state->base->configure_list.prev, configure, link);
|
wl_container_of(state->base->configure_list.prev, configure, link);
|
||||||
|
|
|
@ -288,7 +288,7 @@ bool compare_xdg_surface_v6_toplevel_state(struct wlr_xdg_toplevel_v6 *state) {
|
||||||
// last configure is actually the current state, just use it
|
// last configure is actually the current state, just use it
|
||||||
configured.state = state->current;
|
configured.state = state->current;
|
||||||
configured.width = state->base->surface->current->width;
|
configured.width = state->base->surface->current->width;
|
||||||
configured.height = state->base->surface->current->width;
|
configured.height = state->base->surface->current->height;
|
||||||
} else {
|
} else {
|
||||||
struct wlr_xdg_surface_v6_configure *configure =
|
struct wlr_xdg_surface_v6_configure *configure =
|
||||||
wl_container_of(state->base->configure_list.prev, configure, link);
|
wl_container_of(state->base->configure_list.prev, configure, link);
|
||||||
|
|
Loading…
Reference in New Issue