diff --git a/compositor.h b/compositor.h index 3066ab8..c3974aa 100644 --- a/compositor.h +++ b/compositor.h @@ -650,6 +650,8 @@ extern void ViewMove (View *, int, int); extern void ViewDetach (View *); extern void ViewMap (View *); extern void ViewUnmap (View *); +extern void ViewSkip (View *); +extern void ViewUnskip (View *); extern void ViewMoveFractional (View *, double, double); extern void ViewSetViewport (View *, double, double, double, double, diff --git a/dmabuf.c b/dmabuf.c index f1ad8d5..db2ef56 100644 --- a/dmabuf.c +++ b/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 - crtc of the provider the surface is in. */ + provider of the crtc the surface is in. */ static void MakeFeedback (struct wl_client *client, struct wl_resource *resource, diff --git a/shm.c b/shm.c index 7e07442..e2f6406 100644 --- a/shm.c +++ b/shm.c @@ -21,11 +21,17 @@ along with 12to11. If not, see . */ #include #include #include +#include #include #include "compositor.h" +enum + { + PoolCannotSigbus = 1, + }; + typedef struct _Pool { /* The file descriptor corresponding to this pool. */ @@ -37,6 +43,9 @@ typedef struct _Pool /* The number of references to this pool. */ int refcount; + /* Various flags. */ + int flags; + /* Pointer to the raw data in this pool. */ void *data; @@ -83,7 +92,10 @@ DereferencePool (Pool *pool) if (pool->data != (void *) -1 /* 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); close (pool->fd); @@ -339,6 +351,10 @@ ResizePool (struct wl_client *client, struct wl_resource *resource, { Pool *pool; void *data; +#ifdef F_GET_SEALS + int seals; + struct stat statb; +#endif 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 installed. */ - if (pool->size) + if (pool->size && !(pool->flags & PoolCannotSigbus)) 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->data = data; /* And add a new handler. */ - if (pool->size) + if (pool->size && !(pool->flags & PoolCannotSigbus)) 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) { 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); @@ -411,6 +452,7 @@ CreatePool (struct wl_client *client, struct wl_resource *resource, { XLFree (pool); wl_resource_post_no_memory (resource); + close (fd); return; } @@ -423,6 +465,7 @@ CreatePool (struct wl_client *client, struct wl_resource *resource, XLFree (pool); wl_resource_post_error (resource, WL_SHM_ERROR_INVALID_FD, "mmap: %s", strerror (errno)); + close (fd); return; } @@ -432,10 +475,23 @@ CreatePool (struct wl_client *client, struct wl_resource *resource, 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 the file without telling us, in which case accessing its contents will cause crashes. */ - if (pool->size) + if (!(pool->flags & PoolCannotSigbus) && pool->size) XLRecordBusfault (pool->data, pool->size); pool->fd = fd; diff --git a/subcompositor.c b/subcompositor.c index 4ab9bf6..94e81f5 100644 --- a/subcompositor.c +++ b/subcompositor.c @@ -193,10 +193,14 @@ enum /* This means that the view and all its inferiors should be skipped in bounds computation, input tracking, et cetera. */ 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, - effectively decoupling its relation to the buffer width and + effectively decoupling its relation to the buffer width and height. */ - ViewIsViewported = 1 << 2, + ViewIsViewported = 1 << 3, }; #define IsViewUnmapped(view) \ @@ -206,6 +210,13 @@ enum #define ClearUnmapped(view) \ ((view)->flags &= ~ViewIsUnmapped) +#define IsSkipped(view) \ + ((view)->flags & ViewIsSkipped) +#define SetSkipped(view) \ + ((view)->flags |= ViewIsSkipped) +#define ClearSkipped(view) \ + ((view)->flags &= ~ViewIsSkipped) + #define IsViewported(view) \ ((view)->flags & ViewIsViewported) #define SetViewported(view) \ @@ -528,6 +539,10 @@ SubcompositorUpdateBounds (Subcompositor *subcompositor, int doflags) 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) min_x = list->view->abs_x; @@ -566,7 +581,7 @@ SubcompositorUpdateBoundsForInsert (Subcompositor *subcompositor, { XLAssert (view->subcompositor == subcompositor); - if (!ViewIsMapped (view)) + if (!ViewIsMapped (view) || IsSkipped (view)) /* If the view is unmapped, do nothing. */ return; @@ -718,7 +733,9 @@ ViewRecomputeChildren (View *view, int *doflags) && attached /* Or if it isn't mapped, or none of its parents are mapped. */ - && mapped) + && mapped + /* Or if it is skipped. */ + && !IsSkipped (view)) { if (child->abs_x < view->subcompositor->min_x) { @@ -1242,7 +1259,7 @@ ViewAfterSizeUpdate (View *view) Bool mapped; if (!view->subcompositor || !ViewVisibilityState (view, &mapped) - || !mapped) + || !mapped || IsSkipped (view)) return; /* 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 this view isn't mapped, then do nothing. The bounds - will be recomputed later. */ - && mapped) + /* If this view isn't mapped or is skipped, then do nothing. + The bounds will be recomputed later. */ + && mapped && !IsSkipped (view)) { /* First assume everything will have to be updated. */ 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 ViewFree (View *view) { @@ -1969,6 +2032,15 @@ SubcompositorUpdate (Subcompositor *subcompositor) 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) goto next; @@ -2225,6 +2297,15 @@ SubcompositorUpdate (Subcompositor *subcompositor) 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) goto next_1; @@ -2539,6 +2620,15 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event) 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) goto next; @@ -2669,6 +2759,14 @@ SubcompositorLookupView (Subcompositor *subcompositor, int x, int y, 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) continue; diff --git a/subsurface.c b/subsurface.c index 3996f53..d5b3323 100644 --- a/subsurface.c +++ b/subsurface.c @@ -593,8 +593,12 @@ AfterParentCommit (Surface *surface, void *data) the scanout area of. */ 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; } @@ -720,6 +724,22 @@ Setup (Surface *surface, Role *role) = XLListPrepend (subsurface->parent->subsurfaces, 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; }