12to11/explicit_synchronization.c
hujianwei 3fe08b5c7c Fix various problems encountered
* egl.c (AddShmFormat): Fix coding style.
* explicit_synchronization.c (HandleSurfaceCommit): Remove
redundant NULL check.
* run.c (RunStep): Flush clients after completing selection
transfers.  Fix sizeof when allocating pollfds.
* seat.c (CancelResizeOperation): Avoid NULL-pointer dereference
when subcompositor is not specified.
* shm.c (DereferencePool, CreatePool): Remove redundant checks
of pool->size; it can never be zero.
* subsurface.c (Setup): Remove redundant NULL check.
* text_input.c (SetCursorRectangle): Fix some statements.
(FindTextSections, EncodeIMString, CheckStyles, ConvertString)
(PreeditString, CommitString): Fix usage of format modifiers in
debug trace code.
* xdata.c (HandleSelectionNotify): Use selection time, not event
time.
* xdg_popup.c (MoveWindow, InternalReposition): Remove various
redundant checks.
2022-10-22 07:13:21 +00:00

489 lines
12 KiB
C
Raw 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 <string.h>
#include "compositor.h"
#include "linux-explicit-synchronization-unstable-v1.h"
struct _Synchronization
{
/* The surface destroy listener. */
DestroyCallback *destroy_listener;
/* The surface. */
Surface *surface;
/* The file descriptor of any pending acquire fence. */
int acquire_fence;
/* Any associated release object. */
SyncRelease *release;
/* The associated resource. */
struct wl_resource *resource;
};
struct _SyncRelease
{
/* The associated surface. */
Surface *surface;
/* The associated synchronization. */
Synchronization *synchronization;
/* The associated resource. */
struct wl_resource *resource;
};
/* The global zwp_linux_explicit_synchronization_v1 object. */
static struct wl_global *explicit_sync_global;
static void
HandleReleaseDestroy (struct wl_resource *resource)
{
SyncRelease *release;
release = wl_resource_get_user_data (resource);
/* If release is attached to a surface, remove it from the
surface. */
if (release->surface)
release->surface->release = NULL;
/* Do the same for the synchronization object. */
if (release->synchronization)
release->synchronization->release = NULL;
/* Free the release object. */
XLFree (release);
}
static void
DestroySynchronization (struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
SetAcquireFence (struct wl_client *client, struct wl_resource *resource,
int32_t fd)
{
Synchronization *synchronization;
synchronization = wl_resource_get_user_data (resource);
#define NoSurface ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_SURFACE
/* If the surface was destroyed, raise such an error. */
if (!synchronization->surface)
{
wl_resource_post_error (resource, NoSurface,
"the surface associated with this"
" resource was destroyed");
goto error;
}
#undef NoSurface
#define DuplicateFence \
ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_FENCE
/* If a fence was already attached, raise that as an errore. */
if (synchronization->acquire_fence != -1)
{
wl_resource_post_error (resource, DuplicateFence,
"another fence has already been attached"
" during this commit cycle");
goto error;
}
#undef DuplicateFence
/* Set the file descriptor and return. */
synchronization->acquire_fence = fd;
return;
error:
close (fd);
}
static void
GetRelease (struct wl_client *client, struct wl_resource *resource,
uint32_t id)
{
Synchronization *synchronization;
SyncRelease *release;
/* The release lifecycle is somewhat like this:
First, it starts out as the `release' field of a Synchronization
resource. When the synchronization is committed, it is moved to
the release field of the surface, and it is detatched from both
once release events are sent. */
synchronization = wl_resource_get_user_data (resource);
#define DuplicateRelease \
ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_DUPLICATE_RELEASE
if (synchronization->release)
{
/* Uncommitted release already exists. Post an error. */
wl_resource_post_error (resource, DuplicateRelease,
"another release has already been acquired"
" during this commit cycle");
return;
}
#undef DuplicateRelease
release = XLSafeMalloc (sizeof *release);
if (!release)
{
wl_resource_post_no_memory (resource);
return;
}
memset (release, 0, sizeof *release);
release->resource
= wl_resource_create (client,
&zwp_linux_buffer_release_v1_interface,
wl_resource_get_version (resource),
id);
if (!release->resource)
{
wl_resource_post_no_memory (resource);
return;
}
/* Attach the release. */
release->synchronization = synchronization;
synchronization->release = release;
/* Set the resource implementation. */
wl_resource_set_implementation (release->resource,
NULL, release,
HandleReleaseDestroy);
}
static void
HandleSurfaceDestroy (void *data)
{
Synchronization *synchronization;
synchronization = data;
/* The surface was destroyed. Mark the object as invalid. */
synchronization->surface = NULL;
synchronization->destroy_listener = NULL;
}
static void
HandleSurfaceCommit (Synchronization *synchronization, Surface *surface)
{
#define NoBuffer ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_NO_BUFFER
if (synchronization->acquire_fence != -1)
{
if (!(surface->pending_state.pending & PendingBuffer
&& surface->pending_state.buffer))
{
/* If no buffer was attached, report an error. */
wl_resource_post_error (synchronization->resource,
NoBuffer, "no buffer attached"
" but acquire fence provided");
close (synchronization->acquire_fence);
synchronization->acquire_fence = -1;
return;
}
if (surface->acquire_fence != -1)
close (surface->acquire_fence);
surface->acquire_fence = synchronization->acquire_fence;
synchronization->acquire_fence = -1;
}
/* Move the release callback to the surface. Assume one does
not already exist. */
XLAssert (surface->release == NULL);
/* surface->release can still end up NULL if no release was
attached. */
surface->release = synchronization->release;
if (surface->release)
{
/* Clear these fields, to detach the release from the
synchronization. */
surface->release->synchronization = NULL;
synchronization->release = NULL;
/* And set this field to attach the release to the
surface. */
surface->release->surface = surface;
if (!(surface->pending_state.pending & PendingBuffer
&& surface->pending_state.buffer))
wl_resource_post_error (synchronization->resource,
NoBuffer, "no buffer attached"
" but release provided");
}
#undef NoBuffer
}
static void
HandleSynchronizationDestroy (struct wl_resource *resource)
{
Synchronization *synchronization;
/* Free the synchronization as it has been destroyed. */
synchronization = wl_resource_get_user_data (resource);
if (synchronization->destroy_listener)
XLSurfaceCancelRunOnFree (synchronization->destroy_listener);
if (synchronization->surface)
/* Also detach it from the surface. */
synchronization->surface->synchronization = NULL;
/* Detach any attached release callback. */
if (synchronization->release)
wl_resource_destroy (synchronization->release->resource);
/* If a fence happens to still be attached, close it. */
if (synchronization->acquire_fence != -1)
close (synchronization->acquire_fence);
XLFree (synchronization);
}
static struct zwp_linux_surface_synchronization_v1_interface synchronization_impl =
{
.destroy = DestroySynchronization,
.set_acquire_fence = SetAcquireFence,
.get_release = GetRelease,
};
static void
Destroy (struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
GetSynchronization (struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *surface_resource)
{
Synchronization *synchronization;
Surface *surface;
surface = wl_resource_get_user_data (surface_resource);
#define SynchronizationAlreadyExists \
ZWP_LINUX_EXPLICIT_SYNCHRONIZATION_V1_ERROR_SYNCHRONIZATION_EXISTS
if (surface->synchronization)
{
/* Don't let clients create more synchronization objects if one
already exists. */
wl_resource_post_error (resource, SynchronizationAlreadyExists,
"synchronization object already exists");
return;
}
#undef SynchronizationAlreadyExists
/* Allocate the synchronization object. */
synchronization = XLSafeMalloc (sizeof *synchronization);
if (!synchronization)
{
wl_resource_post_no_memory (resource);
return;
}
memset (synchronization, 0, sizeof *synchronization);
synchronization->resource
= wl_resource_create (client,
&zwp_linux_surface_synchronization_v1_interface,
wl_resource_get_version (resource), id);
if (!synchronization->resource)
{
XLFree (synchronization);
wl_resource_post_no_memory (resource);
return;
}
/* Now, attach the synchronization object to the surface. */
surface->synchronization = synchronization;
/* And attach a destroy listener. */
synchronization->destroy_listener
= XLSurfaceRunOnFree (surface, HandleSurfaceDestroy,
synchronization);
/* And attach the surface to the synchronization. */
synchronization->surface = surface;
/* Set the implementation. */
wl_resource_set_implementation (synchronization->resource,
&synchronization_impl,
synchronization,
HandleSynchronizationDestroy);
/* Clear initial values. */
synchronization->acquire_fence = -1;
}
static struct zwp_linux_explicit_synchronization_v1_interface explicit_sync_impl =
{
.destroy = Destroy,
.get_synchronization = GetSynchronization,
};
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_explicit_synchronization_v1_interface,
version, id);
if (!resource)
{
wl_client_post_no_memory (client);
return;
}
wl_resource_set_implementation (resource, &explicit_sync_impl,
NULL, NULL);
}
void
XLDestroyRelease (SyncRelease *release)
{
/* Destroying the resoruce will cause the release to be freed. */
wl_resource_destroy (release->resource);
}
void
XLSyncRelease (SyncRelease *release)
{
Bool error;
int fd;
/* Optimization ideas. Every time an shm buffer is attached with a
release object, create new finish_fence for the buffer, that is
signalled whenever the contents are uploaded. Then, just use
that fence here instead. */
error = False;
fd = RenderGetFinishFence (&error);
if (error)
#define InvalidFence ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_INVALID_FENCE
wl_resource_post_error (release->resource, InvalidFence,
"server failed to create finish fence");
#undef InvalidFence
else
{
/* Post a fenced release with the fence. */
zwp_linux_buffer_release_v1_send_fenced_release (release->resource,
fd);
/* Close the file descriptor. */
close (fd);
}
/* Destroy the sync object. */
wl_resource_destroy (release->resource);
}
void
XLSyncCommit (Synchronization *synchronization)
{
HandleSurfaceCommit (synchronization, synchronization->surface);
}
void
XLWaitFence (Surface *surface)
{
RenderFence fence;
Bool error;
if (surface->acquire_fence == -1)
return;
error = False;
/* This is expected to close the file descriptor. */
fence = RenderImportFdFence (surface->acquire_fence, &error);
if (error)
{
#define InvalidFence ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_INVALID_FENCE
/* Importing the fence failed; signal an error to the
server. */
wl_resource_post_error (surface->resource,
InvalidFence, "the specified sync"
" fence could not be imported");
#undef InvalidFence
/* Try closing the file descriptor as well. */
close (surface->acquire_fence);
surface->acquire_fence = -1;
return;
}
surface->acquire_fence = -1;
/* Wait for the fence to be triggered. */
RenderWaitFence (fence);
/* Delete the fence. */
RenderDeleteFence (fence);
}
void
XLInitExplicitSynchronization (void)
{
/* If the renderer does not support explicit synchronization,
return. */
if (!(renderer_flags & SupportsExplicitSync))
return;
explicit_sync_global
= wl_global_create (compositor.wl_display,
&zwp_linux_explicit_synchronization_v1_interface,
2, NULL, HandleBind);
}