12to11/dmabuf.c
hujianwei 94333293c8 Fix compatibility with Chromium
* dmabuf.c (struct _Buffer): New field `is_fallback'.
(DestroyBacking): Destroy fallback buffers appropriately.
(CreatePlaceholderBuffer): New function.
(CreateImmed): Create palceholder buffers upon a `failed' event
being sent.
* xdg_toplevel.c (UnsetMaximized, UnsetFullscreen): Add
workarounds for Chromium bugs.
2022-11-18 13:11:22 +00:00

1158 lines
30 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Wayland compositor running on top of an X server.
Copyright (C) 2022 to various contributors.
This file is part of 12to11.
12to11 is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
12to11 is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <drm_fourcc.h>
#include "compositor.h"
#include "linux-dmabuf-unstable-v1.h"
#include <xcb/dri3.h>
#include <X11/Xmd.h>
#include <X11/extensions/dri3proto.h>
typedef struct _DrmFormatInfo DrmFormatInfo;
typedef struct _BufferParams BufferParams;
typedef struct _Buffer Buffer;
typedef struct _TemporarySetEntry TemporarySetEntry;
typedef struct _FormatModifierPair FormatModifierPair;
enum
{
IsUsed = 1,
IsCallbackData = (1 << 2),
};
struct _TemporarySetEntry
{
/* These fields mean the same as they do in the args to
zwp_linux_buffer_params_v1_add. */
int fd;
unsigned int plane_idx, offset, stride;
uint32_t modifier_hi, modifier_lo;
};
struct _BufferParams
{
/* Entries for each plane. DRI3 only supports up to 4 planes. */
TemporarySetEntry entries[4];
/* The struct wl_resource associated with this object. */
struct wl_resource *resource;
/* Some flags. */
int flags;
/* The width and height of the buffer that will be created. */
int width, height;
};
struct _Buffer
{
/* The ExtBuffer associated with this buffer. */
ExtBuffer buffer;
/* The RenderBuffer associated with this buffer. */
RenderBuffer render_buffer;
/* The wl_resource corresponding to this buffer. */
struct wl_resource *resource;
/* The width and height of this buffer. */
unsigned int width, height;
/* The number of references to this buffer. */
int refcount;
/* Whether or not this buffer is actually a single-pixel buffer. */
Bool is_fallback;
};
struct _FormatModifierPair
{
/* See the documentation of
zwp_linux_dmabuf_feedback_v1::format_table for more details. */
uint32_t format;
uint32_t padding;
uint64_t modifier;
};
/* The wl_global associated with linux-dmabuf-unstable-v1. */
static struct wl_global *global_dmabuf;
/* File descriptor for the format table. */
static int format_table_fd;
/* Size of the format table. */
static ssize_t format_table_size;
/* Device nodes of the DRM device. */
static dev_t *drm_device_nodes;
/* The number of DRM device nodes present. */
static int num_device_nodes;
/* DRM formats supported by the renderer. */
static DrmFormat *supported_formats;
/* Number of formats. */
static int n_drm_formats;
static void
CloseFdsEarly (BufferParams *params)
{
int i;
for (i = 0; i < ArrayElements (params->entries); ++i)
{
if (params->entries[i].fd != -1)
close (params->entries[i].fd);
}
}
static void
ReleaseBufferParams (BufferParams *params)
{
/* Also close any fds that were set if this object was not yet
used. */
if (!(params->flags & IsUsed))
CloseFdsEarly (params);
/* params should not be destroyed if it is being used as callback
data. */
XLAssert (!(params->flags & IsCallbackData));
XLFree (params);
}
static void
HandleParamsResourceDestroy (struct wl_resource *resource)
{
BufferParams *params;
params = wl_resource_get_user_data (resource);
/* First, clear params->resource. */
params->resource = NULL;
if (params->flags & IsCallbackData)
/* If params is callback data, simply clear params->resource, and
wait for a callback to be called. */
return;
/* Next, destroy the params now, unless we are waiting for a buffer
to be created, in which case it is necessary to free the buffer
should the creation succeed. */
ReleaseBufferParams (params);
}
static void
DestroyBufferParams (struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static int
ExistingModifier (BufferParams *params, uint32_t *current_hi,
uint32_t *current_lo)
{
int i, count;
/* Pacify -Wmaybe-uninitialized under -O2. count is only non-zero
if both return arguments are initialized, but GCC thinks
otherwise. */
*current_hi = 0;
*current_lo = 0;
for (i = 0, count = 0; i < ArrayElements (params->entries); ++i)
{
if (params->entries[i].fd != -1)
{
*current_hi = params->entries[i].modifier_hi;
*current_lo = params->entries[i].modifier_lo;
count++;
}
}
return count;
}
static void
Add (struct wl_client *client, struct wl_resource *resource, int32_t fd,
uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi,
uint32_t modifier_lo)
{
BufferParams *params;
uint32_t current_hi, current_lo;
params = wl_resource_get_user_data (resource);
if (params->flags & IsUsed)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
"the given params resource has already been used");
close (fd);
return;
}
if (plane_idx >= ArrayElements (params->entries))
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
"maximum number of planes exceeded");
close (fd);
return;
}
if (params->entries[plane_idx].fd != -1)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
"the plane has already been added in the temporary set");
close (fd);
return;
}
if (ExistingModifier (params, &current_hi, &current_lo)
&& (current_hi != modifier_hi || current_lo != modifier_lo))
{
wl_resource_post_error (resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
"modifier does not match other planes in the temporary set");
close (fd);
return;
}
params->entries[plane_idx].fd = fd;
params->entries[plane_idx].plane_idx = plane_idx;
params->entries[plane_idx].offset = offset;
params->entries[plane_idx].stride = stride;
params->entries[plane_idx].modifier_hi = modifier_hi;
params->entries[plane_idx].modifier_lo = modifier_lo;
}
static void
DestroyBacking (Buffer *buffer)
{
if (--buffer->refcount)
return;
if (!buffer->is_fallback)
/* Free the renderer-specific dmabuf buffer. */
RenderFreeDmabufBuffer (buffer->render_buffer);
else
/* This is actually a fallback single-pixel buffer. Destroy it
instead. */
RenderFreeSinglePixelBuffer (buffer->render_buffer);
ExtBufferDestroy (&buffer->buffer);
XLFree (buffer);
}
static void
DestroyBuffer (struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static struct wl_buffer_interface zwp_linux_dmabuf_v1_buffer_impl =
{
.destroy = DestroyBuffer,
};
static void
HandleBufferResourceDestroy (struct wl_resource *resource)
{
Buffer *buffer;
buffer = wl_resource_get_user_data (resource);
buffer->resource = NULL;
DestroyBacking (buffer);
}
static void
RetainBufferFunc (ExtBuffer *buffer)
{
Buffer *dmabuf_buffer;
dmabuf_buffer = (Buffer *) buffer;
dmabuf_buffer->refcount++;
}
static void
DereferenceBufferFunc (ExtBuffer *buffer)
{
Buffer *dmabuf_buffer;
dmabuf_buffer = (Buffer *) buffer;
DestroyBacking (dmabuf_buffer);
}
static unsigned int
WidthFunc (ExtBuffer *buffer)
{
Buffer *dmabuf_buffer;
dmabuf_buffer = (Buffer *) buffer;
return dmabuf_buffer->width;
}
static unsigned int
HeightFunc (ExtBuffer *buffer)
{
Buffer *dmabuf_buffer;
dmabuf_buffer = (Buffer *) buffer;
return dmabuf_buffer->height;
}
static void
ReleaseBufferFunc (ExtBuffer *buffer)
{
if (((Buffer *) buffer)->resource)
wl_buffer_send_release (((Buffer *) buffer)->resource);
}
static RenderBuffer
GetBufferFunc (ExtBuffer *buffer)
{
Buffer *dmabuf_buffer;
dmabuf_buffer = (Buffer *) buffer;
return dmabuf_buffer->render_buffer;
}
static Buffer *
CreateBufferFor (BufferParams *params, RenderBuffer render_buffer,
uint32_t id)
{
Buffer *buffer;
struct wl_client *client;
buffer = XLSafeMalloc (sizeof *buffer);
client = wl_resource_get_client (params->resource);
if (!buffer)
{
RenderFreeDmabufBuffer (render_buffer);
zwp_linux_buffer_params_v1_send_failed (params->resource);
return NULL;
}
memset (buffer, 0, sizeof *buffer);
buffer->resource
= wl_resource_create (client, &wl_buffer_interface, 1, id);
if (!buffer->resource)
{
RenderFreeDmabufBuffer (render_buffer);
XLFree (buffer);
zwp_linux_buffer_params_v1_send_failed (params->resource);
return NULL;
}
buffer->render_buffer = render_buffer;
buffer->width = params->width;
buffer->height = params->height;
/* Initialize function pointers. */
buffer->buffer.funcs.retain = RetainBufferFunc;
buffer->buffer.funcs.dereference = DereferenceBufferFunc;
buffer->buffer.funcs.get_buffer = GetBufferFunc;
buffer->buffer.funcs.width = WidthFunc;
buffer->buffer.funcs.height = HeightFunc;
buffer->buffer.funcs.release = ReleaseBufferFunc;
buffer->refcount++;
wl_resource_set_implementation (buffer->resource,
&zwp_linux_dmabuf_v1_buffer_impl,
buffer, HandleBufferResourceDestroy);
return buffer;
}
#define ModifierHigh(mod) ((uint64_t) (mod) >> 31 >> 1)
#define ModifierLow(mod) ((uint64_t) (mod) & 0xffffffff)
static Bool
IsFormatSupported (uint32_t format, uint32_t mod_hi, uint32_t mod_low)
{
int i;
for (i = 0; i < n_drm_formats; ++i)
{
if (format == supported_formats[i].drm_format
/* Also check that the modifiers have been announced as
supported. */
&& ModifierHigh (supported_formats[i].drm_modifier) == mod_hi
&& ModifierLow (supported_formats[i].drm_modifier) == mod_low)
/* A match was found, so this format is supported. */
return True;
}
/* No match was found, so complain that the format is invalid. This
does not catch non-obvious errors, such as unsupported flags,
which may cause buffer creation to fail after on. */
return False;
}
static void
CreateSucceeded (RenderBuffer render_buffer, void *data)
{
BufferParams *params;
Buffer *buffer;
/* Buffer creation was successful. If the resource was already
destroyed, then simply destroy buffer and free params.
Otherwise, send the created buffer to the client. */
params = data;
if (!params->resource)
{
RenderFreeDmabufBuffer (render_buffer);
/* Now, release the buffer params. Since the callback has been
run, it is no longer callback data. */
params->flags &= ~IsCallbackData;
ReleaseBufferParams (params);
return;
}
/* Mark the params object as no longer being callback data. */
params->flags &= ~IsCallbackData;
/* Create the buffer. */
buffer = CreateBufferFor (params, render_buffer, 0);
/* If buffer is NULL, then the failure message will already have
been sent. */
if (!buffer)
return;
/* Send the buffer to the client. */
zwp_linux_buffer_params_v1_send_created (params->resource,
buffer->resource);
}
static void
CreateFailed (void *data)
{
BufferParams *params;
/* Creation failed. If no resource is attached, simply free params.
Otherwise, send failed and wait for the client to destroy it. */
params = data;
params->flags &= ~IsCallbackData;
if (!params->resource)
ReleaseBufferParams (params);
else
zwp_linux_buffer_params_v1_send_failed (params->resource);
}
static void
Create (struct wl_client *client, struct wl_resource *resource, int32_t width,
int32_t height, uint32_t format, uint32_t flags)
{
BufferParams *params;
DmaBufAttributes attributes;
int num_buffers, i;
uint32_t mod_high, mod_low;
uint32_t all_flags;
params = wl_resource_get_user_data (resource);
if (params->flags & IsUsed)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
"the given params resource has already been used.");
return;
}
/* Now mark the params resource as inert. */
params->flags |= IsUsed;
/* And find out how many buffers are attached to the temporary set,
along with which modifiers are set. */
num_buffers = ExistingModifier (params, &mod_high, &mod_low);
if (!num_buffers)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"no fds were attached to this resource's temporary set");
goto inert_error;
}
if (params->entries[0].fd == -1)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"no fd attached for plane 0 in the temporary set");
goto inert_error;
}
if ((params->entries[3].fd >= 0 || params->entries[2].fd >= 0)
&& (params->entries[2].fd == -1 || params->entries[1].fd == -1))
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"gap in planes attached to temporary set");
goto inert_error;
}
if (width < 0 || height < 0 || width > 65535 || height > 65535)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
"size out of bounds for X server");
goto inert_error;
}
/* Check that the client did not define any invalid flags. */
all_flags = (ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT
| ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED
| ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST);
if (flags & ~all_flags)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
"invalid dmabuf flags: %u", flags);
goto inert_error;
}
/* Now, see if the format and modifier pair specified is supported.
If it is not, post an error for version 4 resources, and fail
creation for earlier ones. */
if (!IsFormatSupported (format, mod_high, mod_low))
{
if (wl_resource_get_version (resource) >= 4)
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
"invalid format/modifiers specified for version 4"
" resource");
else
zwp_linux_buffer_params_v1_send_failed (resource);
goto inert_error;
}
/* Clear the buffer attributes structure. It has to be initialized
even if not completely used. */
memset (&attributes, 0, sizeof attributes);
/* Copy the file descriptors and other plane-specific information
into the buffer attributes structure. They are not closed here;
the renderer should do that itself upon both success and
failure. */
for (i = 0; i < num_buffers; ++i)
{
attributes.fds[i] = params->entries[i].fd;
attributes.strides[i] = params->entries[i].stride;
attributes.offsets[i] = params->entries[i].offset;
}
/* Provide the specified modifier in the buffer attributes
structure. */
attributes.modifier = ((uint64_t) mod_high << 32) | mod_low;
/* Set the number of planes specified. */
attributes.n_planes = num_buffers;
/* And the dimensions of the buffer. */
attributes.width = width;
attributes.height = height;
/* The format. */
attributes.drm_format = format;
/* The flags. */
attributes.flags = flags;
/* Set params->width and params->height. They are used by
CreateBufferFor. */
params->width = width;
params->height = height;
/* Mark params as callback and post asynchronous creation. This is
so that the parameters will not be destroyed until one of the
callback functions are called. */
params->flags |= IsCallbackData;
/* Post asynchronous creation and return. */
RenderBufferFromDmaBufAsync (&attributes, CreateSucceeded,
CreateFailed, params);
return;
inert_error:
/* We must also close every file descriptor attached here, as XCB
has not done that for us yet. */
CloseFdsEarly (params);
}
static void
CreatePlaceholderBuffer (struct wl_client *client, uint32_t id,
int width, int height)
{
Buffer *buffer;
Bool error;
/* Create a placeholder buffer for the given client and dimensions.
The buffer is a completely transparent single-pixel buffer.
Such a placeholder buffer is required when
zwp_linux_buffer_params_v1.failed is posted in response to a
failed create_immed request. */
buffer = XLCalloc (1, sizeof *buffer);
buffer->resource = wl_resource_create (client, &wl_buffer_interface,
1, id);
buffer->is_fallback = True;
if (!buffer->resource)
{
wl_client_post_no_memory (client);
XLFree (buffer);
return;
}
/* A single pixel buffer is used because it is safe to read outside
them. */
error = False;
buffer->render_buffer = RenderBufferFromSinglePixel (0, 0, 0, 0,
&error);
if (error)
{
/* If an error occured, then we are probably out of memory. */
wl_resource_destroy (buffer->resource);
XLFree (buffer);
wl_client_post_no_memory (client);
return;
}
/* Add a reference. */
buffer->refcount = 1;
/* Set the width and height. */
buffer->width = width;
buffer->height = height;
/* Initialize virtual functions. */
buffer->buffer.funcs.retain = RetainBufferFunc;
buffer->buffer.funcs.dereference = DereferenceBufferFunc;
buffer->buffer.funcs.get_buffer = GetBufferFunc;
buffer->buffer.funcs.width = WidthFunc;
buffer->buffer.funcs.height = HeightFunc;
buffer->buffer.funcs.release = ReleaseBufferFunc;
wl_resource_set_implementation (buffer->resource,
&zwp_linux_dmabuf_v1_buffer_impl,
buffer, HandleBufferResourceDestroy);
}
static void
CreateImmed (struct wl_client *client, struct wl_resource *resource, uint32_t id,
int32_t width, int32_t height, uint32_t format, uint32_t flags)
{
BufferParams *params;
DmaBufAttributes attributes;
Bool error;
int num_buffers, i;
uint32_t mod_high, mod_low;
uint32_t all_flags;
RenderBuffer buffer;
params = wl_resource_get_user_data (resource);
if (params->flags & IsUsed)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
"the given params resource has already been used.");
return;
}
/* Now mark the params resource as inert. */
params->flags |= IsUsed;
/* And find out how many buffers are attached to the temporary set,
along with which modifiers are set. */
num_buffers = ExistingModifier (params, &mod_high, &mod_low);
if (!num_buffers)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"no fds were attached to this resource's temporary set");
goto inert_error;
}
if (params->entries[0].fd == -1)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"no fd attached for plane 0 in the temporary set");
goto inert_error;
}
if ((params->entries[3].fd >= 0 || params->entries[2].fd >= 0)
&& (params->entries[2].fd == -1 || params->entries[1].fd == -1))
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
"gap in planes attached to temporary set");
goto inert_error;
}
if (width < 0 || height < 0 || width > 65535 || height > 65535)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
"size out of bounds for X server");
goto inert_error;
}
/* Check that the client did not define any invalid flags. */
all_flags = (ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT
| ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED
| ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST);
if (flags & ~all_flags)
{
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
"invalid dmabuf flags: %u", flags);
goto inert_error;
}
/* Now, see if the format and modifier pair specified is supported.
If it is not, post an error for version 4 resources, and fail
creation for earlier ones. */
if (!IsFormatSupported (format, mod_high, mod_low))
{
if (wl_resource_get_version (resource) >= 4)
wl_resource_post_error (resource,
ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT,
"invalid format/modifiers specified for version 4"
" resource");
else
{
zwp_linux_buffer_params_v1_send_failed (resource);
CreatePlaceholderBuffer (client, id, width, height);
}
goto inert_error;
}
/* Clear the buffer attributes structure. It has to be initialized
even if not completely used. */
memset (&attributes, 0, sizeof attributes);
/* Copy the file descriptors and other plane-specific information
into the buffer attributes structure. They are not closed here;
the renderer should do that itself upon both success and
failure. */
for (i = 0; i < num_buffers; ++i)
{
attributes.fds[i] = params->entries[i].fd;
attributes.strides[i] = params->entries[i].stride;
attributes.offsets[i] = params->entries[i].offset;
}
/* Provide the specified modifier in the buffer attributes
structure. */
attributes.modifier = ((uint64_t) mod_high << 32) | mod_low;
/* Set the number of planes specified. */
attributes.n_planes = num_buffers;
/* And the dimensions of the buffer. */
attributes.width = width;
attributes.height = height;
/* The format. */
attributes.drm_format = format;
/* The flags. */
attributes.flags = flags;
/* Set params->width and params->height. They are used by
CreateBufferFor. */
params->width = width;
params->height = height;
/* Now, try to create the buffer. Send failed should it actually
fail. */
error = False;
buffer = RenderBufferFromDmaBuf (&attributes, &error);
if (error)
{
/* The fds should have been closed by the renderer. */
zwp_linux_buffer_params_v1_send_failed (resource);
CreatePlaceholderBuffer (client, id, width, height);
}
else
/* Otherwise, buffer creation was successful. Create the buffer
for the id. */
CreateBufferFor (params, buffer, id);
return;
inert_error:
/* We must also close every file descriptor attached here, as XCB
has not done that for us yet. */
CloseFdsEarly (params);
}
static struct zwp_linux_buffer_params_v1_interface zwp_linux_buffer_params_v1_impl =
{
.destroy = DestroyBufferParams,
.add = Add,
.create = Create,
.create_immed = CreateImmed,
};
static void
CreateParams (struct wl_client *client, struct wl_resource *resource,
uint32_t id)
{
BufferParams *params;
int i;
params = XLSafeMalloc (sizeof *params);
if (!params)
{
wl_resource_post_no_memory (resource);
return;
}
memset (params, 0, sizeof *params);
params->resource
= wl_resource_create (client, &zwp_linux_buffer_params_v1_interface,
wl_resource_get_version (resource), id);
if (!params->resource)
{
XLFree (params);
wl_resource_post_no_memory (resource);
return;
}
wl_resource_set_implementation (params->resource,
&zwp_linux_buffer_params_v1_impl,
params, HandleParamsResourceDestroy);
/* Initialize all fds to -1. */
for (i = 0; i < ArrayElements (params->entries); ++i)
params->entries[i].fd = -1;
}
static void
Destroy (struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static struct zwp_linux_dmabuf_feedback_v1_interface zld_feedback_v1_impl =
{
.destroy = Destroy,
};
static void
MakeFeedback (struct wl_client *client, struct wl_resource *resource,
uint32_t id)
{
struct wl_resource *feedback;
struct wl_array main_device_array, format_array, array;
int i, provider;
ptrdiff_t format_array_size;
uint16_t *format_array_data;
feedback = wl_resource_create (client,
&zwp_linux_dmabuf_feedback_v1_interface,
wl_resource_get_version (resource), id);
if (!feedback)
{
wl_resource_post_no_memory (resource);
return;
}
wl_resource_set_implementation (feedback, &zld_feedback_v1_impl,
NULL, NULL);
/* Now, send the relevant information. This should eventually be
dynamically updated, but we don't support that yet. */
/* First, send the format table. */
zwp_linux_dmabuf_feedback_v1_send_format_table (feedback,
format_table_fd,
format_table_size);
/* Next, send the main device. The first provider returned by
RRGetProviders is considered to be the main device. */
main_device_array.size = sizeof drm_device_nodes[0];
main_device_array.data = &drm_device_nodes[0];
main_device_array.alloc = main_device_array.size;
zwp_linux_dmabuf_feedback_v1_send_main_device (feedback,
&main_device_array);
/* Then, send the one tranche for each device. */
for (provider = 0; provider < num_device_nodes; ++provider)
{
array.size = sizeof drm_device_nodes[provider];
array.data = &drm_device_nodes[provider];
array.alloc = array.size;
zwp_linux_dmabuf_feedback_v1_send_tranche_target_device (feedback,
&array);
/* Populate the formats array with the contents of the format
table, and send it to the client. */
format_array_size = format_table_size / sizeof (FormatModifierPair);
format_array.size = format_array_size * sizeof (uint16_t);
format_array.data = format_array_data = alloca (format_array.size);
/* This must be reset too. */
format_array.alloc = format_array.size;
/* Simply announce every format to the client. */
for (i = 0; i < format_array_size; ++i)
format_array_data[i] = i;
zwp_linux_dmabuf_feedback_v1_send_tranche_formats (feedback,
&format_array);
/* Send flags. We don't currently support direct scanout, so send
nothing. */
zwp_linux_dmabuf_feedback_v1_send_tranche_flags (feedback, 0);
/* Mark the end of the tranche. */
zwp_linux_dmabuf_feedback_v1_send_tranche_done (feedback);
}
}
static void
GetDefaultFeedback (struct wl_client *client, struct wl_resource *resource,
uint32_t id)
{
MakeFeedback (client, resource, id);
}
static void
GetSurfaceFeedback (struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *surface_resource)
{
MakeFeedback (client, resource, id);
}
static struct zwp_linux_dmabuf_v1_interface zwp_linux_dmabuf_v1_impl =
{
.create_params = CreateParams,
.destroy = Destroy,
.get_default_feedback = GetDefaultFeedback,
.get_surface_feedback = GetSurfaceFeedback,
};
static void
SendSupportedFormats (struct wl_resource *resource)
{
int i;
uint64_t modifier;
for (i = 0; i < n_drm_formats; ++i)
{
if (wl_resource_get_version (resource) < 3)
{
/* Send a legacy format message, but only as long as the
format uses the default (invalid) modifier. */
if (supported_formats[i].drm_modifier == DRM_FORMAT_MOD_INVALID)
zwp_linux_dmabuf_v1_send_format (resource,
supported_formats[i].drm_format);
}
else
{
/* This client supports modifiers, so send everything. */
modifier = supported_formats[i].drm_modifier;
zwp_linux_dmabuf_v1_send_modifier (resource,
supported_formats[i].drm_format,
ModifierHigh (modifier),
ModifierLow (modifier));
}
}
}
static void
HandleBind (struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct wl_resource *resource;
resource = wl_resource_create (client, &zwp_linux_dmabuf_v1_interface,
version, id);
if (!resource)
{
wl_client_post_no_memory (client);
return;
}
wl_resource_set_implementation (resource, &zwp_linux_dmabuf_v1_impl,
NULL, NULL);
if (version < 4)
/* Versions later than 4 use the format table. */
SendSupportedFormats (resource);
}
static Bool
InitDrmDevices (void)
{
/* These can either be master nodes or render nodes. */
drm_device_nodes = RenderGetRenderDevices (&num_device_nodes);
return num_device_nodes > 0;
}
static ssize_t
WriteFormatTable (void)
{
int fd, i;
ssize_t written;
FormatModifierPair pair;
/* Before writing the format table, make sure the DRM device node
can be obtained. */
if (!InitDrmDevices ())
{
fprintf (stderr, "Failed to get direct rendering device node. "
"Hardware acceleration will probably be unavailable.\n");
return -1;
}
fd = XLOpenShm ();
if (fd < 0)
{
fprintf (stderr, "Failed to allocate format table fd. "
"Hardware acceleration will probably be unavailable.\n");
return -1;
}
written = 0;
/* Write each format-modifier pair. */
for (i = 0; i < n_drm_formats; ++i)
{
pair.format = supported_formats[i].drm_format;
pair.padding = 0;
pair.modifier = supported_formats[i].drm_modifier;
if (write (fd, &pair, sizeof pair) != sizeof pair)
/* Writing the modifier pair failed. Punt. */
goto cancel;
/* Now tell the caller how much was written. */
written += sizeof pair;
}
format_table_fd = fd;
return written;
cancel:
close (fd);
return -1;
}
static Bool
ReadSupportedFormats (void)
{
/* Read supported formats from the renderer. If none are supported,
don't initialize dmabuf. */
supported_formats = RenderGetDrmFormats (&n_drm_formats);
return n_drm_formats > 0;
}
void
XLInitDmabuf (void)
{
ssize_t size;
/* First, initialize supported formats. */
if (!ReadSupportedFormats ())
return;
/* And try to create the format table. */
size = WriteFormatTable ();
global_dmabuf = wl_global_create (compositor.wl_display,
&zwp_linux_dmabuf_v1_interface,
/* If writing the format table
failed, don't announce support
for version 4. */
size >= 0 ? 4 : 3, NULL, HandleBind);
/* If the format table was successfully created, set its size. */
format_table_size = size;
}