forked from 12to11/12to11
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:
parent
dfcd969d3e
commit
4d2e85d002
19 changed files with 1266 additions and 275 deletions
1
12to11.c
1
12to11.c
|
@ -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 ();
|
||||
|
|
13
Imakefile
13
Imakefile
|
@ -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
3
README
|
@ -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:
|
||||
|
||||
|
|
89
compositor.h
89
compositor.h
|
@ -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])
|
||||
|
|
3
dmabuf.c
3
dmabuf.c
|
@ -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
10
dnd.c
|
@ -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
312
egl.c
|
@ -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 (®ion);
|
||||
XLScaleRegion (®ion, damage, 1.0f / scale,
|
||||
1.0f / scale);
|
||||
UpdateBuffer (buffer, ®ion);
|
||||
pixman_region32_fini (®ion);
|
||||
}
|
||||
else
|
||||
UpdateBuffer (buffer, damage);
|
||||
UpdateBuffer (buffer, damage, params);
|
||||
}
|
||||
|
||||
static Bool
|
||||
|
|
30
fns.c
30
fns.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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, ×tamp)
|
||||
|| IntAddWrapv (timestamp, clock->tv_nsec / 1000, ×tamp))
|
||||
/* Overflow. */
|
||||
return 0;
|
||||
|
||||
if (IntMultiplyWrapv (clock.tv_sec, 1000000, ×tamp)
|
||||
|| IntAddWrapv (timestamp, clock.tv_nsec / 1000, ×tamp))
|
||||
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, ×tamp))
|
||||
return 0;
|
||||
|
||||
/* And add the remaining nsec portion. */
|
||||
if (IntAddWrapv (timestamp, (clock->tv_nsec % 1000000) / 1000,
|
||||
×tamp))
|
||||
/* 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, ¤t_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 (¤t_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 (¤t_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, ×pec))
|
||||
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, ×pec))
|
||||
return;
|
||||
|
||||
/* Schedule the timer marking the end of this frame for the target
|
||||
time. */
|
||||
clock->end_frame_timer = AddTimerWithBaseTime (HandleEndFrame,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
46
positioner.c
46
positioner.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
18
shaders.txt
18
shaders.txt
|
@ -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);
|
||||
|
|
292
subcompositor.c
292
subcompositor.c
|
@ -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,
|
||||
|
|
55
subsurface.c
55
subsurface.c
|
@ -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
336
surface.c
|
@ -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;
|
||||
}
|
||||
|
|
26
xdg_popup.c
26
xdg_popup.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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, ¤t_width,
|
||||
¤t_height);
|
||||
|
||||
XLXdgRoleGetCurrentGeometry (role, NULL, NULL, &geometry_width,
|
||||
&geometry_height);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue