blob: 6e4c7eeb282d564455587c58b2fd6462c6677ac1 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h" // NOLINT(build/include_directory)
#include "sommelier-tracing.h" // NOLINT(build/include_directory)
#include <assert.h>
#include <errno.h>
#include <gbm.h>
#include <libdrm/drm_fourcc.h>
#include <limits.h>
#include <linux/virtwl.h>
#include <pixman.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-util.h>
#include "drm-server-protocol.h" // NOLINT(build/include_directory)
#include "linux-dmabuf-unstable-v1-client-protocol.h" // NOLINT(build/include_directory)
#include "viewporter-client-protocol.h" // NOLINT(build/include_directory)
#define MIN_SIZE (INT_MIN / 10)
#define MAX_SIZE (INT_MAX / 10)
#define DMA_BUF_SYNC_READ (1 << 0)
#define DMA_BUF_SYNC_WRITE (2 << 0)
#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE)
#define DMA_BUF_SYNC_START (0 << 2)
#define DMA_BUF_SYNC_END (1 << 2)
#define DMA_BUF_BASE 'b'
#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
struct sl_host_compositor {
struct sl_compositor* compositor;
struct wl_resource* resource;
struct wl_compositor* proxy;
};
struct sl_output_buffer {
struct wl_list link;
uint32_t width;
uint32_t height;
uint32_t format;
struct wl_buffer* internal;
struct sl_mmap* mmap;
struct pixman_region32 damage;
struct sl_host_surface* surface;
};
struct dma_buf_sync {
__u64 flags;
};
static void sl_dmabuf_sync(int fd, __u64 flags) {
struct dma_buf_sync sync = {0};
int rv;
sync.flags = flags;
do {
rv = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
} while (rv == -1 && errno == EINTR);
}
static void sl_dmabuf_begin_write(int fd) {
sl_dmabuf_sync(fd, DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE);
}
static void sl_dmabuf_end_write(int fd) {
sl_dmabuf_sync(fd, DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE);
}
static void sl_virtwl_dmabuf_sync(int fd, __u32 flags) {
struct virtwl_ioctl_dmabuf_sync sync = {0};
int rv;
sync.flags = flags;
rv = ioctl(fd, VIRTWL_IOCTL_DMABUF_SYNC, &sync);
assert(!rv);
UNUSED(rv);
}
static void sl_virtwl_dmabuf_begin_write(int fd) {
sl_virtwl_dmabuf_sync(fd, DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE);
}
static void sl_virtwl_dmabuf_end_write(int fd) {
sl_virtwl_dmabuf_sync(fd, DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE);
}
static uint32_t sl_gbm_format_for_shm_format(uint32_t format) {
switch (format) {
case WL_SHM_FORMAT_NV12:
return GBM_FORMAT_NV12;
case WL_SHM_FORMAT_RGB565:
return GBM_FORMAT_RGB565;
case WL_SHM_FORMAT_ARGB8888:
return GBM_FORMAT_ARGB8888;
case WL_SHM_FORMAT_ABGR8888:
return GBM_FORMAT_ABGR8888;
case WL_SHM_FORMAT_XRGB8888:
return GBM_FORMAT_XRGB8888;
case WL_SHM_FORMAT_XBGR8888:
return GBM_FORMAT_XBGR8888;
}
assert(0);
return 0;
}
static uint32_t sl_drm_format_for_shm_format(int format) {
switch (format) {
case WL_SHM_FORMAT_NV12:
return WL_DRM_FORMAT_NV12;
case WL_SHM_FORMAT_RGB565:
return WL_DRM_FORMAT_RGB565;
case WL_SHM_FORMAT_ARGB8888:
return WL_DRM_FORMAT_ARGB8888;
case WL_SHM_FORMAT_ABGR8888:
return WL_DRM_FORMAT_ABGR8888;
case WL_SHM_FORMAT_XRGB8888:
return WL_DRM_FORMAT_XRGB8888;
case WL_SHM_FORMAT_XBGR8888:
return WL_DRM_FORMAT_XBGR8888;
}
assert(0);
return 0;
}
static void sl_output_buffer_destroy(struct sl_output_buffer* buffer) {
wl_buffer_destroy(buffer->internal);
sl_mmap_unref(buffer->mmap);
pixman_region32_fini(&buffer->damage);
wl_list_remove(&buffer->link);
free(buffer);
}
static void sl_output_buffer_release(void* data, struct wl_buffer* buffer) {
struct sl_output_buffer* output_buffer =
static_cast<sl_output_buffer*>(wl_buffer_get_user_data(buffer));
TRACE_EVENT("surface", "sl_output_buffer_release", "resource_id",
wl_resource_get_id(output_buffer->surface->resource));
struct sl_host_surface* host_surface = output_buffer->surface;
wl_list_remove(&output_buffer->link);
wl_list_insert(&host_surface->released_buffers, &output_buffer->link);
}
static const struct wl_buffer_listener sl_output_buffer_listener = {
sl_output_buffer_release};
static void sl_host_surface_destroy(struct wl_client* client,
struct wl_resource* resource) {
TRACE_EVENT("surface", "sl_host_surface_destroy", "resource_id",
wl_resource_get_id(resource));
wl_resource_destroy(resource);
}
static void sl_host_surface_attach(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* buffer_resource,
int32_t x,
int32_t y) {
TRACE_EVENT("surface", "sl_host_surface_attach", "resource_id",
wl_resource_get_id(resource), "buffer_id",
wl_resource_get_id(buffer_resource));
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
struct sl_host_buffer* host_buffer =
buffer_resource ? static_cast<sl_host_buffer*>(
wl_resource_get_user_data(buffer_resource))
: NULL;
struct wl_buffer* buffer_proxy = NULL;
struct sl_window* window;
double scale = host->ctx->scale;
host->current_buffer = NULL;
if (host->contents_shm_mmap) {
sl_mmap_unref(host->contents_shm_mmap);
host->contents_shm_mmap = NULL;
}
if (host_buffer) {
host->contents_width = host_buffer->width;
host->contents_height = host_buffer->height;
buffer_proxy = host_buffer->proxy;
if (host_buffer->shm_mmap)
host->contents_shm_mmap = sl_mmap_ref(host_buffer->shm_mmap);
}
if (host->contents_shm_mmap) {
while (!wl_list_empty(&host->released_buffers)) {
host->current_buffer = wl_container_of(host->released_buffers.next,
host->current_buffer, link);
if (host->current_buffer->width == host_buffer->width &&
host->current_buffer->height == host_buffer->height &&
host->current_buffer->format == host_buffer->shm_format) {
break;
}
sl_output_buffer_destroy(host->current_buffer);
host->current_buffer = NULL;
}
// Allocate new output buffer.
if (!host->current_buffer) {
size_t width = host_buffer->width;
size_t height = host_buffer->height;
uint32_t shm_format = host_buffer->shm_format;
size_t bpp = sl_shm_bpp_for_shm_format(shm_format);
size_t num_planes = sl_shm_num_planes_for_shm_format(shm_format);
host->current_buffer = static_cast<sl_output_buffer*>(
malloc(sizeof(struct sl_output_buffer)));
assert(host->current_buffer);
wl_list_insert(&host->released_buffers, &host->current_buffer->link);
host->current_buffer->width = width;
host->current_buffer->height = height;
host->current_buffer->format = shm_format;
host->current_buffer->surface = host;
pixman_region32_init_rect(&host->current_buffer->damage, 0, 0, MAX_SIZE,
MAX_SIZE);
switch (host->ctx->shm_driver) {
case SHM_DRIVER_DMABUF: {
struct zwp_linux_buffer_params_v1* buffer_params;
struct gbm_bo* bo;
int stride0;
int fd;
bo = gbm_bo_create(host->ctx->gbm, width, height,
sl_gbm_format_for_shm_format(shm_format),
GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR);
stride0 = gbm_bo_get_stride(bo);
fd = gbm_bo_get_fd(bo);
buffer_params = zwp_linux_dmabuf_v1_create_params(
host->ctx->linux_dmabuf->internal);
zwp_linux_buffer_params_v1_add(buffer_params, fd, 0, 0, stride0,
DRM_FORMAT_MOD_INVALID >> 32,
DRM_FORMAT_MOD_INVALID & 0xffffffff);
host->current_buffer->internal =
zwp_linux_buffer_params_v1_create_immed(
buffer_params, width, height,
sl_drm_format_for_shm_format(shm_format), 0);
zwp_linux_buffer_params_v1_destroy(buffer_params);
host->current_buffer->mmap = sl_mmap_create(
fd, height * stride0, bpp, 1, 0, stride0, 0, 0, 1, 0);
host->current_buffer->mmap->begin_write = sl_dmabuf_begin_write;
host->current_buffer->mmap->end_write = sl_dmabuf_end_write;
gbm_bo_destroy(bo);
} break;
case SHM_DRIVER_VIRTWL: {
size_t size = host_buffer->shm_mmap->size;
struct virtwl_ioctl_new ioctl_new = {};
ioctl_new.type = VIRTWL_IOCTL_NEW_ALLOC;
ioctl_new.fd = -1;
ioctl_new.flags = 0;
ioctl_new.size = static_cast<__u32>(size);
struct wl_shm_pool* pool;
int rv;
rv = ioctl(host->ctx->virtwl_fd, VIRTWL_IOCTL_NEW, &ioctl_new);
assert(rv == 0);
UNUSED(rv);
pool =
wl_shm_create_pool(host->ctx->shm->internal, ioctl_new.fd, size);
host->current_buffer->internal = wl_shm_pool_create_buffer(
pool, 0, width, height, host_buffer->shm_mmap->stride[0],
shm_format);
wl_shm_pool_destroy(pool);
host->current_buffer->mmap = sl_mmap_create(
ioctl_new.fd, size, bpp, num_planes, 0,
host_buffer->shm_mmap->stride[0],
host_buffer->shm_mmap->offset[1] -
host_buffer->shm_mmap->offset[0],
host_buffer->shm_mmap->stride[1], host_buffer->shm_mmap->y_ss[0],
host_buffer->shm_mmap->y_ss[1]);
} break;
case SHM_DRIVER_VIRTWL_DMABUF: {
uint32_t drm_format = sl_drm_format_for_shm_format(shm_format);
struct virtwl_ioctl_new ioctl_new = {};
ioctl_new.type = VIRTWL_IOCTL_NEW_DMABUF;
ioctl_new.fd = -1;
ioctl_new.flags = 0;
ioctl_new.dmabuf.width = static_cast<__u32>(width);
ioctl_new.dmabuf.height = static_cast<__u32>(height);
ioctl_new.dmabuf.format = drm_format;
struct zwp_linux_buffer_params_v1* buffer_params;
size_t size;
int rv;
rv = ioctl(host->ctx->virtwl_fd, VIRTWL_IOCTL_NEW, &ioctl_new);
if (rv) {
fprintf(stderr, "error: virtwl dmabuf allocation failed: %s\n",
strerror(errno));
_exit(EXIT_FAILURE);
}
size = ioctl_new.dmabuf.stride0 * height;
buffer_params = zwp_linux_dmabuf_v1_create_params(
host->ctx->linux_dmabuf->internal);
zwp_linux_buffer_params_v1_add(buffer_params, ioctl_new.fd, 0,
ioctl_new.dmabuf.offset0,
ioctl_new.dmabuf.stride0, 0, 0);
if (num_planes > 1) {
zwp_linux_buffer_params_v1_add(buffer_params, ioctl_new.fd, 1,
ioctl_new.dmabuf.offset1,
ioctl_new.dmabuf.stride1, 0, 0);
size = MAX(size, ioctl_new.dmabuf.offset1 +
ioctl_new.dmabuf.stride1 * height /
host_buffer->shm_mmap->y_ss[1]);
}
host->current_buffer->internal =
zwp_linux_buffer_params_v1_create_immed(buffer_params, width,
height, drm_format, 0);
zwp_linux_buffer_params_v1_destroy(buffer_params);
host->current_buffer->mmap = sl_mmap_create(
ioctl_new.fd, size, bpp, num_planes, ioctl_new.dmabuf.offset0,
ioctl_new.dmabuf.stride0, ioctl_new.dmabuf.offset1,
ioctl_new.dmabuf.stride1, host_buffer->shm_mmap->y_ss[0],
host_buffer->shm_mmap->y_ss[1]);
host->current_buffer->mmap->begin_write =
sl_virtwl_dmabuf_begin_write;
host->current_buffer->mmap->end_write = sl_virtwl_dmabuf_end_write;
} break;
}
assert(host->current_buffer->internal);
assert(host->current_buffer->mmap);
wl_buffer_set_user_data(host->current_buffer->internal,
host->current_buffer);
wl_buffer_add_listener(host->current_buffer->internal,
&sl_output_buffer_listener, host->current_buffer);
}
}
x /= scale;
y /= scale;
// TODO(davidriley): This should be done in the commit.
if (host_buffer && host_buffer->sync_point) {
host_buffer->sync_point->sync(host->ctx, host_buffer->sync_point);
}
if (host->current_buffer) {
assert(host->current_buffer->internal);
wl_surface_attach(host->proxy, host->current_buffer->internal, x, y);
} else {
wl_surface_attach(host->proxy, buffer_proxy, x, y);
}
wl_list_for_each(window, &host->ctx->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
while (sl_process_pending_configure_acks(window, host))
continue;
break;
}
}
} // NOLINT(whitespace/indent)
static void sl_host_surface_damage(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
TRACE_EVENT("surface", "sl_host_surface_damage", "resource_id",
wl_resource_get_id(resource));
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
double scale = host->ctx->scale;
struct sl_output_buffer* buffer;
int64_t x1, y1, x2, y2;
wl_list_for_each(buffer, &host->busy_buffers, link) {
pixman_region32_union_rect(&buffer->damage, &buffer->damage, x, y, width,
height);
}
wl_list_for_each(buffer, &host->released_buffers, link) {
pixman_region32_union_rect(&buffer->damage, &buffer->damage, x, y, width,
height);
}
x1 = x;
y1 = y;
x2 = x1 + width;
y2 = y1 + height;
// Enclosing rect after scaling and outset by one pixel to account for
// potential filtering.
x1 = MAX(MIN_SIZE, x1 - 1) / scale;
y1 = MAX(MIN_SIZE, y1 - 1) / scale;
x2 = ceil(MIN(x2 + 1, MAX_SIZE) / scale);
y2 = ceil(MIN(y2 + 1, MAX_SIZE) / scale);
wl_surface_damage(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void sl_frame_callback_done(void* data,
struct wl_callback* callback,
uint32_t time) {
TRACE_EVENT("surface", "sl_frame_callback_done");
struct sl_host_callback* host =
static_cast<sl_host_callback*>(wl_callback_get_user_data(callback));
wl_callback_send_done(host->resource, time);
wl_resource_destroy(host->resource);
}
static const struct wl_callback_listener sl_frame_callback_listener = {
sl_frame_callback_done};
static void sl_host_callback_destroy(struct wl_resource* resource) {
TRACE_EVENT("surface", "sl_host_callback_destroy");
struct sl_host_callback* host =
static_cast<sl_host_callback*>(wl_resource_get_user_data(resource));
wl_callback_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_host_surface_frame(struct wl_client* client,
struct wl_resource* resource,
uint32_t callback) {
TRACE_EVENT("surface", "sl_host_surface_frame", "resource_id",
wl_resource_get_id(resource));
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
struct sl_host_callback* host_callback =
static_cast<sl_host_callback*>(malloc(sizeof(*host_callback)));
assert(host_callback);
host_callback->resource =
wl_resource_create(client, &wl_callback_interface, 1, callback);
wl_resource_set_implementation(host_callback->resource, NULL, host_callback,
sl_host_callback_destroy);
host_callback->proxy = wl_surface_frame(host->proxy);
wl_callback_set_user_data(host_callback->proxy, host_callback);
wl_callback_add_listener(host_callback->proxy, &sl_frame_callback_listener,
host_callback);
}
static void sl_host_surface_set_opaque_region(
struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* region_resource) {
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
struct sl_host_region* host_region =
region_resource ? static_cast<sl_host_region*>(
wl_resource_get_user_data(region_resource))
: NULL;
wl_surface_set_opaque_region(host->proxy,
host_region ? host_region->proxy : NULL);
} // NOLINT(whitespace/indent)
static void sl_host_surface_set_input_region(
struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* region_resource) {
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
struct sl_host_region* host_region =
region_resource ? static_cast<sl_host_region*>(
wl_resource_get_user_data(region_resource))
: NULL;
wl_surface_set_input_region(host->proxy,
host_region ? host_region->proxy : NULL);
} // NOLINT(whitespace/indent)
static void sl_host_surface_commit(struct wl_client* client,
struct wl_resource* resource) {
TRACE_EVENT("surface", "sl_host_surface_commit", "resource_id",
wl_resource_get_id(resource));
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
struct sl_viewport* viewport = NULL;
struct sl_window* window;
if (!wl_list_empty(&host->contents_viewport))
viewport = wl_container_of(host->contents_viewport.next, viewport, link);
if (host->contents_shm_mmap) {
uint8_t* src_addr = static_cast<uint8_t*>(host->contents_shm_mmap->addr);
uint8_t* dst_addr = static_cast<uint8_t*>(host->current_buffer->mmap->addr);
size_t* src_offset = host->contents_shm_mmap->offset;
size_t* dst_offset = host->current_buffer->mmap->offset;
size_t* src_stride = host->contents_shm_mmap->stride;
size_t* dst_stride = host->current_buffer->mmap->stride;
size_t* y_ss = host->contents_shm_mmap->y_ss;
size_t bpp = host->contents_shm_mmap->bpp;
size_t num_planes = host->contents_shm_mmap->num_planes;
double contents_scale_x = host->contents_scale;
double contents_scale_y = host->contents_scale;
double contents_offset_x = 0.0;
double contents_offset_y = 0.0;
pixman_box32_t* rect;
int n;
// Determine scale and offset for damage based on current viewport.
if (viewport) {
double contents_width = host->contents_width;
double contents_height = host->contents_height;
if (viewport->src_x >= 0 && viewport->src_y >= 0) {
contents_offset_x = wl_fixed_to_double(viewport->src_x);
contents_offset_y = wl_fixed_to_double(viewport->src_y);
}
if (viewport->dst_width > 0 && viewport->dst_height > 0) {
contents_scale_x *= contents_width / viewport->dst_width;
contents_scale_y *= contents_height / viewport->dst_height;
// Take source rectangle into account when both destionation size and
// source rectangle are set. If only source rectangle is set, then
// it determines the surface size so it can be ignored.
if (viewport->src_width >= 0 && viewport->src_height >= 0) {
contents_scale_x *=
wl_fixed_to_double(viewport->src_width) / contents_width;
contents_scale_y *=
wl_fixed_to_double(viewport->src_height) / contents_height;
}
}
}
if (host->current_buffer->mmap->begin_write)
host->current_buffer->mmap->begin_write(host->current_buffer->mmap->fd);
rect = pixman_region32_rectangles(&host->current_buffer->damage, &n);
while (n--) {
int32_t x1, y1, x2, y2;
// Enclosing rect after applying scale and offset.
x1 = rect->x1 * contents_scale_x + contents_offset_x;
y1 = rect->y1 * contents_scale_y + contents_offset_y;
x2 = rect->x2 * contents_scale_x + contents_offset_x + 0.5;
y2 = rect->y2 * contents_scale_y + contents_offset_y + 0.5;
x1 = MAX(0, x1);
y1 = MAX(0, y1);
x2 = MIN(static_cast<int32_t>(host->contents_width), x2);
y2 = MIN(static_cast<int32_t>(host->contents_height), y2);
if (x1 < x2 && y1 < y2) {
size_t i;
for (i = 0; i < num_planes; ++i) {
uint8_t* src_base = src_addr + src_offset[i];
uint8_t* dst_base = dst_addr + dst_offset[i];
uint8_t* src = src_base + y1 * src_stride[i] + x1 * bpp;
uint8_t* dst = dst_base + y1 * dst_stride[i] + x1 * bpp;
int32_t width = x2 - x1;
int32_t height = (y2 - y1) / y_ss[i];
size_t bytes = width * bpp;
while (height--) {
memcpy(dst, src, bytes);
dst += dst_stride[i];
src += src_stride[i];
}
}
}
++rect;
}
if (host->current_buffer->mmap->end_write)
host->current_buffer->mmap->end_write(host->current_buffer->mmap->fd);
pixman_region32_clear(&host->current_buffer->damage);
wl_list_remove(&host->current_buffer->link);
wl_list_insert(&host->busy_buffers, &host->current_buffer->link);
}
if (host->contents_width && host->contents_height) {
double scale = host->ctx->scale * host->contents_scale;
if (host->viewport) {
int width = host->contents_width;
int height = host->contents_height;
// We need to take the client's viewport into account while still
// making sure our scale is accounted for.
if (viewport) {
if (viewport->src_x >= 0 && viewport->src_y >= 0 &&
viewport->src_width >= 0 && viewport->src_height >= 0) {
wp_viewport_set_source(host->viewport, viewport->src_x,
viewport->src_y, viewport->src_width,
viewport->src_height);
// If the source rectangle is set and the destination size is not
// set, then src_width and src_height should be integers, and the
// surface size becomes the source rectangle size.
width = wl_fixed_to_int(viewport->src_width);
height = wl_fixed_to_int(viewport->src_height);
}
// Use destination size as surface size when set.
if (viewport->dst_width >= 0 && viewport->dst_height >= 0) {
width = viewport->dst_width;
height = viewport->dst_height;
}
}
wp_viewport_set_destination(host->viewport, ceil(width / scale),
ceil(height / scale));
} else {
wl_surface_set_buffer_scale(host->proxy, scale);
}
}
// No need to defer client commits if surface has a role. E.g. is a cursor
// or shell surface.
if (host->has_role) {
wl_surface_commit(host->proxy);
// GTK determines the scale based on the output the surface has entered.
// If the surface has not entered any output, then have it enter the
// internal output. TODO(reveman): Remove this when surface-output tracking
// has been implemented in Chrome.
if (!host->has_output) {
struct sl_host_output* output;
wl_list_for_each(output, &host->ctx->host_outputs, link) {
if (output->internal) {
wl_surface_send_enter(host->resource, output->resource);
host->has_output = 1;
break;
}
}
}
} else {
// Commit if surface is associated with a window. Otherwise, defer
// commit until window is created.
wl_list_for_each(window, &host->ctx->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
if (window->xdg_surface) {
wl_surface_commit(host->proxy);
if (host->contents_width && host->contents_height)
window->realized = 1;
}
break;
}
}
}
if (host->contents_shm_mmap) {
if (host->contents_shm_mmap->buffer_resource) {
wl_buffer_send_release(host->contents_shm_mmap->buffer_resource);
}
sl_mmap_unref(host->contents_shm_mmap);
host->contents_shm_mmap = NULL;
}
}
static void sl_host_surface_set_buffer_transform(struct wl_client* client,
struct wl_resource* resource,
int32_t transform) {
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
wl_surface_set_buffer_transform(host->proxy, transform);
}
static void sl_host_surface_set_buffer_scale(struct wl_client* client,
struct wl_resource* resource,
int32_t scale) {
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
host->contents_scale = scale;
}
static void sl_host_surface_damage_buffer(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
assert(0);
}
static const struct wl_surface_interface sl_surface_implementation = {
sl_host_surface_destroy,
sl_host_surface_attach,
sl_host_surface_damage,
sl_host_surface_frame,
sl_host_surface_set_opaque_region,
sl_host_surface_set_input_region,
sl_host_surface_commit,
sl_host_surface_set_buffer_transform,
sl_host_surface_set_buffer_scale,
sl_host_surface_damage_buffer};
static void sl_destroy_host_surface(struct wl_resource* resource) {
TRACE_EVENT("surface", "sl_destroy_host_surface", "resource_id",
wl_resource_get_id(resource));
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_resource_get_user_data(resource));
struct sl_window *window, *surface_window = NULL;
struct sl_output_buffer* buffer;
wl_list_for_each(window, &host->ctx->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
surface_window = window;
break;
}
}
if (surface_window) {
surface_window->host_surface_id = 0;
sl_window_update(surface_window);
}
if (host->contents_shm_mmap)
sl_mmap_unref(host->contents_shm_mmap);
while (!wl_list_empty(&host->released_buffers)) {
buffer = wl_container_of(host->released_buffers.next, buffer, link);
sl_output_buffer_destroy(buffer);
}
while (!wl_list_empty(&host->busy_buffers)) {
buffer = wl_container_of(host->busy_buffers.next, buffer, link);
sl_output_buffer_destroy(buffer);
}
while (!wl_list_empty(&host->contents_viewport))
wl_list_remove(host->contents_viewport.next);
if (host->viewport)
wp_viewport_destroy(host->viewport);
wl_surface_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_surface_enter(void* data,
struct wl_surface* surface,
struct wl_output* output) {
TRACE_EVENT("surface", "sl_surface_enter");
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_surface_get_user_data(surface));
struct sl_host_output* host_output =
static_cast<sl_host_output*>(wl_output_get_user_data(output));
wl_surface_send_enter(host->resource, host_output->resource);
host->has_output = 1;
}
static void sl_surface_leave(void* data,
struct wl_surface* surface,
struct wl_output* output) {
TRACE_EVENT("surface", "sl_surface_leave");
struct sl_host_surface* host =
static_cast<sl_host_surface*>(wl_surface_get_user_data(surface));
struct sl_host_output* host_output =
static_cast<sl_host_output*>(wl_output_get_user_data(output));
wl_surface_send_leave(host->resource, host_output->resource);
}
static const struct wl_surface_listener sl_surface_listener = {
sl_surface_enter, sl_surface_leave};
static void sl_region_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_region_add(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_region* host =
static_cast<sl_host_region*>(wl_resource_get_user_data(resource));
double scale = host->ctx->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
wl_region_add(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void sl_region_subtract(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_region* host =
static_cast<sl_host_region*>(wl_resource_get_user_data(resource));
double scale = host->ctx->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
wl_region_subtract(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static const struct wl_region_interface sl_region_implementation = {
sl_region_destroy, sl_region_add, sl_region_subtract};
static void sl_destroy_host_region(struct wl_resource* resource) {
struct sl_host_region* host =
static_cast<sl_host_region*>(wl_resource_get_user_data(resource));
wl_region_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_compositor_create_host_surface(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
TRACE_EVENT("surface", "sl_compositor_create_host_surface");
struct sl_host_compositor* host =
static_cast<sl_host_compositor*>(wl_resource_get_user_data(resource));
struct sl_window *window, *unpaired_window = NULL;
struct sl_host_surface* host_surface =
static_cast<sl_host_surface*>(malloc(sizeof(*host_surface)));
assert(host_surface);
host_surface->ctx = host->compositor->ctx;
host_surface->contents_width = 0;
host_surface->contents_height = 0;
host_surface->contents_scale = 1;
wl_list_init(&host_surface->contents_viewport);
host_surface->contents_shm_mmap = NULL;
host_surface->has_role = 0;
host_surface->has_output = 0;
host_surface->last_event_serial = 0;
host_surface->current_buffer = NULL;
wl_list_init(&host_surface->released_buffers);
wl_list_init(&host_surface->busy_buffers);
host_surface->resource = wl_resource_create(
client, &wl_surface_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_surface->resource,
&sl_surface_implementation, host_surface,
sl_destroy_host_surface);
host_surface->proxy = wl_compositor_create_surface(host->proxy);
wl_surface_set_user_data(host_surface->proxy, host_surface);
wl_surface_add_listener(host_surface->proxy, &sl_surface_listener,
host_surface);
host_surface->viewport = NULL;
if (host_surface->ctx->viewporter) {
host_surface->viewport = wp_viewporter_get_viewport(
host_surface->ctx->viewporter->internal, host_surface->proxy);
}
wl_list_for_each(window, &host->compositor->ctx->unpaired_windows, link) {
if (window->host_surface_id == id) {
unpaired_window = window;
break;
}
}
if (unpaired_window)
sl_window_update(window);
}
static void sl_compositor_create_host_region(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_host_compositor* host =
static_cast<sl_host_compositor*>(wl_resource_get_user_data(resource));
struct sl_host_region* host_region =
static_cast<sl_host_region*>(malloc(sizeof(*host_region)));
assert(host_region);
host_region->ctx = host->compositor->ctx;
host_region->resource = wl_resource_create(
client, &wl_region_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_region->resource,
&sl_region_implementation, host_region,
sl_destroy_host_region);
host_region->proxy = wl_compositor_create_region(host->proxy);
wl_region_set_user_data(host_region->proxy, host_region);
}
static const struct wl_compositor_interface sl_compositor_implementation = {
sl_compositor_create_host_surface, sl_compositor_create_host_region};
static void sl_destroy_host_compositor(struct wl_resource* resource) {
struct sl_host_compositor* host =
static_cast<sl_host_compositor*>(wl_resource_get_user_data(resource));
wl_compositor_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_compositor(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_compositor* host =
static_cast<sl_host_compositor*>(malloc(sizeof(*host)));
assert(host);
host->compositor = ctx->compositor;
host->resource =
wl_resource_create(client, &wl_compositor_interface,
MIN(version, ctx->compositor->version), id);
wl_resource_set_implementation(host->resource, &sl_compositor_implementation,
host, sl_destroy_host_compositor);
host->proxy = static_cast<wl_compositor*>(wl_registry_bind(
wl_display_get_registry(ctx->display), ctx->compositor->id,
&wl_compositor_interface, ctx->compositor->version));
wl_compositor_set_user_data(host->proxy, host);
}
struct sl_global* sl_compositor_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &wl_compositor_interface,
ctx->compositor->version, ctx,
sl_bind_host_compositor);
}