Implement wp_viewporter support and fix scaling for fractional values

* 12to11.c (XLMain): Initialize wp_viewporter.
* Imakefile (ETAGS): Remove unused variable.
(SRCS, OBJS): Add wp_viewporter.c and wp_viewporter.o.
(GENHEADERS): Remove unnecessary headers.
(viewporter): New scanner target.
* README: Document support for wp_viewporter.
* compositor.h (struct _ViewportExt): New forward declaration.
(struct _DrawParams): New fields for cropping and stretching.
(struct _RenderFuncs): Describe how composite works.
(struct _BufferFuncs): Make update_buffer_for_damage take
DrawParams as an argument.
(struct _State): New fields for viewporting.
(struct _Surface): New field `viewport' and associated input
delta.
(struct _XdgRoleImplementationFuncs): New field
`is_window_mapped'.  Do not commit while unmapped.
* dmabuf.c (XLInitDmabuf): Remove outdated comment.
* dnd.c (HandleMotion): Use TruncateWindowToSurface.
* egl.c (struct _EglBuffer): Add 3x3 reverse transformation
matrix.
(struct _CompositeProgram): Rename `scale' to `source'.
(Index): New macro.
(PickBetterVisual, FindVisual): Compensate for EGL picking a
non-RGBA visual.
(EglCompileCompositeProgram): Look for source, not scale.
(ComputeTransformMatrix): New function.
(Composite): Compute transformation matrix and draw using that.
(BufferFromDmaBuf, BufferFromShm): Copy identity transform and
stop setting scale.
(ReverseTransformToBox): New function.
(UpdateShmBufferIncrementally): Accept DrawParams and invert
damage according to that.
(UpdateBuffer, UpdateBufferForDamage): Pass draw params to the
incremental buffer update function.
* fns.c (XLExtendRegion): New function.
* frame_clock.c (CurrentHighPrecisionTimestamp): Delete
function.
(HighPrecisionTimestamp, HighPrecisionTimestamp32): New
functions.
(PostEndFrame): Handle X server time truncation to 32 bits.
(XLFrameClockFreeze): Remove trailing whitespace.
* picture_renderer.c (GetSourceX, GetSourceY, CompareStretch):
New functions.
(MaybeApplyTransform): Check more values before applying
transformations.  Then, handle stretch and offset.
* positioner.c (GetAdjustmentOffset, ApplyConstraintAdjustment)
(XLPositionerCalculateGeometry): Scale coordinates using new
functions.
* renderer.c (RenderUpdateBufferForDamage): Accept DrawParams
instead of scale.
* shaders.txt (Composite Rectangle Fragment Shader RGBA)
(Composite Rectangle Fragment Shader RGBX)
(Composite Rectangle Fragment Shader External): Stop
transforming texcoords.
(Composite Rectangle Vertex Shader): Transform texcoords in the
vertex shader instead.
* subcompositor.c (IsViewported, SetViewported,
ClearViewported): New functions.
(struct _View): New fields for tracking viewports and fractional
offsets.
(ViewAttachBuffer): Do not garbage upon buffer size change if a
viewport is set.
(ViewMoveFractional): New function.
(ViewDamage): Describe what the damage is and is not transformed
by.
(GetContentScale): New function.
(ViewWidth, ViewHeight): Apply viewport.
(ViewSetScale): Use ViewAfterSizeUpdate instead of duplicating
code.
(ViewSetViewport, ViewClearViewport): New functions.
(ViewComputeTransform): Compute transform for viewports.  New
arg draw; use it to determine whether or not to include a
fractional offset.
(IntersectBoxes): Fix intersection calculation.
(SubcompositorUpdate): Don't keep calling ViewWidth and
ViewHeight in a loop.
(SubcompositorExpose): Adjust for changes to buffer damage
uploading.
* subsurface.c (MoveFractional): New function.  Handle
fractional offsets after scaling.
(MaybeUpdateOutputs, AfterParentCommit, Setup, Rescale): Use
that function to move the subsurface instead.
* surface.c (ApplyScale): Update comment.
(ApplyViewport, CheckViewportValues): New functions.
(HandleScaleChanged): Apply the viewport as well upon scale
change.
(ApplyDamage): Improve damage calculation for viewported
surfaces.
(SavePendingState, InternalCommit): Save and commit viewport
state; check old values upon buffer commit.
(InitState): Initialize viewport to initial values.
(XLSurfaceRunFrameCallbacks): Handle overflows of 32-bit time at
the 49-day mark.
(SurfaceToWindow, ScaleToWindow, WindowToSurface, ScaleToSurface)
(TruncateScaleToWindow, TruncateScaleToWindow)
(TruncateWindowToSurface, TruncateScaleToSurface): New functions
for handling scale.
* xdg_popup.c (MoveWindow, IsWindowMapped, XLGetXdgPopup):
* xdg_surface.c (IsRoleMapped, Commit, Subframe)
(GetResizeDimensions, XLXdgRoleCalcNewWindowSize):
* xdg_toplevel.c (IsWindowMapped, NoteConfigureTime, SendStates)
(RecordStateSize, HandleWindowGeometryChange, NoteWindowPreResize)
(XLGetXdgToplevel): Use them instead of manually multiplying
with the factor.
This commit is contained in:
oldosfan 2022-09-30 01:17:47 +00:00
parent dfcd969d3e
commit 4d2e85d002
19 changed files with 1266 additions and 275 deletions

View file

@ -122,6 +122,7 @@ XLMain (int argc, char **argv)
XLInitIconSurfaces ();
XLInitPrimarySelection ();
XLInitExplicitSynchronization ();
XLInitWpViewporter ();
/* This has to come after the rest of the initialization. */
DetermineServerTime ();
XLRunCompositor ();

View file

@ -8,8 +8,6 @@ SYS_LIBRARIES = MathLibrary ThreadsLibraries
DEPLIBS = $(DEPXLIB) $(DEPEXTENSIONLIB) $(DEPXRANDRLIB) $(DEPXRENDERLIB) \
$(DEPXFIXESLIB) $(DEPXILIB) $(DEPXKBFILELIB)
ETAGS = etags
LOCAL_LIBRARIES = $(XLIB) $(EXTENSIONLIB) $(XCBLIB) $(XCB) $(XCB_SHM) \
$(XRANDRLIB) $(PIXMAN) $(XRENDERLIB) $(XILIB) $(XKBFILELIB) $(XFIXESLIB) \
$(XCB_DRI3) $(XCB_SHAPE) $(WAYLAND_SERVER)
@ -22,7 +20,8 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \
ewmh.c timer.c subsurface.c seat.c data_device.c xdg_popup.c \
dmabuf.c buffer.c select.c xdata.c xsettings.c dnd.c \
icon_surface.c primary_selection.c renderer.c \
picture_renderer.c explicit_synchronization.c
picture_renderer.c explicit_synchronization.c transform.c \
wp_viewporter.c
OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
surface.o region.o shm.o atoms.o subcompositor.o positioner.o \
@ -30,11 +29,10 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \
ewmh.o timer.o subsurface.o seat.o data_device.o xdg_popup.o \
dmabuf.o buffer.o select.o xdata.o xsettings.o dnd.o \
icon_surface.o primary_selection.o renderer.o \
picture_renderer.o explicit_synchronization.o
picture_renderer.o explicit_synchronization.o transform.o \
wp_viewporter.o
GENHEADERS = transfer_atoms.h primary-selection-unstable-v1.h \
linux-dmabuf-unstable-v1.h xdg-shell.h \
linux-explicit-synchronization-unstable-v1.h
GENHEADERS = transfer_atoms.h
#ifdef HaveEglSupport
@ -111,6 +109,7 @@ ScannerTarget(linux-dmabuf-unstable-v1)
ScannerTarget(xdg-shell)
ScannerTarget(primary-selection-unstable-v1)
ScannerTarget(linux-explicit-synchronization-unstable-v1)
ScannerTarget(viewporter)
/* Make OBJS depend on scanner headers, and depend on both them and SRCS. */
$(OBJS): $(GENHEADERS)

3
README
View file

@ -3,7 +3,7 @@ preferably with a compositing manager running.
It is not yet complete. What is not yet implemented includes support
for touchscreens, input methods, device switching in dmabuf feedback,
and the viewporter protocol extension.
and the single-pixel buffer protocol extension.
There are also problems with output reporting in subsurfaces.
@ -64,6 +64,7 @@ complete degree:
'wl_data_device_manager', version: 3
'zwp_linux_dmabuf_v1', version: 4
'zwp_primary_selection_device_manager_v1', version: 1
'wp_viewporter', version: 1
When built with EGL, the following Wayland protocol is also supported:

View file

@ -1,4 +1,4 @@
/* Wayland compositor running on top of an X serer.
/* Wayland compositor running on top of an X server.
Copyright (C) 2022 to various contributors.
@ -90,6 +90,10 @@ typedef struct _Seat Seat;
typedef struct _PDataSource PDataSource;
/* Forward declarations from wp_viewporter.c. */
typedef struct _ViewportExt ViewportExt;
/* Defined in 12to11.c. */
extern Compositor compositor;
@ -125,7 +129,13 @@ enum _Operation
enum
{
/* Scale has been set. */
ScaleSet = 1,
ScaleSet = 1,
/* Offset has been set. */
OffsetSet = (1 << 2),
/* Stretch has been set. */
StretchSet = (1 << 3),
};
struct _DrawParams
@ -135,6 +145,15 @@ struct _DrawParams
/* A scale factor to apply to the buffer. */
double scale;
/* Offset from which to start sampling from the buffer, after
scaling. */
double off_x, off_y;
/* The crop, width and height of the buffer. The buffer will be
stretched or shrunk to this size, after being cropped to
crop and being offset by -off_x, -off_y. */
double crop_width, crop_height, stretch_width, stretch_height;
};
struct _SharedMemoryAttributes
@ -261,7 +280,10 @@ struct _RenderFuncs
/* Composite width, height, from the given buffer onto the given
target, at x, y. The arguments are: buffer, target, operation,
source_x, source_y, x, y, width, height, params. params
describes how to transform the given buffer. */
describes how to transform the given buffer.
Reads outside the texture contents should result in transparency
being composited. */
void (*composite) (RenderBuffer, RenderTarget, Operation, int, int,
int, int, int, int, DrawParams *);
@ -360,10 +382,11 @@ struct _BufferFuncs
/* Notice that the given buffer has been damaged. May be NULL. If
the given NULL damage, assume that the entire buffer has been
damaged. Must be called at least once before any rendering can
be performed on the buffer. 3rd arg is the scale by which to
divide the buffer. */
be performed on the buffer. 3rd arg is a DrawParams struct
describing a transform to inversely apply to the damage
region. */
void (*update_buffer_for_damage) (RenderBuffer, pixman_region32_t *,
float);
DrawParams *);
/* Return whether or not the buffer contents can be released early,
by being copied to an offscreen buffer. */
@ -410,7 +433,7 @@ extern Bool RenderValidateShmParams (uint32_t, uint32_t, uint32_t, int32_t,
extern void RenderFreeShmBuffer (RenderBuffer);
extern void RenderFreeDmabufBuffer (RenderBuffer);
extern void RenderUpdateBufferForDamage (RenderBuffer, pixman_region32_t *,
float);
DrawParams *);
extern Bool RenderCanReleaseNow (RenderBuffer);
/* Defined in run.c. */
@ -503,6 +526,8 @@ extern int XLOpenShm (void);
extern void XLScaleRegion (pixman_region32_t *, pixman_region32_t *,
float, float);
extern void XLExtendRegion (pixman_region32_t *, pixman_region32_t *,
int, int);
extern Time XLGetServerTimeRoundtrip (void);
extern RootWindowSelection *XLSelectInputFromRootWindow (unsigned long);
@ -625,6 +650,11 @@ extern void ViewMove (View *, int, int);
extern void ViewDetach (View *);
extern void ViewMap (View *);
extern void ViewUnmap (View *);
extern void ViewMoveFractional (View *, double, double);
extern void ViewSetViewport (View *, double, double, double, double,
double, double);
extern void ViewClearViewport (View *);
extern void ViewSetData (View *, void *);
extern void *ViewGetData (View *);
@ -673,6 +703,8 @@ enum
PendingFrameCallbacks = (1 << 6),
PendingBufferScale = (1 << 7),
PendingAttachments = (1 << 8),
PendingViewportSrc = (1 << 9),
PendingViewportDest = (1 << 10),
/* Flags here are stored in `pending' of the current state for
space reasons. */
@ -717,6 +749,14 @@ struct _State
/* Attachment position. */
int x, y;
/* Viewport destination width and height. All viewport coordinates
and dimensions are specified in terms of the surface coordinate
system. */
int dest_width, dest_height;
/* Viewport source rectangle. */
double src_x, src_y, src_width, src_height;
};
typedef enum _ClientDataType ClientDataType;
@ -848,6 +888,13 @@ struct _Surface
/* The scale factor used to convert from surface coordinates to
window coordinates. */
double factor;
/* Any associated viewport resource. */
ViewportExt *viewport;
/* Any associated input delta. This is used to compensate
for fractional subsurface placement while handling input. */
double input_delta_x, input_delta_y;
};
struct _RoleFuncs
@ -917,6 +964,16 @@ extern void XLSurfaceMoveBy (Surface *, int, int);
extern Window XLWindowFromSurface (Surface *);
extern void XLUpdateSurfaceOutputs (Surface *, int, int, int, int);
extern void SurfaceToWindow (Surface *, double, double, double *, double *);
extern void ScaleToWindow (Surface *, double, double, double *, double *);
extern void WindowToSurface (Surface *, double, double, double *, double *);
extern void ScaleToSurface (Surface *, double, double, double *, double *);
extern void TruncateSurfaceToWindow (Surface *, int, int, int *, int *);
extern void TruncateScaleToWindow (Surface *, int, int, int *, int *);
extern void TruncateWindowToSurface (Surface *, int, int, int *, int *);
extern void TruncateScaleToSurface (Surface *, int, int, int *, int *);
/* Defined in output.c. */
extern int global_scale_factor;
@ -1016,6 +1073,7 @@ struct _XdgRoleImplementationFuncs
void (*handle_geometry_change) (Role *, XdgRoleImplementation *);
void (*post_resize) (Role *, XdgRoleImplementation *, int, int, int, int);
void (*commit_inside_frame) (Role *, XdgRoleImplementation *);
Bool (*is_window_mapped) (Role *, XdgRoleImplementation *);
};
struct _XdgRoleImplementation
@ -1349,6 +1407,23 @@ extern void XLSyncRelease (SyncRelease *);
extern void XLWaitFence (Surface *);
extern void XLInitExplicitSynchronization (void);
/* Defined in transform.c. */
/* N.B. the data is stored in column-major order. */
typedef float Matrix[9];
extern void MatrixMultiply (Matrix, Matrix, Matrix *);
extern void MatrixIdentity (Matrix *);
extern void MatrixTranslate (Matrix *, float, float);
extern void MatrixScale (Matrix *, float, float);
extern void MatrixExport (Matrix *, XTransform *);
/* Defined in wp_viewporter.c. */
extern void XLInitWpViewporter (void);
extern void XLWpViewportReportBadSize (ViewportExt *);
extern void XLWpViewportReportOutOfBuffer (ViewportExt *);
/* Utility functions that don't belong in a specific file. */
#define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0])

View file

@ -1068,9 +1068,6 @@ XLInitDmabuf (void)
/* And try to create the format table. */
size = WriteFormatTable ();
/* Create an unmapped, InputOnly window, that is used to receive
roundtrip events. */
global_dmabuf = wl_global_create (compositor.wl_display,
&zwp_linux_dmabuf_v1_interface,
/* If writing the format table

10
dnd.c
View file

@ -897,12 +897,10 @@ HandleMotion (Surface *toplevel, int x, int y, uint32_t action,
/* Compute the surface-relative coordinates and scale them. */
if (child)
{
/* x_out and y_out are only used if dnd_state.child ends up
non-NULL. */
*x_out = (x - x_off) / child->factor;
*y_out = (y - y_off) / child->factor;
}
/* x_out and y_out are only used if dnd_state.child ends up
non-NULL. */
TruncateWindowToSurface (child, x - x_off, y - y_off,
x_out, y_out);
if (dnd_state.child == child)
/* If nothing changed, don't do anything. */

312
egl.c
View file

@ -123,6 +123,11 @@ struct _EglBuffer
/* The texture name of any generated texture. */
GLuint texture;
/* 3x3 matrix that is used to transform texcoord into actual texture
coordinates. Note that GLfloat[9] should be the same type as
Matrix. */
GLfloat matrix[9];
/* The width and height of the buffer. */
int width, height;
@ -180,13 +185,17 @@ struct _CompositeProgram
/* The index of the texture uniform. */
GLuint texture;
/* The index of the scale uniform. */
GLuint scale;
/* The index of the source uniform. */
GLuint source;
/* The index of the invert_y uniform. */
GLuint invert_y;
};
/* This macro makes column major order easier to reason about for C
folks. */
#define Index(matrix, row, column) ((matrix)[(column) * 3 + (row)])
/* All known SHM formats. */
static FormatInfo known_shm_formats[] =
{
@ -531,6 +540,134 @@ EglInitGlFuncs (void)
IWaitSync = NULL;
}
static Visual *
PickBetterVisual (Visual *visual, int *depth)
{
XRenderPictFormat target_format, *format, *found;
int i, num_x_formats, bpp, n_visuals, j;
EGLint alpha_size;
XPixmapFormatValues *formats;
XVisualInfo empty_template, *visuals;
/* First, see if there is already an alpha channel. */
format = XRenderFindVisualFormat (compositor.display, visual);
if (!format)
return visual;
if (format->type != PictTypeDirect)
/* Can this actually happen? */
return visual;
if (format->direct.alphaMask)
return visual;
/* Next, build the target format from the visual format. */
target_format.type = PictTypeDirect;
target_format.direct = format->direct;
/* Obtain the size of the alpha mask in the EGL config. */
if (!eglGetConfigAttrib (egl_display, egl_config,
EGL_ALPHA_SIZE, &alpha_size))
return visual;
if (alpha_size > 16)
/* If the alpha mask is too big, then use the chosen visual. */
return visual;
/* Add the alpha mask. */
for (i = 0; i < alpha_size; ++i)
target_format.direct.alphaMask |= 1 << i;
/* Look for matching picture formats with the same bpp and a larger
depth. */
formats = XListPixmapFormats (compositor.display, &num_x_formats);
if (!formats)
return visual;
/* Obtain the number of bits per pixel for the given depth. */
bpp = 0;
for (i = 0; i < num_x_formats; ++i)
{
if (formats[i].depth == format->depth)
bpp = formats[i].bits_per_pixel;
}
if (!bpp)
{
XFree (formats);
return visual;
}
/* Get a list of all visuals. */
empty_template.screen = DefaultScreen (compositor.display);
visuals = XGetVisualInfo (compositor.display, VisualScreenMask,
&empty_template, &n_visuals);
if (!visuals)
{
XFree (formats);
return visual;
}
/* Now, loop through each depth. */
for (i = 0; i < num_x_formats; ++i)
{
if (formats[i].depth > format->depth
&& formats[i].bits_per_pixel == bpp)
{
/* Try to find a matching picture format. */
target_format.depth = formats[i].depth;
found = XRenderFindFormat (compositor.display,
PictFormatType
| PictFormatDepth
| PictFormatRed
| PictFormatGreen
| PictFormatBlue
| PictFormatRedMask
| PictFormatBlueMask
| PictFormatGreenMask
| PictFormatAlphaMask,
&target_format, 0);
if (found)
{
/* Now try to find the corresponding visual. */
for (j = 0; j < n_visuals; ++j)
{
if (visuals[j].depth != formats[i].depth)
continue;
if (XRenderFindVisualFormat (compositor.display,
visuals[j].visual) == found)
{
/* We got a usable visual with an alpha channel
otherwise matching the characteristics of the
visual specified by EGL. Return. */
*depth = formats[i].depth;
visual = visuals[j].visual;
XFree (visuals);
XFree (formats);
return visual;
}
}
}
}
}
/* Otherwise, nothing was found. Return the original visual
untouched, but free visuals. */
XFree (visuals);
XFree (formats);
return visual;
}
static Visual *
FindVisual (VisualID visual, int *depth)
{
@ -568,6 +705,12 @@ FindVisual (VisualID visual, int *depth)
them. */
value = visuals->visual;
/* EGL does not know how to find visuals with an alpha channel, even
if we specify one in the framebuffer configuration. Detect when
that is the case, and pick a better visual. */
value = PickBetterVisual (value, &visuals->depth);
*depth = visuals->depth;
XLFree (visuals);
@ -735,8 +878,8 @@ EglCompileCompositeProgram (CompositeProgram *program,
"pos");
program->texture = glGetUniformLocation (program->program,
"texture");
program->scale = glGetUniformLocation (program->program,
"scale");
program->source = glGetUniformLocation (program->program,
"source");
program->invert_y = glGetUniformLocation (program->program,
"invert_y");
@ -1162,6 +1305,39 @@ GetTextureTarget (EglBuffer *buffer)
abort ();
}
static void
ComputeTransformMatrix (EglBuffer *buffer, DrawParams *params)
{
/* Update the transformation matrix of BUFFER. This is a 3x3
transformation matrix that maps from texcoords to actual
coordinates in the buffer. */
/* Copy over the identity transform. */
MatrixIdentity (&buffer->matrix);
/* Set the X and Y scales. */
if (params->flags & ScaleSet)
{
Index (buffer->matrix, 0, 0)
= (float) (1.0 / params->scale);
Index (buffer->matrix, 1, 1)
= (float) (1.0 / params->scale);
}
/* Set the offsets. */
if (params->flags & OffsetSet)
MatrixTranslate (&buffer->matrix,
(float) (params->off_x / buffer->width),
(float) (params->off_y / buffer->height));
/* Set the stretch. */
if (params->flags & StretchSet)
/* Scale the buffer down by this much. */
MatrixScale (&buffer->matrix,
(float) (params->crop_width / params->stretch_width),
(float) (params->crop_height / params->stretch_height));
}
static void
Composite (RenderBuffer buffer, RenderTarget target,
Operation op, int src_x, int src_y, int x, int y,
@ -1173,7 +1349,6 @@ Composite (RenderBuffer buffer, RenderTarget target,
EglBuffer *egl_buffer;
CompositeProgram *program;
GLenum tex_target;
GLfloat scale;
egl_target = target.pointer;
egl_buffer = buffer.pointer;
@ -1188,6 +1363,10 @@ Composite (RenderBuffer buffer, RenderTarget target,
/* Get the texturing target. */
tex_target = GetTextureTarget (egl_buffer);
/* Compute the transformation matrix to use to draw the given
buffer. */
ComputeTransformMatrix (egl_buffer, params);
/* dest rectangle on target. */
x1 = x;
y1 = y;
@ -1233,18 +1412,17 @@ Composite (RenderBuffer buffer, RenderTarget target,
else
glDisable (GL_BLEND);
if (params->flags & ScaleSet)
scale = params->scale;
else
scale = 1.0f;
glActiveTexture (GL_TEXTURE0);
glBindTexture (tex_target, egl_buffer->texture);
glTexParameteri (tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (tex_target, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
glTexParameteri (tex_target, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glUseProgram (program->program);
glUniform1i (program->texture, 0);
glUniform1f (program->scale, scale);
glUniformMatrix3fv (program->source, 1, GL_FALSE,
egl_buffer->matrix);
glUniform1i (program->invert_y, egl_buffer->flags & InvertY);
glVertexAttribPointer (program->position, 2, GL_FLOAT,
GL_FALSE, 0, verts);
@ -1501,6 +1679,10 @@ BufferFromDmaBuf (DmaBufAttributes *attributes, Bool *error)
buffer->width = attributes->width;
buffer->height = attributes->height;
buffer->u.type = DmaBufBuffer;
/* Copy over the identity transform. */
MatrixIdentity (&buffer->matrix);
i = 0;
/* Find the DRM format in question so we can determine the right
@ -1651,6 +1833,9 @@ BufferFromShm (SharedMemoryAttributes *attributes, Bool *error)
buffer->height = attributes->height;
buffer->u.type = ShmBuffer;
/* Copy over the identity transform. */
MatrixIdentity (&buffer->matrix);
/* Record the buffer data. */
buffer->u.shm.format = FindFormatInfo (attributes->format);
@ -1990,10 +2175,52 @@ UpdateTexture (EglBuffer *buffer)
}
static void
UpdateShmBufferIncrementally (EglBuffer *buffer, pixman_region32_t *damage)
ReverseTransformToBox (DrawParams *params, pixman_box32_t *box)
{
double x_factor, y_factor;
if (!params)
return;
/* Apply the inverse of PARAMS to BOX, for use in damage
tracking. */
if (params->flags & ScaleSet)
{
box->x1 = floor (box->x1 / params->scale);
box->y1 = floor (box->y1 / params->scale);
box->x2 = ceil (box->x2 / params->scale);
box->y2 = ceil (box->y2 / params->scale);
}
if (params->flags & OffsetSet)
{
/* Since the offset can be a fractional value, also try to
include as much as possible in the box. */
box->x1 = floor (box->x1 + params->off_x);
box->y1 = floor (box->y1 + params->off_y);
box->x2 = ceil (box->x2 + params->off_x);
box->y2 = ceil (box->y2 + params->off_y);
}
if (params->flags & StretchSet)
{
x_factor = params->crop_width / params->stretch_width;
y_factor = params->crop_height / params->stretch_height;
box->x1 = floor (box->x1 * x_factor);
box->y1 = floor (box->y1 * y_factor);
box->x2 = ceil (box->x2 * x_factor);
box->y2 = ceil (box->y2 * y_factor);
}
}
static void
UpdateShmBufferIncrementally (EglBuffer *buffer, pixman_region32_t *damage,
DrawParams *params)
{
GLenum target;
pixman_box32_t *boxes;
pixman_box32_t *boxes, box;
int nboxes, i, width, height;
void *data_ptr;
size_t expected_data_size;
@ -2018,34 +2245,39 @@ UpdateShmBufferIncrementally (EglBuffer *buffer, pixman_region32_t *damage)
the damage. */
for (i = 0; i < nboxes; ++i)
{
if (boxes[i].x1 >= buffer->width
|| boxes[i].y1 >= buffer->height)
/* This box isn't contained in the buffer. */
continue;
/* Get a copy of the box. */
box = boxes[i];
/* Transform the box according to any transforms. */
ReverseTransformToBox (params, &box);
/* Clip the box X and Y to 0, 0. */
box.x1 = MIN (box.y1, 0);
box.y1 = MIN (box.y1, 0);
/* These computations are correct, since box->x2/box->y2 are
actually 1 pixel outside the last pixel in the box. */
width = MIN (boxes[i].x2, buffer->width) - boxes[i].x1;
height = MIN (boxes[i].y2, buffer->height) - boxes[i].y1;
width = MIN (box.x2, buffer->width) - box.x1;
height = MIN (box.y2, buffer->height) - box.y1;
if (width <= 0 || height <= 0)
/* The box is effectively empty because it straddles one of
the corners of the buffer. */
the corners of the buffer, or is outside of the buffer. */
continue;
/* First, set the length of a single row. */
glPixelStorei (GL_UNPACK_ROW_LENGTH_EXT,
buffer->u.shm.stride / (buffer->u.shm.format->bpp / 8));
/* Next, skip boxes[i].x1 pixels of each row. */
glPixelStorei (GL_UNPACK_SKIP_PIXELS_EXT, boxes[i].x1);
/* Next, skip box.x1 pixels of each row. */
glPixelStorei (GL_UNPACK_SKIP_PIXELS_EXT, box.x1);
/* And boxes[i].y1 rows. */
glPixelStorei (GL_UNPACK_SKIP_ROWS_EXT, boxes[i].y1);
/* And box.y1 rows. */
glPixelStorei (GL_UNPACK_SKIP_ROWS_EXT, box.y1);
/* Copy the image into the sub-texture. */
glTexSubImage2D (target, 0, boxes[i].x1, boxes[i].y1,
width, height, buffer->u.shm.format->gl_format,
glTexSubImage2D (target, 0, box.x1, box.y1, width, height,
buffer->u.shm.format->gl_format,
buffer->u.shm.format->gl_type, data_ptr);
}
@ -2080,7 +2312,8 @@ EnsureTexture (EglBuffer *buffer)
}
static void
UpdateBuffer (RenderBuffer buffer, pixman_region32_t *damage)
UpdateBuffer (RenderBuffer buffer, pixman_region32_t *damage,
DrawParams *params)
{
EglBuffer *egl_buffer;
@ -2106,8 +2339,9 @@ UpdateBuffer (RenderBuffer buffer, pixman_region32_t *damage)
{
case ShmBuffer:
/* Update the shared memory buffer incrementally, taking
into account the damaged area. */
UpdateShmBufferIncrementally (egl_buffer, damage);
into account the damaged area and transform. */
UpdateShmBufferIncrementally (egl_buffer, damage,
params);
break;
default:
@ -2119,23 +2353,9 @@ UpdateBuffer (RenderBuffer buffer, pixman_region32_t *damage)
static void
UpdateBufferForDamage (RenderBuffer buffer, pixman_region32_t *damage,
float scale)
DrawParams *params)
{
pixman_region32_t region;
if (scale != 1.0f && damage)
{
/* Scale the damage, specified in scaled coordinates, down to
texture coordinates. */
pixman_region32_init (&region);
XLScaleRegion (&region, damage, 1.0f / scale,
1.0f / scale);
UpdateBuffer (buffer, &region);
pixman_region32_fini (&region);
}
else
UpdateBuffer (buffer, damage);
UpdateBuffer (buffer, damage, params);
}
static Bool

30
fns.c
View file

@ -380,6 +380,36 @@ XLScaleRegion (pixman_region32_t *dst, pixman_region32_t *src,
XLFree (dst_rects);
}
void
XLExtendRegion (pixman_region32_t *dst, pixman_region32_t *src,
int extend_x, int extend_y)
{
int nrects, i;
pixman_box32_t *src_rects;
pixman_box32_t *dst_rects;
src_rects = pixman_region32_rectangles (src, &nrects);
if (nrects < 128)
dst_rects = alloca (nrects * sizeof *dst_rects);
else
dst_rects = XLMalloc (nrects * sizeof *dst_rects);
for (i = 0; i < nrects; ++i)
{
dst_rects[i].x1 = src_rects[i].x1;
dst_rects[i].x2 = src_rects[i].x2 + extend_x;
dst_rects[i].y1 = src_rects[i].y1;
dst_rects[i].y2 = src_rects[i].y2 + extend_y;
}
pixman_region32_fini (dst);
pixman_region32_init_rects (dst, dst_rects, nrects);
if (nrects >= 128)
XLFree (dst_rects);
}
int
XLOpenShm (void)
{

View file

@ -144,15 +144,45 @@ SetSyncCounter (XSyncCounter counter, uint64_t value)
}
static uint64_t
CurrentHighPrecisionTimestamp (void)
HighPrecisionTimestamp (struct timespec *clock)
{
struct timespec clock;
uint64_t timestamp;
clock_gettime (CLOCK_MONOTONIC, &clock);
if (IntMultiplyWrapv (clock->tv_sec, 1000000, &timestamp)
|| IntAddWrapv (timestamp, clock->tv_nsec / 1000, &timestamp))
/* Overflow. */
return 0;
if (IntMultiplyWrapv (clock.tv_sec, 1000000, &timestamp)
|| IntAddWrapv (timestamp, clock.tv_nsec / 1000, &timestamp))
return timestamp;
}
static uint64_t
HighPrecisionTimestamp32 (struct timespec *clock)
{
uint64_t timestamp, milliseconds;
/* This function is like CurrentHighPrecisionTimestamp, but the X
server time portion is limited to 32 bits. First, the seconds
are converted to milliseconds. */
if (IntMultiplyWrapv (clock->tv_sec, 1000, &milliseconds))
return 0;
/* Next, the nanosecond portion is also converted to
milliseconds. */
if (IntAddWrapv (milliseconds, clock->tv_nsec / 1000000,
&milliseconds))
return 0;
/* Then, the milliseconds are truncated to 32 bits. */
milliseconds &= 0xffffffff;
/* Finally, add the milliseconds to the timestamp. */
if (IntMultiplyWrapv (milliseconds, 1000, &timestamp))
return 0;
/* And add the remaining nsec portion. */
if (IntAddWrapv (timestamp, (clock->tv_nsec % 1000000) / 1000,
&timestamp))
/* Overflow. */
return 0;
@ -197,8 +227,8 @@ HandleEndFrame (Timer *timer, void *data, struct timespec time)
static void
PostEndFrame (FrameClock *clock)
{
uint64_t target, now;
struct timespec timespec;
uint64_t target, fallback, now, additional;
struct timespec timespec, current_time;
XLAssert (clock->end_frame_timer == NULL);
@ -206,11 +236,27 @@ PostEndFrame (FrameClock *clock)
|| !clock->presentation_time)
return;
/* Obtain the monotonic clock time. */
clock_gettime (CLOCK_MONOTONIC, &current_time);
/* Calculate the time by which the next frame must be drawn. It is
a multiple of the refresh rate with the vertical blanking
period added. */
target = clock->last_frame_time + clock->presentation_time;
now = CurrentHighPrecisionTimestamp ();
now = HighPrecisionTimestamp (&current_time);
additional = 0;
/* If now is more than UINT32_MAX * 1000, then this timestamp may
overflow the 32-bit X server time, depending on how the X
compositing manager implements timestamp generation. Generate a
fallback timestamp to use in that situation.
Use now << 10 instead of now / 1000; the difference is too small
to be noticeable. */
if (now << 10 > UINT32_MAX)
fallback = HighPrecisionTimestamp32 (&current_time);
else
fallback = 0;
if (!now)
return;
@ -218,7 +264,22 @@ PostEndFrame (FrameClock *clock)
/* If the last time the frame time was obtained was that long ago,
return immediately. */
if (now - clock->last_frame_time >= MaxPresentationAge)
return;
{
if ((fallback - clock->last_frame_time) <= MaxPresentationAge)
{
/* Some compositors wrap around once the X server time
overflows the 32-bit Time type. If now happens to be
within the limit after its millisecond portion is
truncated to 32 bits, continue, after setting the
additional value the difference between the truncated
value and the actual time. */
additional = now - fallback;
now = fallback;
}
else
return;
}
while (target < now)
{
@ -226,15 +287,19 @@ PostEndFrame (FrameClock *clock)
return;
}
/* Convert the high precision timestamp to a timespec. */
if (!HighPrecisionTimestampToTimespec (target, &timespec))
return;
/* Use 3/4ths of the presentation time. Any more and we risk the
counter value change signalling the end of the frame arriving
after the presentation deadline. */
target = target - (clock->presentation_time / 4 * 3);
/* Add the remainder of now if it was probably truncated by the
compositor. */
target += additional;
/* Convert the high precision timestamp to a timespec. */
if (!HighPrecisionTimestampToTimespec (target, &timespec))
return;
/* Schedule the timer marking the end of this frame for the target
time. */
clock->end_frame_timer = AddTimerWithBaseTime (HandleEndFrame,
@ -435,7 +500,7 @@ XLFrameClockFreeze (FrameClock *clock)
else
{
RemoveTimer (clock->end_frame_timer);
clock->end_frame_timer = NULL;
clock->end_frame_timer = NULL;
}
/* Don't unfreeze until the next EndFrame. */

View file

@ -325,16 +325,54 @@ GetScale (DrawParams *params)
return 1.0;
}
static double
GetSourceX (DrawParams *params)
{
if (params->flags & OffsetSet)
return params->off_x;
return 0.0;
}
static double
GetSourceY (DrawParams *params)
{
if (params->flags & OffsetSet)
return params->off_y;
return 0.0;
}
static Bool
CompareStretch (DrawParams *params, DrawParams *other)
{
if ((params->flags & StretchSet)
!= (other->flags & StretchSet))
return False;
if (params->flags & StretchSet)
return (other->crop_width == params->crop_width
&& other->crop_height == params->crop_height
&& other->stretch_width == params->stretch_width
&& other->stretch_height == params->stretch_height);
return True;
}
static void
MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
{
XTransform transform;
Matrix ftransform;
if (GetScale (params) == GetScale (&buffer->params))
if (GetScale (params) == GetScale (&buffer->params)
&& GetSourceX (params) == GetSourceX (&buffer->params)
&& GetSourceY (params) == GetSourceY (&buffer->params)
&& CompareStretch (params, &buffer->params))
/* Nothing changed. */
return;
/* Otherwise, compute and apply the new transform. */
/* Otherwise, compute and apply the new transform. */
if (!params->flags)
/* No transform of any kind is set, use the identity matrix. */
XRenderSetPictureTransform (compositor.display,
@ -342,14 +380,29 @@ MaybeApplyTransform (PictureBuffer *buffer, DrawParams *params)
&identity_transform);
else
{
memset (&transform, 0, sizeof transform);
MatrixIdentity (&ftransform);
transform.matrix[0][0] = XDoubleToFixed (1.0 / params->scale);
transform.matrix[1][1] = XDoubleToFixed (1.0 / params->scale);
transform.matrix[2][2] = XDoubleToFixed (1);
/* Note that these must be applied in the right order. First,
the scale is applied. Then, the offset, and finally the
stretch. */
if (params->flags & ScaleSet)
MatrixScale (&ftransform, 1.0 / GetScale (params),
1.0 / GetScale (params));
XRenderSetPictureTransform (compositor.display,
buffer->picture,
if (params->flags & OffsetSet)
MatrixTranslate (&ftransform, params->off_x, params->off_y);
if (params->flags & StretchSet)
MatrixScale (&ftransform,
params->crop_width / params->stretch_width,
params->crop_height / params->stretch_height);
/* Export the matrix to an XTransform. */
MatrixExport (&ftransform, &transform);
/* Set the transform. The transform maps from dest coords to
picture coords, so that [X Y 1] = TRANSFORM * [DX DY 1]. */
XRenderSetPictureTransform (compositor.display, buffer->picture,
&transform);
}

View file

@ -60,7 +60,8 @@ struct _Positioner
int refcount;
};
/* Scale factor used during constraint adjustment calculation. */
/* Surface used to handle scaling during constraint adjustment
calculation. */
static double scale_adjustment_factor;
static void
@ -673,8 +674,12 @@ GetAdjustmentOffset (Role *parent, int *off_x, int *off_y)
&parent_gy, NULL, NULL);
XLXdgRoleCurrentRootPosition (parent, &root_x, &root_y);
*off_x = root_x + parent_gx * parent->surface->factor;
*off_y = root_y + parent_gy * parent->surface->factor;
/* Convert the gx and gy to the window coordinate system. */
TruncateSurfaceToWindow (parent->surface, parent_gx, parent_gy,
&parent_gx, &parent_gy);
*off_x = root_x + parent_gx;
*off_y = root_y + parent_gy;
}
static void
@ -684,18 +689,19 @@ ApplyConstraintAdjustment (Positioner *positioner, Role *parent, int x,
{
int width, height, cx, cy, cwidth, cheight, off_x, off_y;
width = positioner->width * scale_adjustment_factor;
height = positioner->height * scale_adjustment_factor;
width = positioner->width;
height = positioner->height;
/* Constraint calculations are simplest if we use scaled
coordinates, and then unscale them later. */
TruncateSurfaceToWindow (parent->surface, x, y, &x, &y);
TruncateScaleToWindow (parent->surface, width, height, &width,
&height);
/* Set the factor describing how to convert surface coordinates to
window ones. */
scale_adjustment_factor = parent->surface->factor;
/* Constraint calculations are simplest if we use scaled
coordinates, and then unscale them later. */
x *= scale_adjustment_factor;
y *= scale_adjustment_factor;
if (positioner->constraint_adjustment
== XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE)
/* There is no constraint adjustment. */
@ -740,10 +746,14 @@ ApplyConstraintAdjustment (Positioner *positioner, Role *parent, int x,
off_y, &y, &height);
finish:
*x_out = x / scale_adjustment_factor;
*y_out = y / scale_adjustment_factor;
*width_out = width / scale_adjustment_factor;
*height_out = height / scale_adjustment_factor;
/* Now, scale the coordinates back. */
TruncateWindowToSurface (parent->surface, x, y, &x, &y);
TruncateScaleToSurface (parent->surface, width, height, &width, &height);
*x_out = x;
*y_out = y;
*width_out = width;
*height_out = height;
}
void
@ -754,8 +764,12 @@ XLPositionerCalculateGeometry (Positioner *positioner, Role *parent,
int x, y, width, height;
CalculatePosition (positioner, &x, &y);
ApplyConstraintAdjustment (positioner, parent, x, y,
&x, &y, &width, &height);
if (parent->surface)
ApplyConstraintAdjustment (positioner, parent, x, y,
&x, &y, &width, &height);
else
width = positioner->width, height = positioner->height;
*x_out = x;
*y_out = y;

View file

@ -241,12 +241,12 @@ RenderFreeDmabufBuffer (RenderBuffer buffer)
void
RenderUpdateBufferForDamage (RenderBuffer buffer, pixman_region32_t *damage,
float scale)
DrawParams *params)
{
if (!buffer_funcs.update_buffer_for_damage)
return;
buffer_funcs.update_buffer_for_damage (buffer, damage, scale);
buffer_funcs.update_buffer_for_damage (buffer, damage, params);
}
Bool

View file

@ -31,19 +31,19 @@ main (void)
attribute vec2 pos;
attribute vec2 texcoord;
varying vec2 v_texcoord;
uniform mat3 source;
void
main (void)
{
gl_Position = vec4 (pos.x, pos.y, 1.0, 1.0);
v_texcoord = texcoord;
v_texcoord = (source * vec3 (texcoord, 1.0)).xy;
}
//==
//== Composite Rectangle Fragment Shader RGBA
precision mediump float;
uniform sampler2D texture;
uniform float scale;
uniform bool invert_y;
varying vec2 v_texcoord;
@ -52,7 +52,7 @@ main (void)
{
vec2 texcoord;
texcoord = v_texcoord / scale;
texcoord = v_texcoord;
if (invert_y)
texcoord = vec2 (texcoord.x, 1.0 - texcoord.y);
@ -64,7 +64,7 @@ main (void)
//== Composite Rectangle Fragment Shader RGBX
precision mediump float;
uniform sampler2D texture;
uniform float scale;
uniform mat3 source;
uniform bool invert_y;
varying vec2 v_texcoord;
@ -73,14 +73,12 @@ main (void)
{
vec2 texcoord;
texcoord = v_texcoord / scale;
texcoord = v_texcoord;
if (invert_y)
texcoord = vec2 (texcoord.x, 1.0 - texcoord.y);
gl_FragColor = vec4 (texture2D (texture,
texcoord).rgb,
1.0);
gl_FragColor = vec4 (texture2D (texture, texcoord).rgb, 1.0);
}
//==
@ -89,7 +87,7 @@ main (void)
precision mediump float;
uniform samplerExternalOES texture;
uniform float scale;
uniform mat3 source;
uniform bool invert_y;
varying vec2 v_texcoord;
@ -98,7 +96,7 @@ main (void)
{
vec2 texcoord;
texcoord = v_texcoord / scale;
texcoord = v_texcoord;
if (invert_y)
texcoord = vec2 (texcoord.x, 1.0 - texcoord.y);

View file

@ -192,7 +192,11 @@ enum
{
/* This means that the view and all its inferiors should be
skipped in bounds computation, input tracking, et cetera. */
ViewIsUnmapped = 1,
ViewIsUnmapped = 1,
/* This means that the view has a viewport specifying its size,
effectively decoupling its relation to the buffer width and
height. */
ViewIsViewported = 1 << 2,
};
#define IsViewUnmapped(view) \
@ -202,6 +206,13 @@ enum
#define ClearUnmapped(view) \
((view)->flags &= ~ViewIsUnmapped)
#define IsViewported(view) \
((view)->flags & ViewIsViewported)
#define SetViewported(view) \
((view)->flags |= ViewIsViewported)
#define ClearViewported(view) \
((view)->flags &= ~ViewIsViewported)
#endif
struct _List
@ -273,6 +284,13 @@ struct _View
/* Flags; whether or not this view is unmapped, etc. */
int flags;
/* The viewport data. */
double src_x, src_y, crop_width, crop_height, dest_width, dest_height;
/* Fractional offset applied to the view contents and damage during
compositing. */
double fract_x, fract_y;
#else
/* Label used during tests. */
const char *label;
@ -749,7 +767,6 @@ ViewUpdateBoundsForInsert (View *view)
view);
}
#endif
TEST_STATIC void
@ -1214,6 +1231,10 @@ main (int argc, char **argv)
#ifndef TEST
/* Notice that VIEW's size has changed, while VIEW itself has not
moved. Recompute the max_x, min_x, min_y, and max_y of its
subcompositor. */
static void
ViewAfterSizeUpdate (View *view)
{
@ -1272,7 +1293,11 @@ ViewAttachBuffer (View *view, ExtBuffer *buffer)
&& (XLBufferWidth (buffer) != XLBufferWidth (old)
|| XLBufferHeight (buffer) != XLBufferHeight (old))))
{
if (view->subcompositor)
if (view->subcompositor
/* If a viewport is specified, then the view width and
height are determined independently from the buffer
size. */
&& !IsViewported (view))
{
/* A new buffer was attached, so garbage the subcompositor
as well. */
@ -1398,6 +1423,20 @@ ViewMove (View *view, int x, int y)
}
}
void
ViewMoveFractional (View *view, double x, double y)
{
XLAssert (x < 1.0 && y < 1.0);
/* This does not necessitate adjustments to the view size, but does
require that the subcompositor be garbaged. */
view->fract_x = x;
view->fract_y = y;
if (view->subcompositor)
SetGarbaged (view->subcompositor);
}
void
ViewDetach (View *view)
{
@ -1477,8 +1516,9 @@ ViewFree (View *view)
void
ViewDamage (View *view, pixman_region32_t *damage)
{
pixman_region32_union (&view->damage,
&view->damage,
/* This damage must be transformed by the viewport and scale, but
must NOT be transformed by the subpixel (fractional) offset. */
pixman_region32_union (&view->damage, &view->damage,
damage);
}
@ -1509,6 +1549,15 @@ ViewGetSubcompositor (View *view)
return view->subcompositor;
}
static double
GetContentScale (int scale)
{
if (scale > 0)
return 1.0 / (scale + 1);
return -scale + 1;
}
int
ViewWidth (View *view)
{
@ -1517,12 +1566,20 @@ ViewWidth (View *view)
if (!view->buffer)
return 0;
if (IsViewported (view))
/* The view has a viewport specified. view->dest_width and
view->dest_height can be fractional values. When that happens,
we simply use the ceiling and rely on the renderer to DTRT with
scaling. */
return ceil (view->dest_width
* GetContentScale (view->scale));
width = XLBufferWidth (view->buffer);
if (view->scale < 0)
return width * (abs (view->scale) + 1);
return ceil (width * (abs (view->scale) + 1));
else
return width / (view->scale + 1);
return ceil (width / (view->scale + 1));
}
int
@ -1533,68 +1590,82 @@ ViewHeight (View *view)
if (!view->buffer)
return 0;
if (IsViewported (view))
/* The view has a viewport specified. view->dest_width and
view->dest_height can be fractional values. When that happens,
we simply use the ceiling and rely on the renderer to DTRT with
scaling. */
return ceil (view->dest_height
* GetContentScale (view->scale));
height = XLBufferHeight (view->buffer);
if (view->scale < 0)
return height * (abs (view->scale) + 1);
return ceil (height * (abs (view->scale) + 1));
else
return height / (view->scale + 1);
return ceil (height / (view->scale + 1));
}
void
ViewSetScale (View *view, int scale)
{
int doflags;
/* First, assume we will have to compute both max_x and max_y. */
doflags = DoMaxX | DoMaxY;
if (view->scale == scale)
return;
view->scale = scale;
if (view->subcompositor)
{
/* If the view is now wider than max_x and/or max_y, update those
now. */
if (view->subcompositor->max_x < ViewMaxX (view))
{
view->subcompositor->max_x = ViewMaxX (view);
/* We don't have to update max_x anymore. */
doflags &= ~DoMaxX;
}
if (view->subcompositor->max_y < ViewMaxY (view))
{
view->subcompositor->max_y = ViewMaxY (view);
/* We don't have to update max_x anymore. */
doflags &= ~DoMaxY;
}
/* Finally, update the bounds. */
SubcompositorUpdateBounds (view->subcompositor,
doflags);
}
/* Recompute subcompositor bounds; they could've changed. */
ViewAfterSizeUpdate (view);
}
static double
GetContentScale (int scale)
{
if (scale > 0)
return 1.0 / (scale + 1);
return -scale + 1;
void
ViewSetViewport (View *view, double src_x, double src_y,
double crop_width, double crop_height,
double dest_width, double dest_height)
{
SetViewported (view);
view->src_x = src_x;
view->src_y = src_y;
view->crop_width = crop_width;
view->crop_height = crop_height;
view->dest_width = dest_width;
view->dest_height = dest_height;
/* Update min_x and min_y. */
ViewAfterSizeUpdate (view);
/* Garbage the subcompositor as damage can no longer be trusted. */
if (view->subcompositor)
SubcompositorGarbage (view->subcompositor);
}
void
ViewClearViewport (View *view)
{
ClearViewported (view);
/* Update min_x and min_y. */
ViewAfterSizeUpdate (view);
/* Garbage the subcompositor as damage can no longer be trusted. */
if (view->subcompositor)
SubcompositorGarbage (view->subcompositor);
}
static void
ViewComputeTransform (View *view, DrawParams *params)
ViewComputeTransform (View *view, DrawParams *params, Bool draw)
{
/* Compute the effective transform of VIEW, then put it in PARAMS.
DRAW means whether or not the transform is intended for drawing;
when not set, the parameters are being used for damage tracking
instead. */
/* First, there is no transform. */
params->flags = 0;
params->off_x = 0.0;
params->off_y = 0.0;
if (view->scale)
{
@ -1602,6 +1673,45 @@ ViewComputeTransform (View *view, DrawParams *params)
params->flags |= ScaleSet;
params->scale = GetContentScale (view->scale);
}
if (IsViewported (view))
{
/* Set the viewport (a.k.a "stretch" and "offset" in the
rendering code). */
params->flags |= StretchSet;
params->flags |= OffsetSet;
params->off_x = view->src_x;
params->off_y = view->src_y;
params->crop_width = view->crop_width;
params->stretch_width = view->dest_width;
params->crop_height = view->crop_height;
params->stretch_height = view->dest_height;
/* If the crop width/height were not specified, use the current
buffer width/height. */
if (params->crop_width == -1)
{
params->crop_width = (XLBufferWidth (view->buffer)
* GetContentScale (view->scale));
params->crop_height = (XLBufferHeight (view->buffer)
* GetContentScale (view->scale));
}
}
if ((view->fract_x != 0.0 || view->fract_y != 0.0)
&& draw)
{
params->flags |= OffsetSet;
/* This is not entirely right. When applying a negative offset,
contents to the left of where the picture actually is can
appear to "shine through". */
params->off_x -= view->fract_x;
params->off_y -= view->fract_y;
}
}
void
@ -1740,7 +1850,7 @@ IntersectBoxes (pixman_box32_t *in, pixman_box32_t *other,
out->y2 = MIN (a.y2, b.y2);
/* If the intersection is empty, return False. */
if (out->x2 - out->x1 <= 0 || out->y2 - out->y1 <= 0)
if (out->x2 - out->x1 < 0 || out->y2 - out->y1 < 0)
return False;
return True;
@ -1754,7 +1864,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
View *start, *original_start, *view, *first;
List *list;
pixman_box32_t *boxes, *extents, temp_boxes;
int nboxes, i, tx, ty;
int nboxes, i, tx, ty, view_width, view_height;
Operation op;
RenderBuffer buffer;
int min_x, min_y;
@ -1862,6 +1972,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (!view->buffer)
goto next;
/* Obtain the view width and height here. */
view_width = ViewWidth (view);
view_height = ViewHeight (view);
if (!start)
{
start = view;
@ -1882,8 +1996,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
pixman_region32_intersect_rect (&temp, &view->opaque,
view->abs_x, view->abs_y,
ViewWidth (view),
ViewHeight (view));
view_width, view_height);
if (IsOpaqueDirty (subcompositor))
pixman_region32_union (&total_opaque, &total_opaque, &temp);
@ -1922,8 +2035,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
pixman_region32_intersect_rect (&temp, &view->input,
view->abs_x, view->abs_y,
ViewWidth (view),
ViewHeight (view));
view_width, view_height);
pixman_region32_union (&total_input, &total_input, &temp);
@ -1942,9 +2054,13 @@ SubcompositorUpdate (Subcompositor *subcompositor)
However, the function does not perform partial updates
when the damage region is empty. */
/* Compute the transform and put it in draw_params, so TRT
can be done in the rendering backend. */
ViewComputeTransform (view, &draw_params, False);
buffer = XLRenderBufferFromBuffer (view->buffer);
RenderUpdateBufferForDamage (buffer, &list->view->damage,
GetContentScale (list->view->scale));
&draw_params);
if (pixman_region32_not_empty (&list->view->damage))
{
@ -1958,9 +2074,21 @@ SubcompositorUpdate (Subcompositor *subcompositor)
clipping. */
pixman_region32_intersect_rect (&temp, &list->view->damage,
view->abs_x, view->abs_y,
ViewWidth (view),
ViewHeight (view));
view_width, view_height);
/* If a fractional offset is set, extend the damage by 1
pixel to cover the offset. */
if (view->fract_x != 0.0 && view->fract_y != 0.0)
{
XLExtendRegion (&temp, &temp, 1, 1);
/* Intersect the region again. */
pixman_region32_intersect_rect (&temp, &temp, view->abs_x,
view->abs_y, view_width,
view_height);
}
/* Union the region with the update region. */
pixman_region32_union (&update_region, &temp, &update_region);
/* If the damage extends outside the area known to be
@ -2100,6 +2228,10 @@ SubcompositorUpdate (Subcompositor *subcompositor)
if (!view->buffer)
goto next_1;
/* Get the view width and height here. */
view_width = ViewWidth (view);
view_height = ViewHeight (view);
buffer = XLRenderBufferFromBuffer (view->buffer);
if (IsGarbaged (subcompositor))
@ -2113,12 +2245,18 @@ SubcompositorUpdate (Subcompositor *subcompositor)
Note that if the subcompositor is not garbaged, then this
has already been done. */
RenderUpdateBufferForDamage (buffer, NULL, 0.0f);
else if (age < 0 || age > 3)
/* The target contents are too old, but the damage can be
trusted. */
RenderUpdateBufferForDamage (buffer, &view->damage,
GetContentScale (list->view->scale));
RenderUpdateBufferForDamage (buffer, NULL, NULL);
else if (age < 0 || age >= 3)
{
/* Compute the transform and put it in draw_params, so TRT
can be done in the rendering backend. */
ViewComputeTransform (view, &draw_params, False);
/* The target contents are too old, but the damage can be
trusted. */
RenderUpdateBufferForDamage (buffer, &view->damage,
&draw_params);
}
if (!first)
{
@ -2126,7 +2264,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
with PictOpSrc so that transparency is applied correctly,
if it contains the entire update region. */
if (IsGarbaged (subcompositor) || age < 0 || age > 3)
if (IsGarbaged (subcompositor) || age < 0 || age >= 3)
{
extents = &temp_boxes;
@ -2149,7 +2287,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
/* Otherwise, fill the whole update region with
transparency. */
if (IsGarbaged (subcompositor) || age < 0 || age > 3)
if (IsGarbaged (subcompositor) || age < 0 || age >= 3)
{
/* Use the entire subcompositor bounds if
garbaged. */
@ -2174,7 +2312,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
first = view;
/* Compute the transform and put it in draw_params. */
ViewComputeTransform (view, &draw_params);
ViewComputeTransform (view, &draw_params, True);
if (!IsGarbaged (subcompositor) && (age >= 0 && age < 3))
{
@ -2190,8 +2328,8 @@ SubcompositorUpdate (Subcompositor *subcompositor)
temp_boxes.x1 = view->abs_x;
temp_boxes.y1 = view->abs_y;
temp_boxes.x2 = view->abs_x + ViewWidth (view);
temp_boxes.y2 = view->abs_y + ViewHeight (view);
temp_boxes.x2 = view->abs_x + view_width;
temp_boxes.y2 = view->abs_y + view_height;
if (IntersectBoxes (&boxes[i], &temp_boxes, &temp_boxes))
RenderComposite (buffer, subcompositor->target, op,
@ -2226,9 +2364,9 @@ SubcompositorUpdate (Subcompositor *subcompositor)
/* dst-y. */
view->abs_y - min_y + ty,
/* width. */
ViewWidth (view),
view_width,
/* height, draw-params. */
ViewHeight (view), &draw_params);
view_height, &draw_params);
/* Also adjust the opaque and input regions here. */
@ -2248,8 +2386,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
pixman_region32_intersect_rect (&temp, &view->opaque,
view->abs_x, view->abs_y,
ViewWidth (view),
ViewHeight (view));
view_width, view_height);
pixman_region32_union (&total_opaque, &temp, &total_opaque);
/* Translate it back. */
@ -2270,8 +2407,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
list->view->abs_y);
pixman_region32_intersect_rect (&temp, &view->input,
view->abs_x, view->abs_y,
ViewWidth (view),
ViewHeight (view));
view_width, view_height);
pixman_region32_union (&total_input, &temp, &total_input);
/* Translate it back. */
@ -2292,7 +2428,7 @@ SubcompositorUpdate (Subcompositor *subcompositor)
complete:
if (IsGarbaged (subcompositor)
|| ((age < 0 || age > 3)
|| ((age < 0 || age >= 3)
&& (IsInputDirty (subcompositor)
|| IsOpaqueDirty (subcompositor))))
{
@ -2436,11 +2572,17 @@ SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
boxes = pixman_region32_rectangles (&temp, &nboxes);
/* Compute the transform. */
ViewComputeTransform (view, &draw_params);
ViewComputeTransform (view, &draw_params, False);
/* Update the attached buffer from any damage. */
RenderUpdateBufferForDamage (buffer, &list->view->damage,
GetContentScale (list->view->scale));
&draw_params);
/* If a fractional offset is set, recompute the transform again,
this time for drawing. */
if (list->view->fract_x != 0.0
|| list->view->fract_y != 0.0)
ViewComputeTransform (view, &draw_params, True);
for (i = 0; i < nboxes; ++i)
RenderComposite (buffer, subcompositor->target, op,

View file

@ -493,8 +493,10 @@ MaybeUpdateOutputs (Subsurface *subsurface)
return;
/* Compute the positions relative to the parent. */
x = subsurface->current_substate.x * subsurface->parent->factor;
y = subsurface->current_substate.y * subsurface->parent->factor;
x = floor (subsurface->current_substate.x
* subsurface->parent->factor);
y = floor (subsurface->current_substate.y
* subsurface->parent->factor);
/* And the base X and Y. */
base_x = subsurface->role.surface->output_x;
@ -525,6 +527,38 @@ MaybeUpdateOutputs (Subsurface *subsurface)
width, height);
}
static void
MoveFractional (Subsurface *subsurface)
{
double x, y;
int x_int, y_int;
/* Move the surface to a fractional window (subcompositor)
coordinate relative to the parent. This is done by placing the
surface at the floor of the coordinates, and then offsetting the
image and input by the remainder during rendering. */
SurfaceToWindow (subsurface->parent, subsurface->current_substate.x,
subsurface->current_substate.y, &x, &y);
x_int = floor (x);
y_int = floor (y);
/* Move the subsurface to x_int, y_int. */
ViewMove (subsurface->role.surface->view, x_int, y_int);
ViewMove (subsurface->role.surface->under, x_int, y_int);
/* Apply the fractional offset. */
ViewMoveFractional (subsurface->role.surface->view,
x - x_int, y - y_int);
ViewMoveFractional (subsurface->role.surface->under,
x - x_int, y - y_int);
/* And set the fractional offset on the surface for input handling
purposes. */
subsurface->role.surface->input_delta_x = x - x_int;
subsurface->role.surface->input_delta_y = y - y_int;
}
static void
AfterParentCommit (Surface *surface, void *data)
{
@ -540,17 +574,14 @@ AfterParentCommit (Surface *surface, void *data)
if (subsurface->pending_substate.flags & PendingPosition)
{
/* Apply the new position. */
subsurface->current_substate.x
= subsurface->pending_substate.x;
subsurface->current_substate.y
= subsurface->pending_substate.y;
/* The X and Y coordinates here are also parent-local and must
be scaled by the global scale factor. */
ViewMove (subsurface->role.surface->view,
subsurface->current_substate.x * subsurface->parent->factor,
subsurface->current_substate.y * subsurface->parent->factor);
/* And move the views. */
MoveFractional (subsurface);
}
/* And any cached surface state too. */
@ -680,6 +711,10 @@ Setup (Surface *surface, Role *role)
ViewGetSubcompositor (parent_view));
ViewInsert (parent_view, surface->view);
/* Now move the subsurface to its initial location (0, 0) */
if (subsurface->parent)
MoveFractional (subsurface);
/* Now add the subsurface to the parent's list of subsurfaces. */
subsurface->parent->subsurfaces
= XLListPrepend (subsurface->parent->subsurfaces,
@ -698,9 +733,7 @@ Rescale (Surface *surface, Role *role)
/* The scale factor changed; move the subsurface to the new correct
position. */
ViewMove (surface->view,
subsurface->current_substate.x * subsurface->parent->factor,
subsurface->current_substate.y * subsurface->parent->factor);
MoveFractional (subsurface);
}
static void

336
surface.c
View file

@ -22,6 +22,7 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <string.h>
#include <inttypes.h>
#include <float.h>
#include "compositor.h"
@ -558,9 +559,7 @@ ApplyScale (Surface *surface)
D = C / L
D = (A * E) / (A / B)
D = B * E
Phew. */
D = B * E. */
b = scale;
g = global_scale_factor;
@ -637,6 +636,161 @@ ApplyInputRegion (Surface *surface)
}
}
static void
ApplyViewport (Surface *surface)
{
State *state;
int dest_width, dest_height;
double crop_width, crop_height, src_x, src_y;
double max_width, max_height;
state = &surface->current_state;
/* If no values are specified, return and clear the viewport. */
if (state->src_x == -1 && state->dest_width == -1)
{
ViewClearViewport (surface->view);
return;
}
/* Calculate the viewport. crop_width and crop_height describe the
amount by which to crop the surface contents, after conversion to
window geometry. dest_width and dest_height then describe how
large the surface should be. src_x and src_y describe the
origin at which to start sampling from the buffer. */
if (state->buffer)
{
max_width = XLBufferWidth (state->buffer);
max_height = XLBufferHeight (state->buffer);
}
else
{
/* If state->buffer is not set then the source rectangle does
not have to be validated now. It will be validated later
once the buffer is attached. */
max_width = DBL_MAX;
max_height = DBL_MAX;
}
if (state->src_x != -1.0)
{
/* This means a source rectangle has been specified. Set src_x
and src_y. */
src_x = state->src_x;
src_y = state->src_y;
/* Also set crop_width and crop_height. */
crop_width = state->src_width;
crop_height = state->src_height;
}
else
{
/* Set crop_width and crop_height to the default values, which
are the width and height of the buffer divided by the buffer
scale. */
src_x = 0;
src_y = 0;
crop_width = -1;
crop_height = -1;
}
/* Now, either dest_width/dest_height are specified, or dest_width
and dest_height should be crop_width and crop_height. If the
latter, then crop_width and crop_height must be integer
values. */
if (state->dest_width != -1)
{
/* This means dest_width and dest_height have been explicitly
specified. */
dest_width = state->dest_width;
dest_height = state->dest_height;
}
else
{
if ((rint (crop_width) != crop_width
|| rint (crop_height) != crop_height)
/* If the src_width and src_height were not specified
manually but were computed from the buffer scale, don't
complain that they are not integer values. The
underlying viewport code satisfactorily handles
fractional width and height anyway. */
&& state->src_x != 1.0)
goto bad_size;
dest_width = state->src_width;
dest_height = state->src_height;
}
/* Now all of the fields above must be set. Verify that none of
them lie outside the buffer. */
if (state->src_x != -1
&& (src_x + crop_width - 1 >= max_width / state->buffer_scale
|| src_y + crop_height - 1 >= max_height / state->buffer_scale))
goto out_of_buffer;
/* Finally, set the viewport. Convert the values to window
coordinates. */
src_x *= surface->factor;
src_y *= surface->factor;
if (crop_width != -1)
{
crop_width *= surface->factor;
crop_height *= surface->factor;
}
dest_width *= surface->factor;
dest_height *= surface->factor;
/* And really set the viewport. */
ViewSetViewport (surface->view, src_x, src_y, crop_width,
crop_height, dest_width, dest_height);
return;
bad_size:
/* By this point, surface->viewport should be non-NULL; however, if
a synchronous subsurface applies invalid viewporter state,
commits it, destroys the wp_viewport resource, and the parent
commits, then the cached state applied due to the parent commit
will be invalid, but the viewport resource will no longer be
associated with the surface. I don't know what to do in that
case, so leave the behavior undefined. */
if (surface->viewport)
XLWpViewportReportBadSize (surface->viewport);
return;
out_of_buffer:
if (surface->viewport)
XLWpViewportReportOutOfBuffer (surface->viewport);
}
static void
CheckViewportValues (Surface *surface)
{
State *state;
int width, height;
state = &surface->current_state;
if (!surface->viewport || state->src_x == -1.0
|| !state->buffer)
return;
/* A buffer is attached and a viewport source rectangle is set;
check that it remains in bounds. */
width = XLBufferWidth (state->buffer);
height = XLBufferHeight (state->buffer);
if (state->src_x + state->src_width - 1 >= width / state->buffer_scale
|| state->src_y + state->src_height - 1 >= height / state->buffer_scale)
XLWpViewportReportBadSize (surface->viewport);
}
static void
HandleScaleChanged (void *data, int new_scale)
{
@ -650,6 +804,7 @@ HandleScaleChanged (void *data, int new_scale)
ApplyScale (surface);
ApplyInputRegion (surface);
ApplyOpaqueRegion (surface);
ApplyViewport (surface);
/* Next, call any role-specific hooks. */
if (surface->role && surface->role->funcs.rescale)
@ -680,21 +835,44 @@ ApplyDamage (Surface *surface)
{
pixman_region32_t temp;
int scale;
float x_factor, y_factor;
scale = GetEffectiveScale (surface->current_state.buffer_scale);
/* N.B. that this must come after the scale is applied. */
if (scale)
if (scale || surface->current_state.src_x != -1
|| surface->current_state.dest_width != -1)
{
pixman_region32_init (&temp);
if (!scale)
x_factor = y_factor = 1.0;
if (scale > 0)
XLScaleRegion (&temp, &surface->current_state.damage,
1.0 / (scale + 1), 1.0 / (scale + 1));
x_factor = y_factor = 1.0 / (scale + 1);
else
x_factor = y_factor = abs (scale) + 1;
/* If a viewport dest size is set, add that to the scale as
well. */
if (surface->current_state.src_width != -1)
{
x_factor += (float) (surface->current_state.src_width
/ surface->current_state.dest_width);
y_factor += (float) (surface->current_state.src_height
/ surface->current_state.dest_height);
}
if (x_factor != 1.0f && y_factor != 1.0f)
XLScaleRegion (&temp, &surface->current_state.damage,
abs (scale) + 1, abs (scale) + 1);
x_factor, y_factor);
/* If a viewport is set, translate the damage region by the
src_x and src_y. This is lossy. */
if (surface->current_state.src_x != -1.0)
pixman_region32_translate (&temp,
floor (surface->current_state.src_x),
floor (surface->current_state.src_y));
ViewDamage (surface->view, &temp);
@ -767,6 +945,25 @@ SavePendingState (Surface *surface)
surface->cached_state.buffer_scale
= surface->pending_state.buffer_scale;
if (surface->pending_state.pending & PendingViewportDest)
{
surface->cached_state.dest_width
= surface->pending_state.dest_width;
surface->cached_state.dest_height
= surface->pending_state.dest_height;
}
if (surface->pending_state.pending & PendingViewportSrc)
{
surface->cached_state.src_x = surface->pending_state.src_x;
surface->cached_state.src_y = surface->pending_state.src_y;
surface->cached_state.src_width
= surface->pending_state.src_width;
surface->cached_state.src_height
= surface->pending_state.src_height;
}
if (surface->pending_state.pending & PendingAttachments)
{
surface->cached_state.x = surface->pending_state.x;
@ -860,6 +1057,11 @@ InternalCommit (Surface *surface, State *pending)
pending->buffer);
ApplyBuffer (surface);
ClearBuffer (pending);
/* Check that any applied viewport source rectangles remain
valid. */
if (!(pending->pending & PendingViewportSrc))
CheckViewportValues (surface);
}
else
{
@ -889,6 +1091,29 @@ InternalCommit (Surface *surface, State *pending)
ApplyOpaqueRegion (surface);
}
if (pending->pending & PendingViewportSrc
|| pending->pending & PendingViewportDest)
{
/* Copy the viewport data over to the current state. */
if (pending->pending & PendingViewportDest)
{
surface->current_state.dest_width = pending->dest_width;
surface->current_state.dest_height = pending->dest_height;
}
if (pending->pending & PendingViewportSrc)
{
surface->current_state.src_x = pending->src_x;
surface->current_state.src_y = pending->src_y;
surface->current_state.src_width = pending->src_width;
surface->current_state.src_height = pending->src_height;
}
/* And apply the viewport now. */
ApplyViewport (surface);
}
if (pending->pending & PendingAttachments)
{
surface->current_state.x = pending->x;
@ -1077,6 +1302,14 @@ InitState (State *state)
state->frame_callbacks.next = &state->frame_callbacks;
state->frame_callbacks.last = &state->frame_callbacks;
state->frame_callbacks.resource = NULL;
/* Initialize the viewport to the default undefined values. */
state->dest_width = -1;
state->dest_height = -1;
state->src_x = -1.0;
state->src_y = -1.0;
state->src_width = -1.0;
state->src_height = -1.0;
}
static void
@ -1321,16 +1554,17 @@ XLStateDetachBuffer (State *state)
void
XLSurfaceRunFrameCallbacks (Surface *surface, struct timespec time)
{
uint32_t ms_time;
uint64_t ms_time;
XLList *list;
/* I don't know what else is reasonable in case of overflow. */
/* If ms_time is too large to fit in uint32_t, take the lower 32
bits. */
if (IntMultiplyWrapv (time.tv_sec, 1000, &ms_time))
ms_time = UINT32_MAX;
ms_time = UINT64_MAX;
else if (IntAddWrapv (ms_time, time.tv_nsec / 1000000,
&ms_time))
ms_time = UINT32_MAX;
ms_time = UINT64_MAX;
RunFrameCallbacks (&surface->current_state.frame_callbacks,
ms_time);
@ -1474,3 +1708,83 @@ XLSurfaceMoveBy (Surface *surface, int west, int north)
surface->role->funcs.move_by (surface, surface->role,
west, north);
}
/* The following functions convert from window to surface
coordinates and vice versa:
SurfaceToWindow - take given surface coordinate, and return a
window relative coordinate.
ScaleToWindow - take given surface dimension, and return a
window relative dimension.
WindowToSurface - take given window coordinate, and return a
surface relative coordinate as a double.
ScaleToSurface - take given window dimension, and return a
surface relative dimension.
Functions prefixed by "truncate" return and accept integer values
instead of floating point ones; truncation is performed on
fractional values. */
void
SurfaceToWindow (Surface *surface, double x, double y,
double *x_out, double *y_out)
{
*x_out = x * surface->factor + surface->input_delta_x;
*y_out = y * surface->factor + surface->input_delta_y;
}
void
ScaleToWindow (Surface *surface, double width, double height,
double *width_out, double *height_out)
{
*width_out = width * surface->factor;
*height_out = height * surface->factor;
}
void
WindowToSurface (Surface *surface, double x, double y,
double *x_out, double *y_out)
{
*x_out = x / surface->factor - surface->input_delta_x;
*y_out = y / surface->factor - surface->input_delta_y;
}
void
ScaleToSurface (Surface *surface, double width, double height,
double *width_out, double *height_out)
{
*width_out = width / surface->factor;
*height_out = height / surface->factor;
}
void
TruncateSurfaceToWindow (Surface *surface, int x, int y,
int *x_out, int *y_out)
{
*x_out = x * surface->factor + surface->input_delta_x;
*y_out = y * surface->factor + surface->input_delta_y;
}
void
TruncateScaleToWindow (Surface *surface, int width, int height,
int *width_out, int *height_out)
{
*width_out = width * surface->factor;
*height_out = height * surface->factor;
}
void
TruncateWindowToSurface (Surface *surface, int x, int y,
int *x_out, int *y_out)
{
*x_out = x / surface->factor - surface->input_delta_x;
*y_out = y / surface->factor - surface->input_delta_y;
}
void
TruncateScaleToSurface (Surface *surface, int width, int height,
int *width_out, int *height_out)
{
*width_out = width / surface->factor;
*height_out = height / surface->factor;
}

View file

@ -278,7 +278,6 @@ MoveWindow (XdgPopup *popup)
int root_x, root_y, parent_gx, parent_gy;
int geometry_x, geometry_y, x, y;
Window window;
double parent_scale, current_scale;
/* No parent was specified. */
if (!popup->parent)
@ -292,9 +291,6 @@ MoveWindow (XdgPopup *popup)
scale. */
return;
parent_scale = popup->parent->surface->factor;
current_scale = popup->role->surface->factor;
window = XLWindowFromXdgRole (popup->role);
XLXdgRoleGetCurrentGeometry (popup->parent, &parent_gx,
@ -305,17 +301,17 @@ MoveWindow (XdgPopup *popup)
&root_y);
/* Parent geometry is relative to the parent coordinate system. */
parent_gx *= parent_scale;
parent_gy *= parent_scale;
TruncateSurfaceToWindow (popup->parent->surface, parent_gx, parent_gy,
&parent_gx, &parent_gy);
/* geometry_x and geometry_y are relative to the local coordinate
system. */
geometry_x *= current_scale;
geometry_y *= current_scale;
TruncateSurfaceToWindow (popup->role->surface, geometry_x,
geometry_y, &geometry_x, &geometry_y);
/* X and Y are relative to the parent coordinate system. */
x = popup->x * parent_scale;
y = popup->y * parent_scale;
TruncateSurfaceToWindow (popup->parent->surface, popup->x,
popup->y, &x, &y);
XMoveWindow (compositor.display, window,
x + root_x + parent_gx - geometry_x,
@ -754,6 +750,15 @@ HandleParentResize (void *data)
InternalReposition (popup);
}
static Bool
IsWindowMapped (Role *role, XdgRoleImplementation *impl)
{
XdgPopup *popup;
popup = PopupFromRoleImpl (impl);
return popup->state & StateIsMapped;
}
static const struct xdg_popup_interface xdg_popup_impl =
{
.destroy = Destroy,
@ -798,6 +803,7 @@ XLGetXdgPopup (struct wl_client *client, struct wl_resource *resource,
popup->impl.funcs.ack_configure = AckConfigure;
popup->impl.funcs.note_size = NoteSize;
popup->impl.funcs.handle_geometry_change = HandleGeometryChange;
popup->impl.funcs.is_window_mapped = IsWindowMapped;
if (parent_resource)
{

View file

@ -628,6 +628,13 @@ Unfreeze (XdgRole *role)
XLFrameClockUnfreeze (role->clock);
}
static Bool
IsRoleMapped (XdgRole *role)
{
return role->impl->funcs.is_window_mapped (&role->role,
role->impl);
}
static void
Commit (Surface *surface, Role *role)
{
@ -666,6 +673,12 @@ Commit (Surface *surface, Role *role)
ack_configure. */
xdg_role->state &= ~StateWaitingForAckCommit;
/* If the window is unmapped, skip all of this code! Once the
window is mapped again, the compositor will send _NET_FRAME_DRAWN
should a frame still be in progress. */
if (!IsRoleMapped (xdg_role))
goto start_drawing;
/* A frame is already in progress, so instead say that an urgent
update is needed immediately after the frame completes. In any
case, don't run frame callbacks upon buffer release anymore. */
@ -687,6 +700,8 @@ Commit (Surface *surface, Role *role)
return;
}
start_drawing:
/* If the frame clock is frozen but we are no longer waiting for the
configure event to be acknowledged by the client, unfreeze the
frame clock. */
@ -706,7 +721,13 @@ Commit (Surface *surface, Role *role)
callbacks are not provided by the frame clock while it is frozen.
If that happens, just run the frame callback immediately. */
if (XLFrameClockIsFrozen (xdg_role->clock))
if (XLFrameClockIsFrozen (xdg_role->clock)
/* If the window is not mapped, then the native frame clock will
not draw frames. Some clients do commit before the initial
configure event and wait for the frame callback to be called
after or before ack_configure, leading to the mapping commit
never being performed. */
|| !IsRoleMapped (xdg_role))
RunFrameCallbacksConditionally (xdg_role);
return;
@ -843,6 +864,10 @@ Subframe (Surface *surface, Role *role)
return False;
}
/* Similarly, return False if the role is unmapped. */
if (!IsRoleMapped (xdg_role))
return False;
/* If a frame is already in progress, return False. Then, require a
late frame. */
if (XLFrameClockFrameInProgress (xdg_role->clock))
@ -1167,8 +1192,8 @@ GetResizeDimensions (Surface *surface, Role *role, int *x_out,
{
XLXdgRoleGetCurrentGeometry (role, NULL, NULL, x_out, y_out);
*x_out *= surface->factor;
*y_out *= surface->factor;
/* Scale these surface-local dimensions to window-local ones. */
TruncateSurfaceToWindow (surface, *x_out, *y_out, x_out, y_out);
}
static void
@ -1481,10 +1506,15 @@ XLXdgRoleCalcNewWindowSize (Role *role, int width, int height,
SubcompositorBounds (xdg_role->subcompositor,
&min_x, &min_y, &max_x, &max_y);
/* Adjust the current_width and current_height by the global scale
/* Calculate the current width and height. */
current_width = (max_x - min_x + 1);
current_height = (max_y - min_y + 1);
/* Adjust the current_width and current_height by the scale
factor. */
current_width = (max_x - min_x + 1) / role->surface->factor;
current_height = (max_y - min_y + 1) / role->surface->factor;
TruncateScaleToSurface (role->surface, current_width,
current_height, &current_width,
&current_height);
XLXdgRoleGetCurrentGeometry (role, NULL, NULL, &geometry_width,
&geometry_height);

View file

@ -283,6 +283,15 @@ RunUnmapCallbacks (XdgToplevel *toplevel)
toplevel->unmap_callbacks.last = &toplevel->unmap_callbacks;
}
static Bool
IsWindowMapped (Role *role, XdgRoleImplementation *impl)
{
XdgToplevel *toplevel;
toplevel = ToplevelFromRoleImpl (impl);
return toplevel->state & StateIsMapped;
}
static void
WriteHints (XdgToplevel *toplevel)
{
@ -362,13 +371,9 @@ NoteConfigureTime (Timer *timer, void *data, struct timespec time)
{
XdgToplevel *toplevel;
int width, height, effective_width, effective_height;
double factor;
toplevel = data;
/* Obtain the scale factor. toplevel->role->surface should not be
NULL here, as the timer is cancelled upon role detachment. */
factor = toplevel->role->surface->factor;
/* If only the window state changed, call SendStates. */
if (!(toplevel->state & StatePendingConfigureSize))
@ -379,8 +384,14 @@ NoteConfigureTime (Timer *timer, void *data, struct timespec time)
if (toplevel->state & StatePendingConfigureStates)
WriteStates (toplevel);
effective_width = toplevel->configure_width / factor;
effective_height = toplevel->configure_height / factor;
effective_width = toplevel->configure_width;
effective_height = toplevel->configure_height;
/* toplevel->role->surface should not be NULL here, as the timer
is cancelled upon role detachment. */
TruncateScaleToSurface (toplevel->role->surface,
effective_width, effective_height,
&effective_width, &effective_height);
/* Compute the geometry for the configure event based on the
current size of the toplevel. */
@ -482,24 +493,24 @@ static void
SendStates (XdgToplevel *toplevel)
{
int width, height;
double factor;
WriteStates (toplevel);
/* Obtain the scale factor. toplevel->role->surface should not be
NULL here. */
factor = toplevel->role->surface->factor;
/* Adjust the width and height we're sending by the window
geometry. */
if (toplevel->state & StateMissingState)
XLXdgRoleGetCurrentGeometry (toplevel->role, NULL, NULL,
&width, &height);
else
XLXdgRoleCalcNewWindowSize (toplevel->role,
toplevel->width / factor,
toplevel->height / factor,
&width, &height);
{
/* toplevel->role->surface should not be NULL here. */
TruncateScaleToSurface (toplevel->role->surface,
toplevel->width, toplevel->height,
&width, &height);
XLXdgRoleCalcNewWindowSize (toplevel->role, width,
height, &width, &height);
}
SendConfigure (toplevel, width, height);
@ -515,15 +526,11 @@ RecordStateSize (XdgToplevel *toplevel)
{
Bool a, b;
int width, height;
double factor;
if (!toplevel->role->surface)
/* We can't get the scale factor in this case. */
return;
/* Obtain the scale factor. */
factor = toplevel->role->surface->factor;
/* Record the last known size of a toplevel before its state is
changed. That way, we can send xdg_toplevel::configure with the
right state, should the window manager send ConfigureNotify
@ -539,8 +546,10 @@ RecordStateSize (XdgToplevel *toplevel)
upon minimization. */
XLXdgRoleGetCurrentGeometry (toplevel->role, NULL, NULL,
&width, &height);
width *= factor;
height *= factor;
/* Scale the width and height to window dimensions. */
TruncateScaleToWindow (toplevel->role->surface, width, height,
&width, &height);
}
else
{
@ -795,11 +804,9 @@ HandleWindowGeometryChange (XdgToplevel *toplevel)
XLXdgRoleGetCurrentGeometry (toplevel->role, &x, &y,
&width, &height);
width *= toplevel->role->surface->factor;
height *= toplevel->role->surface->factor;
x *= toplevel->role->surface->factor;
y *= toplevel->role->surface->factor;
TruncateScaleToWindow (toplevel->role->surface, width, height,
&width, &height);
TruncateSurfaceToWindow (toplevel->role->surface, x, y, &x, &y);
dx = SubcompositorWidth (subcompositor) - width;
dy = SubcompositorHeight (subcompositor) - height;
@ -813,21 +820,25 @@ HandleWindowGeometryChange (XdgToplevel *toplevel)
/* Initially, specify PSize. After the first MapNotify, also
specify PPosition so that subsurfaces won't move the window. */
hints->min_width = (toplevel->min_width
* toplevel->role->surface->factor
+ dx);
hints->min_height = (toplevel->min_height
* toplevel->role->surface->factor
+ dy);
/* First, make hints->min_width and hints->min_height the min width
in terms of the window coordinate system. Then, add deltas. */
TruncateScaleToWindow (toplevel->role->surface, toplevel->min_width,
toplevel->min_height, &hints->min_width,
&hints->min_height);
/* Add deltas. */
hints->min_width += dx;
hints->min_height += dy;
if (toplevel->max_width)
{
hints->max_width = (toplevel->max_width
* toplevel->role->surface->factor
+ dx);
hints->max_height = (toplevel->max_height
* toplevel->role->surface->factor
+ dy);
/* Do the same with the max width. */
TruncateScaleToWindow (toplevel->role->surface, toplevel->max_width,
toplevel->max_height, &hints->max_width,
&hints->max_height);
hints->max_width += dx;
hints->max_height += dy;
hints->flags |= PMaxSize;
}
else
@ -1373,10 +1384,13 @@ NoteWindowPreResize (Role *role, XdgRoleImplementation *impl,
XLXdgRoleGetCurrentGeometry (toplevel->role, &x, &y,
&gwidth, &gheight);
dx = width - gwidth * toplevel->role->surface->factor;
dy = height - gheight * toplevel->role->surface->factor;
x *= toplevel->role->surface->factor;
y *= toplevel->role->surface->factor;
/* Scale the window geometry to window dimensions. */
TruncateScaleToWindow (toplevel->role->surface, gwidth, gheight,
&gwidth, &gheight);
TruncateSurfaceToWindow (toplevel->role->surface, x, y, &x, &y);
dx = width - gwidth;
dy = height - gheight;
ApplyGtkFrameExtents (toplevel, x, y, dx - x, dy - y);
}
@ -1966,6 +1980,7 @@ XLGetXdgToplevel (struct wl_client *client, struct wl_resource *resource,
toplevel->impl.funcs.handle_geometry_change = HandleGeometryChange;
toplevel->impl.funcs.post_resize = PostResize;
toplevel->impl.funcs.commit_inside_frame = CommitInsideFrame;
toplevel->impl.funcs.is_window_mapped = IsWindowMapped;
/* Set up the sentinel node for the list of unmap callbacks. */
toplevel->unmap_callbacks.next = &toplevel->unmap_callbacks;