12to11/subsurface.c
oldosfan 4d2e85d002 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.
2022-09-30 01:17:47 +00:00

1025 lines
25 KiB
C

/* Wayland compositor running on top of an X server.
Copyright (C) 2022 to various contributors.
This file is part of 12to11.
12to11 is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
12to11 is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include <string.h>
#include <stdlib.h>
#include "compositor.h"
enum
{
PendingPosition = 1,
};
enum _SurfaceActionType
{
Sentinel,
PlaceAboveOther,
PlaceBelowOther,
};
typedef enum _SurfaceActionType SurfaceActionType;
typedef struct _Subsurface Subsurface;
typedef struct _Substate Substate;
typedef struct _SurfaceAction SurfaceAction;
typedef struct _SurfaceActionClientData SurfaceActionClientData;
#define SubsurfaceFromRole(role) ((Subsurface *) (role))
struct _SurfaceAction
{
/* What this action is. */
SurfaceActionType type;
/* What subsurface this action applies to. */
Subsurface *subsurface;
/* What surface is the "other" surface. */
Surface *other;
/* Surface destroy listener. */
DestroyCallback *destroy_listener;
/* The next and last surface actions in this list. */
SurfaceAction *next, *last;
};
struct _Substate
{
/* The position of the subsurface relative to the parent. */
int x, y;
/* Various flags. */
int flags;
};
struct _Subsurface
{
/* The role object itself. */
Role role;
/* The parent surface. */
Surface *parent;
/* The number of references to this subsurface. */
int refcount;
/* Pending substate. */
Substate pending_substate;
/* Current substate. */
Substate current_substate;
/* Commit callback attached to the parent. */
CommitCallback *commit_callback;
/* Whether or not this is synchronous. */
Bool synchronous;
/* Whether or not a commit is pending. */
Bool pending_commit;
/* The last dimensions and position that were used to update this
surface's outputs. */
int output_x, output_y, output_width, output_height;
};
struct _SurfaceActionClientData
{
/* Any pending subsurface actions. */
SurfaceAction actions;
};
/* The global wl_subcompositor resource. */
struct wl_global *global_subcompositor;
static void
UnlinkSurfaceAction (SurfaceAction *subaction)
{
subaction->last->next = subaction->next;
subaction->next->last = subaction->last;
}
static void
HandleOtherSurfaceDestroyed (void *data)
{
SurfaceAction *action;
action = data;
UnlinkSurfaceAction (action);
XLFree (action);
}
static void
DestroySurfaceAction (SurfaceAction *subaction)
{
XLSurfaceCancelRunOnFree (subaction->destroy_listener);
UnlinkSurfaceAction (subaction);
XLFree (subaction);
}
static Bool
CheckSiblingRelationship (Subsurface *subsurface, Surface *other)
{
Subsurface *other_subsurface;
if (other->role_type != SubsurfaceType
/* The role might've been detached from the other surface. */
|| !other->role)
return False;
other_subsurface = SubsurfaceFromRole (other->role);
if (other_subsurface->parent != subsurface->parent)
return False;
return True;
}
static void
ParentBelow (View *parent, View *below, Surface *surface)
{
ViewInsertBefore (parent, surface->view, below);
ViewInsertBefore (parent, surface->under, surface->view);
}
static void
ParentAbove (View *parent, View *above, Surface *surface)
{
ViewInsertAfter (parent, surface->under, above);
ViewInsertAfter (parent, surface->view, surface->under);
}
static void
ParentStart (View *parent, Surface *surface)
{
ViewInsert (parent, surface->under);
ViewInsert (parent, surface->view);
}
static void
RunOneSurfaceAction (Subsurface *subsurface, SurfaceAction *subaction)
{
View *target;
if (!subsurface->role.surface || !subsurface->parent)
return;
if (subaction->type == PlaceAboveOther)
{
if (subaction->other != subsurface->parent
&& !CheckSiblingRelationship (subsurface, subaction->other))
/* The hierarchy changed in some unacceptable way between the
action being recorded and the commit of the parent.
Ignore. */
return;
/* Determine the target under which to place the view. If
subaction->other is underneath the parent, then this will
actually be subsurface->parent->under. */
target = ViewGetParent (subaction->other->view);
/* After that, unparent the views. */
ViewUnparent (subsurface->role.surface->view);
ViewUnparent (subsurface->role.surface->under);
if (subaction->other == subsurface->parent)
/* Re-insert this view at the beginning of the parent. */
ParentStart (subsurface->parent->view,
subsurface->role.surface);
else
/* Re-insert this view in front of the other surface. */
ParentAbove (target, subaction->other->view,
subsurface->role.surface);
}
else if (subaction->type == PlaceBelowOther)
{
if (subaction->other != subsurface->parent
&& !CheckSiblingRelationship (subsurface, subaction->other))
return;
target = ViewGetParent (subaction->other->view);
ViewUnparent (subsurface->role.surface->view);
ViewUnparent (subsurface->role.surface->under);
if (subaction->other != subsurface->parent)
/* Re-insert this view before the other surface. */
ParentBelow (target, subaction->other->under,
subsurface->role.surface);
else
/* Re-insert this view below the parent surface. */
ParentStart (subsurface->parent->under,
subsurface->role.surface);
}
}
static void
FreeSurfaceActions (SurfaceAction *first)
{
SurfaceAction *action, *last;
action = first->next;
while (action != first)
{
last = action;
action = action->next;
DestroySurfaceAction (last);
}
}
static void
FreeSubsurfaceData (void *data)
{
SurfaceActionClientData *client;
client = data;
FreeSurfaceActions (&client->actions);
}
static SurfaceAction *
AddSurfaceAction (Subsurface *subsurface, Surface *other,
SurfaceActionType type)
{
SurfaceAction *action;
SurfaceActionClientData *client;
action = XLMalloc (sizeof *action);
action->subsurface = subsurface;
action->type = type;
action->other = other;
action->destroy_listener
= XLSurfaceRunOnFree (other, HandleOtherSurfaceDestroyed,
action);
client = XLSurfaceGetClientData (subsurface->parent,
SubsurfaceData,
sizeof *client,
FreeSubsurfaceData);
if (!client->actions.next)
{
/* Client is not yet initialized, so initialize the sentinel
node. */
client->actions.next = &client->actions;
client->actions.last = &client->actions;
client->actions.type = Sentinel;
}
action->next = client->actions.next;
action->last = &client->actions;
client->actions.next->last = action;
client->actions.next = action;
return action;
}
static void
RunSurfaceActions (SurfaceAction *first)
{
SurfaceAction *action, *last;
action = first->last;
while (action != first)
{
last = action;
/* Run the actions backwards so they appear in the right
order. */
action = action->last;
RunOneSurfaceAction (last->subsurface, last);
DestroySurfaceAction (last);
}
}
static void
DestroySubsurface (struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
SetPosition (struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y)
{
Subsurface *subsurface;
subsurface = wl_resource_get_user_data (resource);
subsurface->pending_substate.x = x;
subsurface->pending_substate.y = y;
subsurface->pending_substate.flags |= PendingPosition;
}
static void
PlaceAbove (struct wl_client *client, struct wl_resource *resource,
struct wl_resource *surface_resource)
{
Subsurface *subsurface;
Surface *other;
subsurface = wl_resource_get_user_data (resource);
other = wl_resource_get_user_data (surface_resource);
if (other != subsurface->parent
&& !CheckSiblingRelationship (subsurface, other))
{
wl_resource_post_error (resource, WL_SUBSURFACE_ERROR_BAD_SURFACE,
"surface is not a sibling or the parent");
return;
}
AddSurfaceAction (subsurface, other, PlaceAboveOther);
}
static void
PlaceBelow (struct wl_client *client, struct wl_resource *resource,
struct wl_resource *surface_resource)
{
Subsurface *subsurface;
Surface *other;
subsurface = wl_resource_get_user_data (resource);
other = wl_resource_get_user_data (surface_resource);
if (other != subsurface->parent
|| !CheckSiblingRelationship (subsurface, other))
{
wl_resource_post_error (resource, WL_SUBSURFACE_ERROR_BAD_SURFACE,
"surface is not a sibling or the parent");
return;
}
AddSurfaceAction (subsurface, other, PlaceBelowOther);
}
static void
NoteDesyncChild (Surface *surface, Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
if (!subsurface->parent || !subsurface->parent->role
|| !subsurface->parent->role->funcs.note_desync_child)
return;
subsurface->parent->role->funcs.note_desync_child (subsurface->parent,
subsurface->parent->role);
}
static void
NoteChildSynced (Surface *surface, Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
if (!subsurface->parent || !subsurface->parent->role
|| !subsurface->parent->role->funcs.note_child_synced)
return;
subsurface->parent->role->funcs.note_child_synced (subsurface->parent,
subsurface->parent->role);
}
static void
SetSync (struct wl_client *client, struct wl_resource *resource)
{
Subsurface *subsurface;
subsurface = wl_resource_get_user_data (resource);
if (subsurface->role.surface
&& !subsurface->synchronous)
NoteChildSynced (subsurface->role.surface,
&subsurface->role);
subsurface->synchronous = True;
}
static void
SetDesync (struct wl_client *client, struct wl_resource *resource)
{
Subsurface *subsurface;
subsurface = wl_resource_get_user_data (resource);
if (subsurface->role.surface
&& subsurface->synchronous)
NoteDesyncChild (subsurface->role.surface,
&subsurface->role);
subsurface->synchronous = False;
if (subsurface->pending_commit
&& subsurface->role.surface)
XLCommitSurface (subsurface->role.surface, False);
subsurface->pending_commit = False;
}
static const struct wl_subsurface_interface wl_subsurface_impl =
{
.destroy = DestroySubsurface,
.set_position = SetPosition,
.place_above = PlaceAbove,
.place_below = PlaceBelow,
.set_sync = SetSync,
.set_desync = SetDesync,
};
static void
DestroyBacking (Subsurface *subsurface)
{
if (--subsurface->refcount)
return;
XLFree (subsurface);
}
static Bool
EarlyCommit (Surface *surface, Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
/* If the role is synchronous, don't commit until the parent
commits. */
if (subsurface->synchronous)
{
subsurface->pending_commit = True;
return False;
}
return True;
}
static void
MaybeUpdateOutputs (Subsurface *subsurface)
{
int x, y, width, height, base_x, base_y;
if (subsurface->role.surface->output_x == INT_MIN
|| subsurface->role.surface->output_y == INT_MIN)
/* Valid base coordinates are not yet available. */
return;
if (!subsurface->parent)
/* A valid scale factor is not available. */
return;
/* Compute the positions relative to the parent. */
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;
base_y = subsurface->role.surface->output_y;
/* Compute the absolute width and height of the surface
contents. */
width = ViewWidth (subsurface->role.surface->view);
height = ViewHeight (subsurface->role.surface->view);
/* If nothing really changed, return. */
if (x == subsurface->output_x
&& y == subsurface->output_y
&& width == subsurface->output_width
&& height == subsurface->output_height)
return;
/* Otherwise, recompute the outputs this subsurface overlaps and
record those values. */
subsurface->output_x = x;
subsurface->output_y = y;
subsurface->output_width = width;
subsurface->output_height = height;
/* Recompute overlaps. */
XLUpdateSurfaceOutputs (subsurface->role.surface,
x + base_x, y + base_y,
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)
{
Subsurface *subsurface;
subsurface = data;
/* The surface might've been destroyed already. */
if (!subsurface->role.surface)
return;
/* Apply pending state. */
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;
/* And move the views. */
MoveFractional (subsurface);
}
/* And any cached surface state too. */
if (subsurface->pending_commit)
{
XLCommitSurface (subsurface->role.surface, False);
/* If the size changed, update the outputs this surface is in
the scanout area of. */
MaybeUpdateOutputs (subsurface);
}
subsurface->pending_commit = False;
subsurface->pending_substate.flags = 0;
}
static Bool
Subframe (Surface *surface, Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
if (!subsurface->parent || !subsurface->parent->role
|| !subsurface->parent->role->funcs.subframe)
return True;
return subsurface->parent->role->funcs.subframe (subsurface->parent,
subsurface->parent->role);
}
static void
EndSubframe (Surface *surface, Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
if (!subsurface->parent || !subsurface->parent->role
|| !subsurface->parent->role->funcs.end_subframe)
return;
subsurface->parent->role->funcs.end_subframe (subsurface->parent,
subsurface->parent->role);
}
static Window
GetWindow (Surface *surface, Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
if (!subsurface->parent || !subsurface->parent->role
|| !subsurface->parent->role->funcs.get_window)
return None;
return
subsurface->parent->role->funcs.get_window (subsurface->parent,
subsurface->parent->role);
}
static void
Commit (Surface *surface, Role *role)
{
Subcompositor *subcompositor;
Subsurface *subsurface;
subcompositor = ViewGetSubcompositor (surface->view);
subsurface = SubsurfaceFromRole (role);
if (!subcompositor)
return;
/* If no buffer is attached, unmap the views. */
if (!surface->current_state.buffer)
{
ViewUnmap (surface->under);
ViewUnmap (surface->view);
}
else
/* Once a buffer is attached to the view, it is automatically
mapped. */
ViewMap (surface->under);
if (!subsurface->synchronous)
{
/* If the surface is asynchronous, draw this subframe now.
Otherwise it is synchronous, so we should wait for the
toplevel to end the frame. */
if (Subframe (surface, role))
{
SubcompositorUpdate (subcompositor);
EndSubframe (surface, role);
}
/* If the size changed, update the outputs this surface is in
the scanout area of. */
MaybeUpdateOutputs (subsurface);
}
}
static Bool
Setup (Surface *surface, Role *role)
{
Subsurface *subsurface;
View *parent_view;
surface->role_type = SubsurfaceType;
subsurface = SubsurfaceFromRole (role);
subsurface->refcount++;
subsurface->output_x = INT_MIN;
subsurface->output_y = INT_MIN;
role->surface = surface;
parent_view = subsurface->parent->view;
/* Set the subcompositor here. If the role providing the
subcompositor hasn't been attached to the parent, then when it is
it will call ViewSetSubcompositor on the parent's view. */
ViewSetSubcompositor (surface->under,
ViewGetSubcompositor (parent_view));
ViewInsert (parent_view, surface->under);
ViewSetSubcompositor (surface->view,
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,
surface);
return True;
}
static void
Rescale (Surface *surface, Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
/* The scale factor changed; move the subsurface to the new correct
position. */
MoveFractional (subsurface);
}
static void
ParentRescale (Surface *surface, Role *role)
{
/* This is called when the scale factor of the parent changes. */
Rescale (surface, role);
}
static void
Teardown (Surface *surface, Role *role)
{
Subsurface *subsurface;
SurfaceActionClientData *client;
SurfaceAction *action;
Subcompositor *subcompositor;
subsurface = SubsurfaceFromRole (role);
/* If this subsurface is desynchronous, tell the toplevel parent
that it is now gone. */
if (!subsurface->synchronous)
NoteDesyncChild (role->surface, role);
role->surface = NULL;
if (subsurface->parent)
{
subcompositor = ViewGetSubcompositor (surface->view);
ViewUnparent (surface->view);
ViewSetSubcompositor (surface->view, NULL);
ViewUnparent (surface->under);
ViewSetSubcompositor (surface->under, NULL);
client = subsurface->parent->client_data[SubsurfaceData];
if (client)
{
/* Free all subsurface actions involving this
subsurface. */
action = client->actions.next;
while (action != &client->actions)
{
if (action->subsurface == subsurface)
DestroySurfaceAction (action);
}
}
subsurface->parent->subsurfaces
= XLListRemove (subsurface->parent->subsurfaces, surface);
XLSurfaceCancelCommitCallback (subsurface->commit_callback);
/* According to the spec, this removal should take effect
immediately. */
if (subcompositor
&& Subframe (surface, role))
{
SubcompositorUpdate (subcompositor);
EndSubframe (surface, role);
}
}
DestroyBacking (subsurface);
}
static void
ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
if (!subsurface->parent || !subsurface->parent->role)
{
XLReleaseBuffer (buffer);
return;
}
subsurface->parent->role->funcs.release_buffer (subsurface->parent,
subsurface->parent->role,
buffer);
}
static void
HandleSubsurfaceResourceDestroy (struct wl_resource *resource)
{
Subsurface *subsurface;
subsurface = wl_resource_get_user_data (resource);
DestroyBacking (subsurface);
}
static void
GetSubsurface (struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *surface_resource,
struct wl_resource *parent_resource)
{
Surface *surface, *parent;
Subsurface *subsurface;
surface = wl_resource_get_user_data (surface_resource);
parent = wl_resource_get_user_data (parent_resource);
/* If the surface already has a role, don't attach this subsurface.
Likewise if the surface previously held some other role. */
if (surface->role || (surface->role_type != AnythingType
&& surface->role_type != SubsurfaceType))
{
wl_resource_post_error (resource, WL_DISPLAY_ERROR_IMPLEMENTATION,
"trying to attach subsurface to surface with role");
return;
}
subsurface = XLSafeMalloc (sizeof *subsurface);
if (!subsurface)
{
wl_resource_post_no_memory (resource);
return;
}
memset (subsurface, 0, sizeof *subsurface);
subsurface->role.resource
= wl_resource_create (client, &wl_subsurface_interface,
wl_resource_get_version (resource),
id);
if (!subsurface->role.resource)
{
XLFree (subsurface);
wl_resource_post_no_memory (resource);
return;
}
wl_resource_set_implementation (subsurface->role.resource, &wl_subsurface_impl,
subsurface, HandleSubsurfaceResourceDestroy);
/* Now the wl_resource holds a reference to the subsurface. */
subsurface->refcount++;
subsurface->role.funcs.commit = Commit;
subsurface->role.funcs.teardown = Teardown;
subsurface->role.funcs.setup = Setup;
subsurface->role.funcs.release_buffer = ReleaseBuffer;
subsurface->role.funcs.subframe = Subframe;
subsurface->role.funcs.end_subframe = EndSubframe;
subsurface->role.funcs.early_commit = EarlyCommit;
subsurface->role.funcs.get_window = GetWindow;
subsurface->role.funcs.rescale = Rescale;
subsurface->role.funcs.parent_rescale = ParentRescale;
subsurface->role.funcs.note_child_synced = NoteChildSynced;
subsurface->role.funcs.note_desync_child = NoteDesyncChild;
subsurface->parent = parent;
subsurface->commit_callback
= XLSurfaceRunAtCommit (parent, AfterParentCommit, subsurface);
subsurface->synchronous = True;
if (!XLSurfaceAttachRole (surface, &subsurface->role))
abort ();
}
static void
DestroySubcompositor (struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static const struct wl_subcompositor_interface wl_subcompositor_impl =
{
.destroy = DestroySubcompositor,
.get_subsurface = GetSubsurface,
};
static void
HandleBind (struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct wl_resource *resource;
resource = wl_resource_create (client, &wl_subcompositor_interface,
version, id);
if (!resource)
{
wl_client_post_no_memory (client);
return;
}
wl_resource_set_implementation (resource, &wl_subcompositor_impl,
NULL, NULL);
}
void
XLInitSubsurfaces (void)
{
global_subcompositor
= wl_global_create (compositor.wl_display,
&wl_subcompositor_interface,
1, NULL, HandleBind);
}
void
XLSubsurfaceParentDestroyed (Role *role)
{
Subsurface *subsurface;
subsurface = SubsurfaceFromRole (role);
subsurface->parent = NULL;
/* The callback is freed with the parent. */
subsurface->commit_callback = NULL;
if (subsurface->role.surface)
{
ViewUnparent (subsurface->role.surface->view);
ViewUnparent (subsurface->role.surface->under);
}
}
void
XLSubsurfaceHandleParentCommit (Surface *parent)
{
SurfaceActionClientData *client;
client = parent->client_data[SubsurfaceData];
if (client)
RunSurfaceActions (&client->actions);
}
void
XLUpdateOutputsForChildren (Surface *parent, int base_x, int base_y)
{
XLList *item;
Subsurface *subsurface;
Surface *child;
int output_x, output_y, output_width, output_height;
for (item = parent->subsurfaces; item; item = item->next)
{
child = item->data;
subsurface = SubsurfaceFromRole (child->role);
output_x = (subsurface->current_substate.x
* parent->factor);
output_y = (subsurface->current_substate.y
* parent->factor);
output_width = ViewWidth (child->view);
output_height = ViewHeight (child->view);
XLUpdateSurfaceOutputs (child,
base_x + output_x,
base_y + output_y,
output_width,
output_height);
/* Record those values in the child. */
subsurface->output_x = output_x;
subsurface->output_y = output_y;
subsurface->output_width = output_width;
subsurface->output_height = output_height;
}
}
void
XLUpdateDesynchronousChildren (Surface *parent, int *n_children)
{
XLList *item;
Subsurface *subsurface;
Surface *child;
for (item = parent->subsurfaces; item; item = item->next)
{
child = item->data;
subsurface = SubsurfaceFromRole (child->role);
if (!subsurface->synchronous)
/* The subsurface is desynchronous, so add it to the number of
desynchronous children. */
*n_children += 1;
/* Update these numbers recursively as well. */
XLUpdateDesynchronousChildren (child, n_children);
}
}