2022-07-14 17:42:14 +03:00
|
|
|
/*
|
|
|
|
* Copyright 2022 Collabora, Ltd.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <libweston/libweston.h>
|
|
|
|
#include <libweston/weston-log.h>
|
|
|
|
#include "libweston-internal.h"
|
|
|
|
#include "output-capture.h"
|
|
|
|
#include "pixel-formats.h"
|
|
|
|
#include "shared/helpers.h"
|
|
|
|
#include "shared/weston-drm-fourcc.h"
|
|
|
|
#include "shared/xalloc.h"
|
|
|
|
#include "weston-output-capture-server-protocol.h"
|
|
|
|
|
|
|
|
/* Lifetimes
|
|
|
|
*
|
|
|
|
* weston_output_capture_info is created at weston_output enable, and
|
|
|
|
* destroyed at weston_output disable. It maintains lists of
|
|
|
|
* weston_capture_source and weston_capture_task.
|
|
|
|
*
|
|
|
|
* Protocol request weston_capture_v1.create creates weston_capture_source
|
|
|
|
* whose lifetime is equal to the weston_capture_source_v1 protocol object
|
|
|
|
* (wl_resource) lifetime.
|
|
|
|
*
|
|
|
|
* weston_capture_source is associated with a weston_output. When the
|
|
|
|
* weston_output is disabled, weston_capture_source is removed from the list
|
|
|
|
* in weston_output_capture_info and any pending task is retired as failed.
|
|
|
|
* Furhter capture attempts on the source will be immediately failed.
|
|
|
|
*
|
|
|
|
* Protocol request weston_capture_source_v1.capture creates
|
|
|
|
* weston_capture_task, if the weston_capture_source still has its output and
|
|
|
|
* no pending task. weston_capture_task becomes the pending task for the
|
|
|
|
* weston_capture_source, and is added to the list in
|
|
|
|
* weston_output_capture_info. Retiring weston_capture_task destroys it.
|
|
|
|
*
|
|
|
|
* Each weston_capture_task is associated with a wl_buffer (weston_buffer).
|
|
|
|
* If the buffer is destroyed, the task is retired as failed.
|
|
|
|
*
|
|
|
|
* Operation
|
|
|
|
*
|
|
|
|
* Each weston_capture_source has a "pixel source" property. Pixel source
|
|
|
|
* describes what the capture shall actually contain. See
|
|
|
|
* weston_capture_v1.create request in the protocol specification.
|
|
|
|
* One pixel source can be provided by at most one component at a time.
|
|
|
|
*
|
|
|
|
* Whenever a renderer or DRM-backend is repainting an output, they will
|
|
|
|
* use weston_output_pull_capture_task() at the appropriate stages to see
|
|
|
|
* if there are any capture tasks to be serviced for a specific pixel source.
|
|
|
|
* The renderer or DRM-backend must then retire the returned tasks by either
|
|
|
|
* failing or completing them.
|
|
|
|
*
|
|
|
|
* When an output repaint completes, no weston_capture_task shall remain in
|
|
|
|
* the list. Renderers or backends could stash them in their own lists though.
|
|
|
|
*
|
|
|
|
* In order to allow clients to allocate correctly sized and formatted buffers
|
|
|
|
* to hold captured images, weston_output_capture_info maintains the current
|
|
|
|
* size and format for each type of pixel source. Renderers and DRM-backend
|
|
|
|
* who provide pixel sources are also responsible for keeping the buffer
|
|
|
|
* requirements information up-to-date with
|
|
|
|
* weston_output_update_capture_info().
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** Implementation of weston_capture_source_v1 protocol object */
|
|
|
|
struct weston_capture_source {
|
|
|
|
struct wl_resource *resource;
|
|
|
|
|
|
|
|
/* struct weston_output_capture_info::capture_source_list */
|
|
|
|
struct wl_list link;
|
|
|
|
|
|
|
|
enum weston_output_capture_source pixel_source;
|
|
|
|
|
|
|
|
/* weston_output_capture_info_destroy() will reset this. */
|
|
|
|
struct weston_output *output;
|
|
|
|
|
|
|
|
struct weston_capture_task *pending;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** A pending task to capture an output */
|
|
|
|
struct weston_capture_task {
|
|
|
|
/* We get cleaned up through owner->pending pointing to us. */
|
|
|
|
struct weston_capture_source *owner;
|
|
|
|
|
|
|
|
/* struct weston_output_capture_info::pending_capture_list */
|
|
|
|
struct wl_list link;
|
|
|
|
|
|
|
|
struct weston_buffer *buffer;
|
|
|
|
struct wl_listener buffer_resource_destroy_listener;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Buffer requirements broadcasting for a pixel source */
|
|
|
|
struct weston_output_capture_source_info {
|
|
|
|
enum weston_output_capture_source pixel_source;
|
|
|
|
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
uint32_t drm_format;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Capture records for an output */
|
|
|
|
struct weston_output_capture_info {
|
|
|
|
/* struct weston_capture_task::link */
|
|
|
|
struct wl_list pending_capture_list;
|
|
|
|
|
|
|
|
/* struct weston_capture_source::link */
|
|
|
|
struct wl_list capture_source_list;
|
|
|
|
|
|
|
|
struct weston_output_capture_source_info source_info[WESTON_OUTPUT_CAPTURE_SOURCE__COUNT];
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Create capture tracking information on weston_output enable */
|
|
|
|
struct weston_output_capture_info *
|
|
|
|
weston_output_capture_info_create(void)
|
|
|
|
{
|
|
|
|
struct weston_output_capture_info *ci;
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
ci = xzalloc(sizeof *ci);
|
|
|
|
|
|
|
|
wl_list_init(&ci->pending_capture_list);
|
|
|
|
wl_list_init(&ci->capture_source_list);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize to no sources available by leaving
|
|
|
|
* width, height and drm_format as zero.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < ARRAY_LENGTH(ci->source_info); i++)
|
|
|
|
ci->source_info[i].pixel_source = i;
|
|
|
|
|
|
|
|
return ci;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Clean up capture tracking information on weston_output disable */
|
|
|
|
void
|
|
|
|
weston_output_capture_info_destroy(struct weston_output_capture_info **cip)
|
|
|
|
{
|
|
|
|
struct weston_output_capture_info *ci = *cip;
|
|
|
|
struct weston_capture_source *csrc, *tmp;
|
|
|
|
|
|
|
|
assert(ci);
|
|
|
|
|
|
|
|
/* Unlink sources. They get destroyed by their wl_resource later. */
|
|
|
|
wl_list_for_each_safe(csrc, tmp, &ci->capture_source_list, link) {
|
|
|
|
csrc->output = NULL;
|
|
|
|
|
|
|
|
wl_list_remove(&csrc->link);
|
|
|
|
wl_list_init(&csrc->link);
|
|
|
|
|
|
|
|
if (csrc->pending)
|
|
|
|
weston_capture_task_retire_failed(csrc->pending, "output removed");
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(wl_list_empty(&ci->pending_capture_list));
|
|
|
|
|
|
|
|
free(ci);
|
|
|
|
*cip = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Assert that all capture tasks were taken
|
|
|
|
*
|
|
|
|
* This is called at the end of a weston_output repaint cycle when the renderer
|
|
|
|
* and the backend have had their chance to service all pending capture tasks.
|
|
|
|
* The remaining tasks would not be serviced by anything, so make sure none
|
|
|
|
* linger.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
weston_output_capture_info_repaint_done(struct weston_output_capture_info *ci)
|
|
|
|
{
|
|
|
|
assert(wl_list_empty(&ci->pending_capture_list));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
source_info_is_available(const struct weston_output_capture_source_info *csi)
|
|
|
|
{
|
|
|
|
return csi->width > 0 && csi->height > 0 &&
|
|
|
|
csi->drm_format != DRM_FORMAT_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
capture_info_send_source_info(struct weston_output_capture_info *ci,
|
|
|
|
struct weston_output_capture_source_info *csi)
|
|
|
|
{
|
|
|
|
struct weston_capture_source *csrc;
|
|
|
|
|
|
|
|
wl_list_for_each(csrc, &ci->capture_source_list, link) {
|
|
|
|
if (csrc->pixel_source != csi->pixel_source)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
weston_capture_source_v1_send_format(csrc->resource,
|
|
|
|
csi->drm_format);
|
|
|
|
weston_capture_source_v1_send_size(csrc->resource,
|
|
|
|
csi->width, csi->height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct weston_output_capture_source_info *
|
|
|
|
capture_info_get_csi(struct weston_output_capture_info *ci,
|
|
|
|
enum weston_output_capture_source src)
|
|
|
|
{
|
|
|
|
int srcidx = src;
|
|
|
|
|
|
|
|
assert(ci);
|
|
|
|
assert(srcidx >= 0 && srcidx < (int)ARRAY_LENGTH(ci->source_info));
|
|
|
|
|
|
|
|
return &ci->source_info[srcidx];
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Update capture requirements broadcast to clients
|
|
|
|
*
|
|
|
|
* This is called by renderers and DRM-backend to update the buffer
|
|
|
|
* requirements information that is delivered to clients wanting to capture
|
|
|
|
* the output. This is how clients know what size and format buffer they
|
|
|
|
* need to allocate for the given output and pixel source.
|
|
|
|
*
|
|
|
|
* \param output The output whose capture info to update.
|
|
|
|
* \param src The source type on the output.
|
|
|
|
* \param width The new buffer width.
|
|
|
|
* \param height The new buffer height.
|
2023-01-26 14:07:15 +03:00
|
|
|
* \param format The new pixel format.
|
2022-07-14 17:42:14 +03:00
|
|
|
*
|
|
|
|
* If any one of width, height or format is zero/NULL, the source becomes
|
|
|
|
* unavailable to clients. Otherwise the source becomes available.
|
|
|
|
*
|
|
|
|
* Initially all sources are unavailable.
|
|
|
|
*/
|
|
|
|
WL_EXPORT void
|
|
|
|
weston_output_update_capture_info(struct weston_output *output,
|
|
|
|
enum weston_output_capture_source src,
|
2023-01-26 14:07:15 +03:00
|
|
|
int width, int height,
|
|
|
|
const struct pixel_format_info *format)
|
2022-07-14 17:42:14 +03:00
|
|
|
{
|
|
|
|
struct weston_output_capture_info *ci = output->capture_info;
|
|
|
|
struct weston_output_capture_source_info *csi;
|
|
|
|
|
|
|
|
csi = capture_info_get_csi(ci, src);
|
|
|
|
|
|
|
|
if (csi->width == width &&
|
|
|
|
csi->height == height &&
|
2023-01-26 14:07:15 +03:00
|
|
|
csi->drm_format == format->format)
|
2022-07-14 17:42:14 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
csi->width = width;
|
|
|
|
csi->height = height;
|
2023-01-26 14:07:15 +03:00
|
|
|
csi->drm_format = format->format;
|
2022-07-14 17:42:14 +03:00
|
|
|
|
|
|
|
if (source_info_is_available(csi)) {
|
|
|
|
capture_info_send_source_info(ci, csi);
|
|
|
|
} else {
|
|
|
|
struct weston_capture_task *ct, *tmp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This source just became unavailable, so fail all pending
|
|
|
|
* tasks using it.
|
|
|
|
*/
|
|
|
|
wl_list_for_each_safe(ct, tmp,
|
|
|
|
&ci->pending_capture_list, link) {
|
|
|
|
if (ct->owner->pixel_source != csi->pixel_source)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
weston_capture_task_retire_failed(ct, "source removed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
buffer_is_compatible(struct weston_buffer *buffer,
|
|
|
|
struct weston_output_capture_source_info *csi)
|
|
|
|
{
|
|
|
|
return buffer->width == csi->width &&
|
|
|
|
buffer->height == csi->height &&
|
|
|
|
buffer->pixel_format->format == csi->drm_format &&
|
|
|
|
buffer->format_modifier == DRM_FORMAT_MOD_LINEAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_capture_task_destroy(struct weston_capture_task *ct)
|
|
|
|
{
|
|
|
|
if (ct->owner->pixel_source != WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK &&
|
|
|
|
ct->owner->output)
|
|
|
|
weston_output_disable_planes_decr(ct->owner->output);
|
|
|
|
|
|
|
|
assert(ct->owner->pending == ct);
|
|
|
|
ct->owner->pending = NULL;
|
|
|
|
wl_list_remove(&ct->link);
|
|
|
|
wl_list_remove(&ct->buffer_resource_destroy_listener.link);
|
|
|
|
free(ct);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_capture_task_buffer_destroy_handler(struct wl_listener *l, void *data)
|
|
|
|
{
|
|
|
|
struct weston_capture_task *ct =
|
|
|
|
wl_container_of(l, ct, buffer_resource_destroy_listener);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Client destroyed the wl_buffer object. By protocol spec, this is
|
|
|
|
* undefined behaviour. Do the most sensible thing.
|
|
|
|
*/
|
|
|
|
weston_capture_task_retire_failed(ct, "wl_buffer destroyed");
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct weston_capture_task *
|
|
|
|
weston_capture_task_create(struct weston_capture_source *csrc,
|
|
|
|
struct weston_buffer *buffer)
|
|
|
|
{
|
|
|
|
struct weston_capture_task *ct;
|
|
|
|
|
|
|
|
ct = xzalloc(sizeof *ct);
|
|
|
|
|
|
|
|
ct->owner = csrc;
|
|
|
|
/* Owner will explicitly destroy us if the owner gets destroyed. */
|
|
|
|
|
|
|
|
ct->buffer = buffer;
|
|
|
|
ct->buffer_resource_destroy_listener.notify = weston_capture_task_buffer_destroy_handler;
|
|
|
|
wl_resource_add_destroy_listener(buffer->resource,
|
|
|
|
&ct->buffer_resource_destroy_listener);
|
|
|
|
|
|
|
|
wl_list_insert(&csrc->output->capture_info->pending_capture_list,
|
|
|
|
&ct->link);
|
|
|
|
|
|
|
|
if (ct->owner->pixel_source != WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK)
|
|
|
|
weston_output_disable_planes_incr(ct->owner->output);
|
|
|
|
|
|
|
|
return ct;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
capture_is_authorized(struct weston_capture_source *csrc)
|
|
|
|
{
|
|
|
|
struct weston_compositor *compositor = csrc->output->compositor;
|
|
|
|
const struct weston_output_capture_client who = {
|
|
|
|
.client = wl_resource_get_client(csrc->resource),
|
|
|
|
.output = csrc->output,
|
|
|
|
};
|
|
|
|
struct weston_output_capture_attempt att = {
|
|
|
|
.who = &who,
|
|
|
|
.authorized = false,
|
|
|
|
.denied = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
wl_signal_emit(&compositor->output_capture.ask_auth, &att);
|
|
|
|
|
|
|
|
return att.authorized && !att.denied;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Fetch the next capture task
|
|
|
|
*
|
|
|
|
* This is used by renderers and DRM-backend to get the next capture task
|
|
|
|
* they want to service. Only tasks for the given pixel source will be
|
|
|
|
* returned.
|
|
|
|
*
|
|
|
|
* Width, height and drm_format are for ensuring that
|
|
|
|
* weston_output_update_capture_info() was up-to-date before this.
|
|
|
|
*
|
|
|
|
* \return A capture task, or NULL if no more tasks.
|
|
|
|
*/
|
|
|
|
WL_EXPORT struct weston_capture_task *
|
|
|
|
weston_output_pull_capture_task(struct weston_output *output,
|
|
|
|
enum weston_output_capture_source src,
|
|
|
|
int width, int height,
|
|
|
|
const struct pixel_format_info *format)
|
|
|
|
{
|
|
|
|
struct weston_output_capture_info *ci = output->capture_info;
|
|
|
|
struct weston_output_capture_source_info *csi;
|
|
|
|
struct weston_capture_task *ct, *tmp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the capture provider (renderers, DRM-backend) called
|
|
|
|
* weston_output_update_capture_info() if something changed, so that
|
|
|
|
* the 'retry' event keeps its promise of size/format events been
|
|
|
|
* already sent.
|
|
|
|
*/
|
|
|
|
csi = capture_info_get_csi(ci, src);
|
|
|
|
assert(csi->width == width);
|
|
|
|
assert(csi->height == height);
|
|
|
|
assert(csi->drm_format == format->format);
|
|
|
|
|
|
|
|
wl_list_for_each_safe(ct, tmp, &ci->pending_capture_list, link) {
|
|
|
|
assert(ct->owner->output == output);
|
|
|
|
|
|
|
|
if (ct->owner->pixel_source != src)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!capture_is_authorized(ct->owner)) {
|
|
|
|
weston_capture_task_retire_failed(ct, "unauthorized");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell the client to retry, if requirements changed after
|
|
|
|
* the task was filed.
|
|
|
|
*/
|
|
|
|
if (!buffer_is_compatible(ct->buffer, csi)) {
|
|
|
|
weston_capture_source_v1_send_retry(ct->owner->resource);
|
|
|
|
weston_capture_task_destroy(ct);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pass ct ownership to the caller */
|
|
|
|
wl_list_remove(&ct->link);
|
|
|
|
wl_list_init(&ct->link);
|
|
|
|
|
|
|
|
return ct;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-03-28 15:30:21 +03:00
|
|
|
/** Check if any renderer-based capture tasks are waiting on the output */
|
2022-07-14 17:42:14 +03:00
|
|
|
WL_EXPORT bool
|
2023-03-28 15:30:21 +03:00
|
|
|
weston_output_has_renderer_capture_tasks(struct weston_output *output)
|
2022-07-14 17:42:14 +03:00
|
|
|
{
|
|
|
|
struct weston_output_capture_info *ci = output->capture_info;
|
2023-03-28 15:30:21 +03:00
|
|
|
struct weston_capture_task *ct;
|
2022-07-14 17:42:14 +03:00
|
|
|
|
2023-03-28 15:30:21 +03:00
|
|
|
wl_list_for_each(ct, &ci->pending_capture_list, link)
|
|
|
|
if (ct->owner->pixel_source != WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK)
|
|
|
|
return true;
|
|
|
|
return false;
|
2022-07-14 17:42:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the destination buffer */
|
|
|
|
WL_EXPORT struct weston_buffer *
|
|
|
|
weston_capture_task_get_buffer(struct weston_capture_task *ct)
|
|
|
|
{
|
|
|
|
return ct->buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Signal completion of the capture task
|
|
|
|
*
|
|
|
|
* Sends 'complete' protocol event to the client, and destroys the task.
|
|
|
|
*/
|
|
|
|
WL_EXPORT void
|
|
|
|
weston_capture_task_retire_complete(struct weston_capture_task *ct)
|
|
|
|
{
|
|
|
|
weston_capture_source_v1_send_complete(ct->owner->resource);
|
|
|
|
weston_capture_task_destroy(ct);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Signal failure of the capture task
|
|
|
|
*
|
|
|
|
* Sends 'failed' protocol event to the client, and destroys the task.
|
|
|
|
*/
|
|
|
|
WL_EXPORT void
|
|
|
|
weston_capture_task_retire_failed(struct weston_capture_task *ct,
|
|
|
|
const char *err_msg)
|
|
|
|
{
|
|
|
|
weston_capture_source_v1_send_failed(ct->owner->resource, err_msg);
|
|
|
|
weston_capture_task_destroy(ct);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
destroy_capture_source(struct wl_resource *csrc_resource)
|
|
|
|
{
|
|
|
|
struct weston_capture_source *csrc;
|
|
|
|
|
|
|
|
csrc = wl_resource_get_user_data(csrc_resource);
|
|
|
|
assert(csrc_resource == csrc->resource);
|
|
|
|
|
|
|
|
if (csrc->pending)
|
|
|
|
weston_capture_task_destroy(csrc->pending);
|
|
|
|
|
|
|
|
wl_list_remove(&csrc->link);
|
|
|
|
free(csrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_capture_source_v1_destroy(struct wl_client *client,
|
|
|
|
struct wl_resource *csrc_resource)
|
|
|
|
{
|
|
|
|
wl_resource_destroy(csrc_resource);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_capture_source_v1_capture(struct wl_client *client,
|
|
|
|
struct wl_resource *csrc_resource,
|
|
|
|
struct wl_resource *buffer_resource)
|
|
|
|
{
|
|
|
|
struct weston_output_capture_source_info *csi;
|
|
|
|
struct weston_capture_source *csrc;
|
|
|
|
struct weston_buffer *buffer;
|
|
|
|
|
|
|
|
csrc = wl_resource_get_user_data(csrc_resource);
|
|
|
|
assert(csrc_resource == csrc->resource);
|
|
|
|
|
|
|
|
/* A capture task already exists? */
|
|
|
|
if (csrc->pending) {
|
|
|
|
wl_resource_post_error(csrc->resource,
|
|
|
|
WESTON_CAPTURE_SOURCE_V1_ERROR_SEQUENCE,
|
|
|
|
"capture attempted before previous capture retired");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* weston_output disabled after creating the source? */
|
|
|
|
if (!csrc->output) {
|
|
|
|
weston_capture_source_v1_send_failed(csrc->resource, "output removed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Is the pixel source not available? */
|
|
|
|
csi = capture_info_get_csi(csrc->output->capture_info,
|
|
|
|
csrc->pixel_source);
|
|
|
|
if (!source_info_is_available(csi)) {
|
|
|
|
weston_capture_source_v1_send_failed(csrc->resource, "source unavailable");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer = weston_buffer_from_resource(csrc->output->compositor,
|
|
|
|
buffer_resource);
|
|
|
|
if (!buffer) {
|
|
|
|
wl_client_post_no_memory(client);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the buffer not up-to-date with the size and format? */
|
|
|
|
if (!buffer_is_compatible(buffer, csi)) {
|
|
|
|
weston_capture_source_v1_send_retry(csrc->resource);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
csrc->pending = weston_capture_task_create(csrc, buffer);
|
|
|
|
weston_output_schedule_repaint(csrc->output);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct weston_capture_source_v1_interface weston_capture_source_v1_impl = {
|
|
|
|
.destroy = weston_capture_source_v1_destroy,
|
|
|
|
.capture = weston_capture_source_v1_capture,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
pixel_source_from_proto(uint32_t v)
|
|
|
|
{
|
|
|
|
switch (v) {
|
|
|
|
case WESTON_CAPTURE_V1_SOURCE_WRITEBACK:
|
|
|
|
return WESTON_OUTPUT_CAPTURE_SOURCE_WRITEBACK;
|
|
|
|
case WESTON_CAPTURE_V1_SOURCE_FRAMEBUFFER:
|
|
|
|
return WESTON_OUTPUT_CAPTURE_SOURCE_FRAMEBUFFER;
|
|
|
|
case WESTON_CAPTURE_V1_SOURCE_FULL_FRAMEBUFFER:
|
|
|
|
return WESTON_OUTPUT_CAPTURE_SOURCE_FULL_FRAMEBUFFER;
|
|
|
|
case WESTON_CAPTURE_V1_SOURCE_BLENDING:
|
|
|
|
return WESTON_OUTPUT_CAPTURE_SOURCE_BLENDING;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_capture_v1_create(struct wl_client *client,
|
|
|
|
struct wl_resource *capture_resource,
|
|
|
|
struct wl_resource *output_resource,
|
|
|
|
uint32_t source,
|
|
|
|
uint32_t capture_source_new_id)
|
|
|
|
{
|
|
|
|
int32_t isrc = pixel_source_from_proto(source);
|
|
|
|
struct weston_capture_source *csrc;
|
|
|
|
struct weston_head *head;
|
|
|
|
|
|
|
|
if (isrc < 0) {
|
|
|
|
wl_resource_post_error(capture_resource,
|
|
|
|
WESTON_CAPTURE_V1_ERROR_INVALID_SOURCE,
|
|
|
|
"%u is not a valid source", source);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
csrc = zalloc(sizeof *csrc);
|
|
|
|
if (!csrc) {
|
|
|
|
wl_client_post_no_memory(client);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
csrc->pixel_source = isrc;
|
|
|
|
wl_list_init(&csrc->link);
|
|
|
|
|
|
|
|
csrc->resource = wl_resource_create(client,
|
|
|
|
&weston_capture_source_v1_interface,
|
|
|
|
wl_resource_get_version(capture_resource),
|
|
|
|
capture_source_new_id);
|
|
|
|
if (!csrc->resource) {
|
|
|
|
free(csrc);
|
|
|
|
wl_client_post_no_memory(client);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl_resource_set_implementation(csrc->resource,
|
|
|
|
&weston_capture_source_v1_impl,
|
|
|
|
csrc, destroy_capture_source);
|
|
|
|
|
|
|
|
head = weston_head_from_resource(output_resource);
|
|
|
|
if (head) {
|
|
|
|
struct weston_output *output = head->output;
|
|
|
|
struct weston_output_capture_info *ci = output->capture_info;
|
|
|
|
struct weston_output_capture_source_info *csi;
|
|
|
|
|
|
|
|
csi = capture_info_get_csi(ci, csrc->pixel_source);
|
|
|
|
wl_list_insert(&ci->capture_source_list, &csrc->link);
|
|
|
|
|
|
|
|
csrc->output = output;
|
|
|
|
|
|
|
|
if (source_info_is_available(csi))
|
|
|
|
capture_info_send_source_info(ci, csi);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* if (!head) then weston_capture_source_v1_capture() will respond with
|
|
|
|
* the failed event.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weston_capture_v1_destroy(struct wl_client *client,
|
|
|
|
struct wl_resource *resource)
|
|
|
|
{
|
|
|
|
wl_resource_destroy(resource);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct weston_capture_v1_interface weston_capture_v1_impl = {
|
|
|
|
.destroy = weston_capture_v1_destroy,
|
|
|
|
.create = weston_capture_v1_create,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
bind_weston_capture(struct wl_client *client,
|
|
|
|
void *data, uint32_t version, uint32_t id)
|
|
|
|
{
|
|
|
|
struct wl_resource *resource;
|
|
|
|
|
|
|
|
/* Access control is done at capture request. */
|
|
|
|
resource = wl_resource_create(client, &weston_capture_v1_interface,
|
|
|
|
version, id);
|
|
|
|
if (!resource) {
|
|
|
|
wl_client_post_no_memory(client);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wl_resource_set_implementation(resource, &weston_capture_v1_impl,
|
|
|
|
NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
weston_compositor_install_capture_protocol(struct weston_compositor *compositor)
|
|
|
|
{
|
|
|
|
compositor->output_capture.weston_capture_v1 =
|
|
|
|
wl_global_create(compositor->wl_display,
|
|
|
|
&weston_capture_v1_interface,
|
|
|
|
1, NULL, bind_weston_capture);
|
|
|
|
abort_oom_if_null(compositor->output_capture.weston_capture_v1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Add a new authority that may authorize or deny screenshots
|
|
|
|
*
|
|
|
|
* \param compositor The compositor instance.
|
|
|
|
* \param listener The listener to populate, and which will be passed as the
|
|
|
|
* listener to the auth callback.
|
|
|
|
* \param auth The callback function which shall be called every time any
|
|
|
|
* client sends a request to capture an output.
|
|
|
|
*
|
|
|
|
* The callback function \c auth is called with argument \c att. If you
|
|
|
|
* want to authorize the screenshot after inspecting the fields in
|
|
|
|
* \c att->who , you must set \c att->authorized to true. If you want to
|
|
|
|
* deny the screenshot instead, set \c att->denied to true. Otherwise,
|
|
|
|
* do not change anything.
|
|
|
|
*
|
|
|
|
* Any screenshot is carried out only if after iterating through all
|
|
|
|
* authorities \c att->authorized is true and \c att->denied is false.
|
|
|
|
* Both default to false, which forbids screenshots without any authorities.
|
|
|
|
*
|
|
|
|
* You can remove an added authority by \c wl_list_remove(&listener->link) .
|
|
|
|
*/
|
|
|
|
WL_EXPORT void
|
|
|
|
weston_compositor_add_screenshot_authority(struct weston_compositor *compositor,
|
|
|
|
struct wl_listener *listener,
|
|
|
|
void (*auth)(struct wl_listener *l,
|
|
|
|
struct weston_output_capture_attempt *att))
|
|
|
|
{
|
|
|
|
listener->notify = (wl_notify_func_t)auth;
|
|
|
|
wl_signal_add(&compositor->output_capture.ask_auth, listener);
|
|
|
|
}
|