forked from 12to11/12to11
Fix subsurface visibility lifecycle
* compositor.h: Update prototypes. * dmabuf.c (MakeFeedback): Fix comment. * shm.c (Pool): New field `flags'. (DereferencePool, ResizePool, CreatePool): Detect whether or not accessing a pool cannot result in SIGBUS, and refrain from adding bus traps if that is the case. * subcompositor.c (IsSkipped, SetSkipped, ClearSkipped): New view state. (SubcompositorUpdateBounds, SubcompositorUpdateBoundsForInsert) (ViewRecomputeChildren, ViewAfterSizeUpdate, ViewMove, ViewFree) (SubcompositorUpdate, SubcompositorExpose, SubcompositorLookupView): Skip "skipped" views. * subsurface.c (AfterParentCommit): Unskip views. (Setup): Mark view as unskipped.
This commit is contained in:
parent
4d2e85d002
commit
085f493150
5 changed files with 190 additions and 14 deletions
|
@ -650,6 +650,8 @@ extern void ViewMove (View *, int, int);
|
||||||
extern void ViewDetach (View *);
|
extern void ViewDetach (View *);
|
||||||
extern void ViewMap (View *);
|
extern void ViewMap (View *);
|
||||||
extern void ViewUnmap (View *);
|
extern void ViewUnmap (View *);
|
||||||
|
extern void ViewSkip (View *);
|
||||||
|
extern void ViewUnskip (View *);
|
||||||
extern void ViewMoveFractional (View *, double, double);
|
extern void ViewMoveFractional (View *, double, double);
|
||||||
|
|
||||||
extern void ViewSetViewport (View *, double, double, double, double,
|
extern void ViewSetViewport (View *, double, double, double, double,
|
||||||
|
|
2
dmabuf.c
2
dmabuf.c
|
@ -831,7 +831,7 @@ static struct zwp_linux_dmabuf_feedback_v1_interface zld_feedback_v1_impl =
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TODO: dynamically switch tranche for surface feedbacks based on the
|
/* TODO: dynamically switch tranche for surface feedbacks based on the
|
||||||
crtc of the provider the surface is in. */
|
provider of the crtc the surface is in. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
MakeFeedback (struct wl_client *client, struct wl_resource *resource,
|
MakeFeedback (struct wl_client *client, struct wl_resource *resource,
|
||||||
|
|
64
shm.c
64
shm.c
|
@ -21,11 +21,17 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include "compositor.h"
|
#include "compositor.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PoolCannotSigbus = 1,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct _Pool
|
typedef struct _Pool
|
||||||
{
|
{
|
||||||
/* The file descriptor corresponding to this pool. */
|
/* The file descriptor corresponding to this pool. */
|
||||||
|
@ -37,6 +43,9 @@ typedef struct _Pool
|
||||||
/* The number of references to this pool. */
|
/* The number of references to this pool. */
|
||||||
int refcount;
|
int refcount;
|
||||||
|
|
||||||
|
/* Various flags. */
|
||||||
|
int flags;
|
||||||
|
|
||||||
/* Pointer to the raw data in this pool. */
|
/* Pointer to the raw data in this pool. */
|
||||||
void *data;
|
void *data;
|
||||||
|
|
||||||
|
@ -83,7 +92,10 @@ DereferencePool (Pool *pool)
|
||||||
|
|
||||||
if (pool->data != (void *) -1
|
if (pool->data != (void *) -1
|
||||||
/* If the pool is of size 0, no busfault was installed. */
|
/* If the pool is of size 0, no busfault was installed. */
|
||||||
&& pool->size)
|
&& pool->size
|
||||||
|
/* If reading from the pool cannot possibly cause SIGBUS, then
|
||||||
|
no bus fault trap was installed. */
|
||||||
|
&& !(pool->flags & PoolCannotSigbus))
|
||||||
XLRemoveBusfault (pool->data);
|
XLRemoveBusfault (pool->data);
|
||||||
|
|
||||||
close (pool->fd);
|
close (pool->fd);
|
||||||
|
@ -339,6 +351,10 @@ ResizePool (struct wl_client *client, struct wl_resource *resource,
|
||||||
{
|
{
|
||||||
Pool *pool;
|
Pool *pool;
|
||||||
void *data;
|
void *data;
|
||||||
|
#ifdef F_GET_SEALS
|
||||||
|
int seals;
|
||||||
|
struct stat statb;
|
||||||
|
#endif
|
||||||
|
|
||||||
pool = wl_resource_get_user_data (resource);
|
pool = wl_resource_get_user_data (resource);
|
||||||
|
|
||||||
|
@ -365,14 +381,27 @@ ResizePool (struct wl_client *client, struct wl_resource *resource,
|
||||||
|
|
||||||
/* Now cancel the existing bus fault handler, should it have been
|
/* Now cancel the existing bus fault handler, should it have been
|
||||||
installed. */
|
installed. */
|
||||||
if (pool->size)
|
if (pool->size && !(pool->flags & PoolCannotSigbus))
|
||||||
XLRemoveBusfault (pool->data);
|
XLRemoveBusfault (pool->data);
|
||||||
|
|
||||||
|
pool->flags = 0;
|
||||||
|
|
||||||
|
/* Recheck whether or not reading from the pool can cause
|
||||||
|
SIGBUS. */
|
||||||
|
#ifdef F_GET_SEALS
|
||||||
|
seals = fcntl (pool->fd, F_GET_SEALS);
|
||||||
|
|
||||||
|
if (seals != -1 && seals & F_SEAL_SHRINK
|
||||||
|
&& fstat (pool->fd, &statb) >= 0
|
||||||
|
&& statb.st_size >= size)
|
||||||
|
pool->flags |= PoolCannotSigbus;
|
||||||
|
#endif
|
||||||
|
|
||||||
pool->size = size;
|
pool->size = size;
|
||||||
pool->data = data;
|
pool->data = data;
|
||||||
|
|
||||||
/* And add a new handler. */
|
/* And add a new handler. */
|
||||||
if (pool->size)
|
if (pool->size && !(pool->flags & PoolCannotSigbus))
|
||||||
XLRecordBusfault (pool->data, pool->size);
|
XLRecordBusfault (pool->data, pool->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,6 +423,18 @@ CreatePool (struct wl_client *client, struct wl_resource *resource,
|
||||||
uint32_t id, int32_t fd, int32_t size)
|
uint32_t id, int32_t fd, int32_t size)
|
||||||
{
|
{
|
||||||
Pool *pool;
|
Pool *pool;
|
||||||
|
#ifdef F_GET_SEALS
|
||||||
|
int seals;
|
||||||
|
struct stat statb;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (size <= 0)
|
||||||
|
{
|
||||||
|
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_STRIDE,
|
||||||
|
"invalid size given to create_pool");
|
||||||
|
close (fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pool = XLSafeMalloc (sizeof *pool);
|
pool = XLSafeMalloc (sizeof *pool);
|
||||||
|
|
||||||
|
@ -411,6 +452,7 @@ CreatePool (struct wl_client *client, struct wl_resource *resource,
|
||||||
{
|
{
|
||||||
XLFree (pool);
|
XLFree (pool);
|
||||||
wl_resource_post_no_memory (resource);
|
wl_resource_post_no_memory (resource);
|
||||||
|
close (fd);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -423,6 +465,7 @@ CreatePool (struct wl_client *client, struct wl_resource *resource,
|
||||||
XLFree (pool);
|
XLFree (pool);
|
||||||
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FD,
|
wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FD,
|
||||||
"mmap: %s", strerror (errno));
|
"mmap: %s", strerror (errno));
|
||||||
|
close (fd);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -432,10 +475,23 @@ CreatePool (struct wl_client *client, struct wl_resource *resource,
|
||||||
|
|
||||||
pool->size = size;
|
pool->size = size;
|
||||||
|
|
||||||
|
/* Try to determine whether or not the accessing the pool data
|
||||||
|
cannot result in SIGBUS, as the file is already larger (or equal
|
||||||
|
in size) to the pool and the size is sealed. */
|
||||||
|
pool->flags = 0;
|
||||||
|
#ifdef F_GET_SEALS
|
||||||
|
seals = fcntl (fd, F_GET_SEALS);
|
||||||
|
|
||||||
|
if (seals != -1 && seals & F_SEAL_SHRINK
|
||||||
|
&& fstat (fd, &statb) >= 0
|
||||||
|
&& statb.st_size >= size)
|
||||||
|
pool->flags |= PoolCannotSigbus;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Begin trapping SIGBUS from this pool. The client may truncate
|
/* Begin trapping SIGBUS from this pool. The client may truncate
|
||||||
the file without telling us, in which case accessing its contents
|
the file without telling us, in which case accessing its contents
|
||||||
will cause crashes. */
|
will cause crashes. */
|
||||||
if (pool->size)
|
if (!(pool->flags & PoolCannotSigbus) && pool->size)
|
||||||
XLRecordBusfault (pool->data, pool->size);
|
XLRecordBusfault (pool->data, pool->size);
|
||||||
|
|
||||||
pool->fd = fd;
|
pool->fd = fd;
|
||||||
|
|
114
subcompositor.c
114
subcompositor.c
|
@ -193,10 +193,14 @@ enum
|
||||||
/* This means that the view and all its inferiors should be
|
/* This means that the view and all its inferiors should be
|
||||||
skipped in bounds computation, input tracking, et cetera. */
|
skipped in bounds computation, input tracking, et cetera. */
|
||||||
ViewIsUnmapped = 1,
|
ViewIsUnmapped = 1,
|
||||||
|
/* This means that the view itself (not including its inferiors)
|
||||||
|
should be skipped for bounds computation and input
|
||||||
|
tracking, etc. */
|
||||||
|
ViewIsSkipped = 1 << 2,
|
||||||
/* This means that the view has a viewport specifying its size,
|
/* This means that the view has a viewport specifying its size,
|
||||||
effectively decoupling its relation to the buffer width and
|
effectively decoupling its relation to the buffer width and
|
||||||
height. */
|
height. */
|
||||||
ViewIsViewported = 1 << 2,
|
ViewIsViewported = 1 << 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IsViewUnmapped(view) \
|
#define IsViewUnmapped(view) \
|
||||||
|
@ -206,6 +210,13 @@ enum
|
||||||
#define ClearUnmapped(view) \
|
#define ClearUnmapped(view) \
|
||||||
((view)->flags &= ~ViewIsUnmapped)
|
((view)->flags &= ~ViewIsUnmapped)
|
||||||
|
|
||||||
|
#define IsSkipped(view) \
|
||||||
|
((view)->flags & ViewIsSkipped)
|
||||||
|
#define SetSkipped(view) \
|
||||||
|
((view)->flags |= ViewIsSkipped)
|
||||||
|
#define ClearSkipped(view) \
|
||||||
|
((view)->flags &= ~ViewIsSkipped)
|
||||||
|
|
||||||
#define IsViewported(view) \
|
#define IsViewported(view) \
|
||||||
((view)->flags & ViewIsViewported)
|
((view)->flags & ViewIsViewported)
|
||||||
#define SetViewported(view) \
|
#define SetViewported(view) \
|
||||||
|
@ -528,6 +539,10 @@ SubcompositorUpdateBounds (Subcompositor *subcompositor, int doflags)
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSkipped (list->view))
|
||||||
|
/* Skip past the view itself should it be skipped. */
|
||||||
|
goto next;
|
||||||
|
|
||||||
if ((doflags & DoMinX) && min_x > list->view->abs_x)
|
if ((doflags & DoMinX) && min_x > list->view->abs_x)
|
||||||
min_x = list->view->abs_x;
|
min_x = list->view->abs_x;
|
||||||
|
|
||||||
|
@ -566,7 +581,7 @@ SubcompositorUpdateBoundsForInsert (Subcompositor *subcompositor,
|
||||||
{
|
{
|
||||||
XLAssert (view->subcompositor == subcompositor);
|
XLAssert (view->subcompositor == subcompositor);
|
||||||
|
|
||||||
if (!ViewIsMapped (view))
|
if (!ViewIsMapped (view) || IsSkipped (view))
|
||||||
/* If the view is unmapped, do nothing. */
|
/* If the view is unmapped, do nothing. */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -718,7 +733,9 @@ ViewRecomputeChildren (View *view, int *doflags)
|
||||||
&& attached
|
&& attached
|
||||||
/* Or if it isn't mapped, or none of its parents are
|
/* Or if it isn't mapped, or none of its parents are
|
||||||
mapped. */
|
mapped. */
|
||||||
&& mapped)
|
&& mapped
|
||||||
|
/* Or if it is skipped. */
|
||||||
|
&& !IsSkipped (view))
|
||||||
{
|
{
|
||||||
if (child->abs_x < view->subcompositor->min_x)
|
if (child->abs_x < view->subcompositor->min_x)
|
||||||
{
|
{
|
||||||
|
@ -1242,7 +1259,7 @@ ViewAfterSizeUpdate (View *view)
|
||||||
Bool mapped;
|
Bool mapped;
|
||||||
|
|
||||||
if (!view->subcompositor || !ViewVisibilityState (view, &mapped)
|
if (!view->subcompositor || !ViewVisibilityState (view, &mapped)
|
||||||
|| !mapped)
|
|| !mapped || IsSkipped (view))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* First, assume we will have to compute both max_x and max_y. */
|
/* First, assume we will have to compute both max_x and max_y. */
|
||||||
|
@ -1354,9 +1371,9 @@ ViewMove (View *view, int x, int y)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view->subcompositor && ViewVisibilityState (view, &mapped)
|
if (view->subcompositor && ViewVisibilityState (view, &mapped)
|
||||||
/* If this view isn't mapped, then do nothing. The bounds
|
/* If this view isn't mapped or is skipped, then do nothing.
|
||||||
will be recomputed later. */
|
The bounds will be recomputed later. */
|
||||||
&& mapped)
|
&& mapped && !IsSkipped (view))
|
||||||
{
|
{
|
||||||
/* First assume everything will have to be updated. */
|
/* First assume everything will have to be updated. */
|
||||||
doflags |= DoMaxX | DoMaxY | DoMinY | DoMinX;
|
doflags |= DoMaxX | DoMaxY | DoMinY | DoMinX;
|
||||||
|
@ -1491,6 +1508,52 @@ ViewUnmap (View *view)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ViewUnskip (View *view)
|
||||||
|
{
|
||||||
|
if (!IsSkipped (view))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ClearSkipped (view);
|
||||||
|
|
||||||
|
if (view->subcompositor && view->buffer)
|
||||||
|
{
|
||||||
|
/* Garbage the subcompositor and recompute bounds, if something
|
||||||
|
is attached to the view. */
|
||||||
|
SetGarbaged (view->subcompositor);
|
||||||
|
SubcompositorUpdateBounds (view->subcompositor, DoAll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ViewSkip (View *view)
|
||||||
|
{
|
||||||
|
if (IsSkipped (view))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Mark the view as skipped. */
|
||||||
|
SetSkipped (view);
|
||||||
|
|
||||||
|
if (view->subcompositor)
|
||||||
|
{
|
||||||
|
/* Mark the subcompositor as having unmapped or skipped
|
||||||
|
views. */
|
||||||
|
SetPartiallyMapped (view->subcompositor);
|
||||||
|
|
||||||
|
/* If nothing is attached, the subcompositor need not be
|
||||||
|
garbaged. */
|
||||||
|
if (view->buffer)
|
||||||
|
{
|
||||||
|
/* Recompute the bounds of the subcompositor. */
|
||||||
|
SubcompositorUpdateBounds (view->subcompositor,
|
||||||
|
DoAll);
|
||||||
|
|
||||||
|
/* Garbage the view's subcompositor. */
|
||||||
|
SetGarbaged (view->subcompositor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ViewFree (View *view)
|
ViewFree (View *view)
|
||||||
{
|
{
|
||||||
|
@ -1969,6 +2032,15 @@ SubcompositorUpdate (Subcompositor *subcompositor)
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSkipped (view))
|
||||||
|
{
|
||||||
|
/* We must skip this view, as it represents (for
|
||||||
|
instance) a subsurface that has been added, but not
|
||||||
|
committed. */
|
||||||
|
SetPartiallyMapped (subcompositor);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
if (!view->buffer)
|
if (!view->buffer)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
|
@ -2225,6 +2297,15 @@ SubcompositorUpdate (Subcompositor *subcompositor)
|
||||||
goto next_1;
|
goto next_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSkipped (view))
|
||||||
|
{
|
||||||
|
/* We must skip this view, as it represents (for
|
||||||
|
instance) a subsurface that has been added, but not
|
||||||
|
committed. */
|
||||||
|
SetPartiallyMapped (subcompositor);
|
||||||
|
goto next_1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!view->buffer)
|
if (!view->buffer)
|
||||||
goto next_1;
|
goto next_1;
|
||||||
|
|
||||||
|
@ -2539,6 +2620,15 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSkipped (list->view))
|
||||||
|
{
|
||||||
|
/* We must skip this view, as it represents (for
|
||||||
|
instance) a subsurface that has been added, but not
|
||||||
|
committed. */
|
||||||
|
SetPartiallyMapped (subcompositor);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
if (!list->view->buffer)
|
if (!list->view->buffer)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
|
@ -2669,6 +2759,14 @@ SubcompositorLookupView (Subcompositor *subcompositor, int x, int y,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsSkipped (list->view))
|
||||||
|
{
|
||||||
|
/* We must skip this view, as it represents (for instance) a
|
||||||
|
subsurface that has been added, but not committed. */
|
||||||
|
SetPartiallyMapped (subcompositor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!list->view->buffer)
|
if (!list->view->buffer)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
22
subsurface.c
22
subsurface.c
|
@ -593,8 +593,12 @@ AfterParentCommit (Surface *surface, void *data)
|
||||||
the scanout area of. */
|
the scanout area of. */
|
||||||
MaybeUpdateOutputs (subsurface);
|
MaybeUpdateOutputs (subsurface);
|
||||||
}
|
}
|
||||||
subsurface->pending_commit = False;
|
|
||||||
|
|
||||||
|
/* Mark the subsurface as unskipped. (IOW, make it visible). */
|
||||||
|
ViewUnskip (subsurface->role.surface->view);
|
||||||
|
ViewUnskip (subsurface->role.surface->under);
|
||||||
|
|
||||||
|
subsurface->pending_commit = False;
|
||||||
subsurface->pending_substate.flags = 0;
|
subsurface->pending_substate.flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,6 +724,22 @@ Setup (Surface *surface, Role *role)
|
||||||
= XLListPrepend (subsurface->parent->subsurfaces,
|
= XLListPrepend (subsurface->parent->subsurfaces,
|
||||||
surface);
|
surface);
|
||||||
|
|
||||||
|
/* And mark the view as "skipped"; this differs from unmapping,
|
||||||
|
which we cannot simply use, in that children remain visible, as
|
||||||
|
the specification says the following:
|
||||||
|
|
||||||
|
Adding sub-surfaces to a parent is a double-buffered operation
|
||||||
|
on the parent (see wl_surface.commit). The effect of adding a
|
||||||
|
sub-surface becomes visible on the next time the state of the
|
||||||
|
parent surface is applied.
|
||||||
|
|
||||||
|
So if a child is added to a desynchronized subsurface whose parent
|
||||||
|
toplevel has not yet committed, and commit is called on the
|
||||||
|
desynchronized subsurface, the child should become indirectly
|
||||||
|
visible on the parent toplevel through the child. */
|
||||||
|
ViewSkip (surface->view);
|
||||||
|
ViewSkip (surface->under);
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue