forked from 12to11/12to11
2433 lines
60 KiB
C
2433 lines
60 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/>. */
|
||
|
||
#ifndef TEST
|
||
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <alloca.h>
|
||
|
||
#include "compositor.h"
|
||
|
||
#define TEST_STATIC
|
||
#else
|
||
|
||
typedef int Bool;
|
||
|
||
#define True 1
|
||
#define False 0
|
||
|
||
typedef struct _View View;
|
||
typedef struct _List List;
|
||
typedef struct _Subcompositor Subcompositor;
|
||
|
||
#define TEST_STATIC static
|
||
|
||
#endif
|
||
|
||
/* This module implements a "subcompositor" that composites together
|
||
the contents of hierarchies of "views", each of which have attached
|
||
ExtBuffers and other assorted state.
|
||
|
||
Each view has a parent (which can be the subcompositor itself), and
|
||
a list of children, which is sorted according to Z order. In
|
||
addition to the list of children of the subcompositor itself, every
|
||
view in the subcompositor is stored in a single doubly-linked list,
|
||
ordered implicitly according to the in which every inferior (direct
|
||
or indirect children of the subcompositor) will be composited.
|
||
This list is updated whenever a new view is inserted or the Z order
|
||
or parent of one of the views change.
|
||
|
||
For example, assume the subcompositor has the following children:
|
||
|
||
[A] [B] [C]
|
||
| | |
|
||
[D] [E] [F] [G] [H] [I]
|
||
|
||
Then, the contents of the list will be:
|
||
|
||
[A], [D], [E], [B], [F], [G], [C], [H], [I]
|
||
|
||
To aid in updating the linked list, each view maintains a pointer
|
||
to the link in the list containing the view itself, and the link
|
||
containing the last inferior (direct or indirect children of the
|
||
view) of the view. So, in the above example, the view "A" will
|
||
also point to:
|
||
|
||
+ = link pointer of "A"
|
||
+ = last inferior pointer of "A"
|
||
[A], [D], [E], [B], [F], [G], [C], [H], [I]
|
||
|
||
To add view to another view, the view is first appended to the end
|
||
of the other view's list of children, and the links between its
|
||
link and its last inferior link are linked after its last inferior
|
||
link. Finally, the other view and each of its parents is iterated
|
||
through, and the last inferior pointer is updated to the last
|
||
inferior link of the view that was inserted if it is equal to the
|
||
other view's original last inferior pointer.
|
||
|
||
If a view named "J" with no children were to be inserted at the end
|
||
of "A", then "J" would first be added to the end of "A"'s list of
|
||
children, creating such a hierarchy:
|
||
|
||
[A]
|
||
|
|
||
[D] [E] [J]
|
||
|
||
Then, "J"'s link and inferior pointers would be inserted after "E"
|
||
(where + represents the current location of "A"'s last inferior
|
||
pointer), resulting in the subcompositor's list of inferiors
|
||
looking like this:
|
||
|
||
+ * = link pointer of "J"
|
||
+ * = last inferior pointer of "J"
|
||
[A], [D], [E], [J], [B], [F], [G], [C], [H], [I]
|
||
|
||
Finally, the inferior pointer of each of "E"'s parents that
|
||
previously pointed to "E" is updated, like so:
|
||
|
||
+ *
|
||
+*
|
||
[A], [D], [E], [J], [B], [F], [G], [C], [H], [I]
|
||
|
||
A similar procedure applies to adding a view to the subcompositor
|
||
itself.
|
||
|
||
Unparenting a view (thereby removing it from the view hierarchy) is
|
||
is done by unlinking the implicitly-formed list between the view's
|
||
link pointer and the view's last inferior pointer from its
|
||
surroundings, and removing it from its parent's list of children.
|
||
This in turn creates a separate, implicitly-formed list, that
|
||
allows for view hierarchy operations to be performed on a detached
|
||
view. Unlinking "A" from the above hierarchy would produce two
|
||
separate lists:
|
||
|
||
+ *
|
||
+*
|
||
[A], [D], [E], [J] = the implicit sub-list of "A"
|
||
[B], [F], [G], [C], [H], [I] = the
|
||
subcompositor inferior list
|
||
|
||
Finally, the inferior pointer of all parents pointing to the
|
||
unparented view's inferior pointer are updated to the
|
||
next-bottom-most sibling view's inferior pointer. This cannot be
|
||
demonstrated using the chart above, since "A" is a toplevel.
|
||
|
||
Unlike the Wayland protocol itself, this does not support placing
|
||
children of a view before the view itself. That is implemented
|
||
manually by moving such children to a separate sibling of the
|
||
parent that is always stacked below that view. */
|
||
|
||
enum
|
||
{
|
||
/* This means that the view hierarchy has changed, and all
|
||
subcompositing optimisations should be skipped. */
|
||
SubcompositorIsGarbaged = 1,
|
||
/* This means that the opaque region of one of the views
|
||
changed. */
|
||
SubcompositorIsOpaqueDirty = (1 << 2),
|
||
/* This means that the input region of one of the views
|
||
changed. */
|
||
SubcompositorIsInputDirty = (1 << 3),
|
||
/* This means that there is at least one unmapped view in this
|
||
subcompositor. */
|
||
SubcompositorIsPartiallyMapped = (1 << 4),
|
||
/* This means that the subcompositor is frozen and updates should
|
||
do nothing. */
|
||
SubcompositorIsFrozen = (1 << 5),
|
||
};
|
||
|
||
#define IsGarbaged(subcompositor) \
|
||
((subcompositor)->state & SubcompositorIsGarbaged)
|
||
#define SetGarbaged(subcompositor) \
|
||
((subcompositor)->state |= SubcompositorIsGarbaged)
|
||
|
||
#define SetOpaqueDirty(subcompositor) \
|
||
((subcompositor)->state |= SubcompositorIsOpaqueDirty)
|
||
#define IsOpaqueDirty(subcompositor) \
|
||
((subcompositor)->state & SubcompositorIsOpaqueDirty)
|
||
|
||
#define SetInputDirty(subcompositor) \
|
||
((subcompositor)->state |= SubcompositorIsInputDirty)
|
||
#define IsInputDirty(subcompositor) \
|
||
((subcompositor)->state & SubcompositorIsInputDirty)
|
||
|
||
#define SetPartiallyMapped(subcompositor) \
|
||
((subcompositor)->state |= SubcompositorIsPartiallyMapped)
|
||
#define IsPartiallyMapped(subcompositor) \
|
||
((subcompositor)->state & SubcompositorIsPartiallyMapped)
|
||
|
||
#define SetFrozen(subcompositor) \
|
||
((subcompositor)->state |= SubcompositorIsFrozen)
|
||
#define IsFrozen(subcompositor) \
|
||
((subcompositor)->state & SubcompositorIsFrozen)
|
||
|
||
#ifndef TEST
|
||
|
||
enum
|
||
{
|
||
/* This means that the view and all its inferiors should be
|
||
skipped in bounds computation, input tracking, et cetera. */
|
||
ViewIsUnmapped = 1,
|
||
};
|
||
|
||
#define IsViewUnmapped(view) \
|
||
((view)->flags & ViewIsUnmapped)
|
||
#define SetUnmapped(view) \
|
||
((view)->flags |= ViewIsUnmapped)
|
||
#define ClearUnmapped(view) \
|
||
((view)->flags &= ~ViewIsUnmapped)
|
||
|
||
#endif
|
||
|
||
struct _List
|
||
{
|
||
/* Pointer to the next element of this list.
|
||
This list itself if this is the sentinel link. */
|
||
List *next;
|
||
|
||
/* Pointer to the last element of this list.
|
||
This list itself if this is the sentinel link. */
|
||
List *last;
|
||
|
||
/* The view of this list. */
|
||
View *view;
|
||
};
|
||
|
||
struct _View
|
||
{
|
||
/* Subcompositor this view belongs to. NULL at first; callers are
|
||
supposed to call ViewSetSubcompositor before inserting a view
|
||
into a compositor. */
|
||
Subcompositor *subcompositor;
|
||
|
||
#ifndef TEST
|
||
/* Picture this subcompositor draws to. */
|
||
Picture picture;
|
||
#endif
|
||
|
||
/* Pointer to the parent view. NULL if the parent is the
|
||
subcompositor itself. */
|
||
View *parent;
|
||
|
||
/* Pointer to the link containing the view itself. */
|
||
List *link;
|
||
|
||
/* Pointer to another such link used in the view hierarchy. */
|
||
List *self;
|
||
|
||
/* Pointer to the link containing the view's last inferior. */
|
||
List *inferior;
|
||
|
||
/* List of children. */
|
||
List *children;
|
||
|
||
/* The end of that list. */
|
||
List *children_last;
|
||
|
||
/* Buffer data. */
|
||
|
||
#ifndef TEST
|
||
/* The buffer associated with this view, or None if nothing is
|
||
attached. */
|
||
ExtBuffer *buffer;
|
||
|
||
/* The damaged and opaque regions. */
|
||
pixman_region32_t damage, opaque;
|
||
|
||
/* The input region. */
|
||
pixman_region32_t input;
|
||
|
||
/* The position of this view relative to its parent. */
|
||
int x, y;
|
||
|
||
/* The absolute position of this view relative to the subcompositor
|
||
(or topmost parent if the view hierarchy is detached). */
|
||
int abs_x, abs_y;
|
||
|
||
/* Some data associated with this view. Can be a surface or
|
||
something else. */
|
||
void *data;
|
||
|
||
/* The scale of this view. */
|
||
int scale;
|
||
|
||
/* Flags; whether or not this view is unmapped, etc. */
|
||
int flags;
|
||
#else
|
||
/* Label used during tests. */
|
||
const char *label;
|
||
#endif
|
||
};
|
||
|
||
struct _Subcompositor
|
||
{
|
||
/* Various flags describing the state of this subcompositor. */
|
||
int state;
|
||
|
||
/* List of all inferiors in compositing order. */
|
||
List *inferiors, *last;
|
||
|
||
/* Toplevel children of this subcompositor. */
|
||
List *children, *last_children;
|
||
|
||
#ifndef TEST
|
||
/* The picture that is rendered to. */
|
||
Picture target;
|
||
|
||
/* Function called when the opaque region changes. */
|
||
void (*opaque_change) (Subcompositor *, void *,
|
||
pixman_region32_t *);
|
||
|
||
/* Function called when the input region changes. */
|
||
void (*input_change) (Subcompositor *, void *,
|
||
pixman_region32_t *);
|
||
|
||
/* Function called with the bounds before each update. */
|
||
void (*note_bounds) (void *, int, int, int, int);
|
||
|
||
/* Data for those three functions. */
|
||
void *opaque_change_data, *input_change_data, *note_bounds_data;
|
||
|
||
/* The minimum origin of any surface in this subcompositor. Used to
|
||
compute the actual size of the subcompositor. */
|
||
int min_x, min_y;
|
||
|
||
/* The maximum position of any surface in this subcompositor. Used
|
||
to compute the actual size of the subcompositor. */
|
||
int max_x, max_y;
|
||
|
||
/* An additional offset to apply when drawing to the target. */
|
||
int tx, ty;
|
||
#endif
|
||
};
|
||
|
||
#ifndef TEST
|
||
|
||
enum
|
||
{
|
||
DoMinX = 1,
|
||
DoMinY = (1 << 1),
|
||
DoMaxX = (1 << 2),
|
||
DoMaxY = (1 << 3),
|
||
DoAll = 0xf,
|
||
};
|
||
|
||
/* The identity transform. */
|
||
|
||
static XTransform identity_transform;
|
||
|
||
#endif
|
||
|
||
|
||
/* Circular doubly linked list of views. These lists work unusually:
|
||
for example, only some lists have a "sentinel" node at the
|
||
beginning with the value NULL. This is so that sub-lists can be
|
||
extracted from them without consing. */
|
||
|
||
static List *
|
||
ListInit (View *value)
|
||
{
|
||
List *link;
|
||
|
||
link = XLCalloc (1, sizeof *link);
|
||
link->next = link;
|
||
link->last = link;
|
||
link->view = value;
|
||
|
||
return link;
|
||
}
|
||
|
||
static void
|
||
ListRelinkAfter (List *start, List *end, List *dest)
|
||
{
|
||
end->next = dest->next;
|
||
start->last = dest;
|
||
|
||
dest->next->last = end;
|
||
dest->next = start;
|
||
}
|
||
|
||
static void
|
||
ListInsertAfter (List *after, List *item)
|
||
{
|
||
ListRelinkAfter (item, item, after);
|
||
}
|
||
|
||
static void
|
||
ListInsertBefore (List *before, List *item)
|
||
{
|
||
ListRelinkAfter (item, item, before->last);
|
||
}
|
||
|
||
static void
|
||
ListRelinkBefore (List *start, List *end, List *dest)
|
||
{
|
||
ListRelinkAfter (start, end, dest->last);
|
||
}
|
||
|
||
/* Unlink the list between START and END from their surroundings.
|
||
Then, turn START and END into a proper list. This requires that
|
||
START is not the sentinel node. */
|
||
|
||
static void
|
||
ListUnlink (List *start, List *end)
|
||
{
|
||
/* First, make the list skip past END. */
|
||
start->last->next = end->next;
|
||
end->next->last = start->last;
|
||
|
||
/* Then, unlink the list. */
|
||
start->last = end;
|
||
end->next = start;
|
||
}
|
||
|
||
TEST_STATIC Subcompositor *
|
||
MakeSubcompositor (void)
|
||
{
|
||
Subcompositor *subcompositor;
|
||
|
||
subcompositor = XLCalloc (1, sizeof *subcompositor);
|
||
subcompositor->inferiors = ListInit (NULL);
|
||
subcompositor->children = ListInit (NULL);
|
||
|
||
subcompositor->last = subcompositor->inferiors;
|
||
subcompositor->last_children = subcompositor->children;
|
||
|
||
return subcompositor;
|
||
}
|
||
|
||
TEST_STATIC View *
|
||
MakeView (void)
|
||
{
|
||
View *view;
|
||
|
||
view = XLCalloc (1, sizeof *view);
|
||
view->subcompositor = NULL;
|
||
view->parent = NULL;
|
||
|
||
/* Note that view->link is not supposed to have a sentinel; it can
|
||
only be part of a larger list. */
|
||
view->link = ListInit (view);
|
||
view->inferior = view->link;
|
||
|
||
/* Likewise for view->self. */
|
||
view->self = ListInit (view);
|
||
|
||
/* But view->children is a complete list by itself. */
|
||
view->children = ListInit (NULL);
|
||
view->children_last = view->children;
|
||
|
||
#ifndef TEST
|
||
view->buffer = NULL;
|
||
|
||
pixman_region32_init (&view->damage);
|
||
pixman_region32_init (&view->opaque);
|
||
pixman_region32_init (&view->input);
|
||
#endif
|
||
|
||
return view;
|
||
}
|
||
|
||
#ifndef TEST
|
||
|
||
static int
|
||
ViewMaxX (View *view)
|
||
{
|
||
return view->abs_x + ViewWidth (view) - 1;
|
||
}
|
||
|
||
static int
|
||
ViewMaxY (View *view)
|
||
{
|
||
return view->abs_y + ViewHeight (view) - 1;
|
||
}
|
||
|
||
static Bool
|
||
ViewIsMapped (View *view)
|
||
{
|
||
if (view->subcompositor
|
||
&& !IsPartiallyMapped (view->subcompositor))
|
||
return True;
|
||
|
||
if (IsViewUnmapped (view))
|
||
return False;
|
||
|
||
if (view->parent)
|
||
return ViewIsMapped (view->parent);
|
||
|
||
return True;
|
||
}
|
||
|
||
static void
|
||
SubcompositorUpdateBounds (Subcompositor *subcompositor, int doflags)
|
||
{
|
||
List *list;
|
||
int min_x, min_y, max_x, max_y;
|
||
|
||
/* Updates were optimized out. */
|
||
if (!doflags)
|
||
return;
|
||
|
||
list = subcompositor->inferiors->next;
|
||
min_x = max_x = min_y = max_y = 0;
|
||
|
||
while (list != subcompositor->inferiors)
|
||
{
|
||
if (list->view)
|
||
{
|
||
/* If the view is unmapped, skip past its children. */
|
||
if (IsViewUnmapped (list->view))
|
||
{
|
||
list = list->view->inferior;
|
||
goto next;
|
||
}
|
||
|
||
if ((doflags & DoMinX) && min_x > list->view->abs_x)
|
||
min_x = list->view->abs_x;
|
||
|
||
if ((doflags & DoMinY) && min_x > list->view->abs_y)
|
||
min_y = list->view->abs_y;
|
||
|
||
if ((doflags & DoMaxX) && max_x < ViewMaxX (list->view))
|
||
max_x = ViewMaxX (list->view);
|
||
|
||
if ((doflags & DoMaxY) && max_y < ViewMaxY (list->view))
|
||
max_y = ViewMaxY (list->view);
|
||
}
|
||
|
||
next:
|
||
list = list->next;
|
||
}
|
||
|
||
if (doflags & DoMinX)
|
||
subcompositor->min_x = min_x;
|
||
|
||
if (doflags & DoMinY)
|
||
subcompositor->min_y = min_y;
|
||
|
||
if (doflags & DoMaxX)
|
||
subcompositor->max_x = max_x;
|
||
|
||
if (doflags & DoMaxY)
|
||
subcompositor->max_y = max_y;
|
||
|
||
SetGarbaged (subcompositor);
|
||
}
|
||
|
||
static void
|
||
SubcompositorUpdateBoundsForInsert (Subcompositor *subcompositor,
|
||
View *view)
|
||
{
|
||
XLAssert (view->subcompositor == subcompositor);
|
||
|
||
if (!ViewIsMapped (view))
|
||
/* If the view is unmapped, do nothing. */
|
||
return;
|
||
|
||
/* Inserting a view cannot shrink the subcompositor. */
|
||
|
||
if (view->abs_x < subcompositor->min_x)
|
||
subcompositor->min_x = view->abs_x;
|
||
|
||
if (view->abs_x < view->subcompositor->min_y)
|
||
subcompositor->min_y = view->abs_y;
|
||
|
||
if (view->subcompositor->max_x < ViewMaxX (view))
|
||
subcompositor->max_x = ViewMaxX (view);
|
||
|
||
if (view->subcompositor->max_y < ViewMaxY (view))
|
||
subcompositor->max_y = ViewMaxY (view);
|
||
}
|
||
|
||
#endif
|
||
|
||
#ifndef TEST
|
||
|
||
void
|
||
SubcompositorSetTarget (Subcompositor *compositor, Picture picture)
|
||
{
|
||
compositor->target = picture;
|
||
|
||
/* We don't know if the new picture has the previous state left
|
||
over. */
|
||
SetGarbaged (compositor);
|
||
}
|
||
|
||
#endif
|
||
|
||
TEST_STATIC void
|
||
SubcompositorInsert (Subcompositor *compositor, View *view)
|
||
{
|
||
/* Link view into the list of children. */
|
||
ListInsertBefore (compositor->last_children, view->self);
|
||
|
||
/* Make view's inferiors part of the compositor. */
|
||
ListRelinkBefore (view->link, view->inferior,
|
||
compositor->last);
|
||
|
||
/* Now that the view hierarchy has been changed, garbage the
|
||
subcompositor. */
|
||
SetGarbaged (compositor);
|
||
|
||
#ifndef TEST
|
||
/* And update bounds. */
|
||
SubcompositorUpdateBoundsForInsert (compositor, view);
|
||
#endif
|
||
}
|
||
|
||
TEST_STATIC void
|
||
SubcompositorInsertBefore (Subcompositor *compositor, View *view,
|
||
View *sibling)
|
||
{
|
||
/* Link view into the list of children, before the given
|
||
sibling. */
|
||
ListInsertBefore (sibling->self, view->self);
|
||
|
||
/* Make view's inferiors part of the compositor. */
|
||
ListRelinkBefore (view->link, view->inferior, sibling->link);
|
||
|
||
/* Now that the view hierarchy has been changed, garbage the
|
||
subcompositor. */
|
||
SetGarbaged (compositor);
|
||
|
||
#ifndef TEST
|
||
/* And update bounds. */
|
||
SubcompositorUpdateBoundsForInsert (compositor, view);
|
||
#endif
|
||
}
|
||
|
||
TEST_STATIC void
|
||
SubcompositorInsertAfter (Subcompositor *compositor, View *view,
|
||
View *sibling)
|
||
{
|
||
/* Link view into the list of children, after the given sibling. */
|
||
ListInsertAfter (sibling->self, view->self);
|
||
|
||
/* Make view's inferiors part of the compositor. */
|
||
ListRelinkAfter (view->link, view->inferior, sibling->inferior);
|
||
|
||
/* Now that the view hierarchy has been changed, garbage the
|
||
subcompositor. */
|
||
SetGarbaged (compositor);
|
||
|
||
#ifndef TEST
|
||
/* And update bounds. */
|
||
SubcompositorUpdateBoundsForInsert (compositor, view);
|
||
#endif
|
||
}
|
||
|
||
#ifndef TEST
|
||
|
||
static Bool
|
||
ViewVisibilityState (View *view, Bool *mapped)
|
||
{
|
||
if (IsViewUnmapped (view) && mapped)
|
||
{
|
||
*mapped = False;
|
||
|
||
/* Clear mapped, so it will never be set again. */
|
||
mapped = NULL;
|
||
}
|
||
|
||
if (view->parent)
|
||
return ViewVisibilityState (view->parent, mapped);
|
||
|
||
if (mapped)
|
||
*mapped = !IsViewUnmapped (view);
|
||
|
||
return view->link->next != view->link;
|
||
}
|
||
|
||
static void
|
||
ViewRecomputeChildren (View *view, int *doflags)
|
||
{
|
||
List *list;
|
||
View *child;
|
||
Bool attached, mapped;
|
||
|
||
list = view->children;
|
||
attached = ViewVisibilityState (view, &mapped);
|
||
|
||
do
|
||
{
|
||
list = list->next;
|
||
|
||
if (list->view)
|
||
{
|
||
child = list->view;
|
||
|
||
child->abs_x = view->abs_x + child->x;
|
||
child->abs_y = view->abs_y + child->y;
|
||
|
||
if (view->subcompositor
|
||
/* Don't operate on the subcompositor should the view be
|
||
detached. */
|
||
&& attached
|
||
/* Or if it isn't mapped, or none of its parents are
|
||
mapped. */
|
||
&& mapped)
|
||
{
|
||
if (child->abs_x < view->subcompositor->min_x)
|
||
{
|
||
view->subcompositor->min_x = child->abs_x;
|
||
|
||
if (doflags)
|
||
*doflags &= ~DoMinX;
|
||
}
|
||
|
||
if (child->abs_x < view->subcompositor->min_y)
|
||
{
|
||
view->subcompositor->min_y = child->abs_y;
|
||
|
||
if (doflags)
|
||
*doflags &= ~DoMinY;
|
||
}
|
||
|
||
if (view->subcompositor->max_x < ViewMaxX (child))
|
||
{
|
||
view->subcompositor->max_x = ViewMaxX (child);
|
||
|
||
if (doflags)
|
||
*doflags &= ~DoMaxX;
|
||
}
|
||
|
||
if (view->subcompositor->max_y < ViewMaxY (child))
|
||
{
|
||
view->subcompositor->max_y = ViewMaxY (child);
|
||
|
||
if (doflags)
|
||
*doflags &= ~DoMaxY;
|
||
}
|
||
}
|
||
|
||
ViewRecomputeChildren (child, doflags);
|
||
}
|
||
}
|
||
while (list != view->children);
|
||
}
|
||
|
||
static void
|
||
ViewUpdateBoundsForInsert (View *view)
|
||
{
|
||
if (view->subcompositor)
|
||
SubcompositorUpdateBoundsForInsert (view->subcompositor,
|
||
view);
|
||
}
|
||
|
||
|
||
#endif
|
||
|
||
TEST_STATIC void
|
||
ViewInsert (View *view, View *child)
|
||
{
|
||
View *parent;
|
||
List *prior;
|
||
|
||
/* Make child's parent view. */
|
||
child->parent = view;
|
||
|
||
/* Insert child into the hierarchy list. */
|
||
ListInsertBefore (view->children_last, child->self);
|
||
|
||
/* Insert child's inferior list. */
|
||
ListRelinkAfter (child->link, child->inferior, view->inferior);
|
||
|
||
/* Note what the previous last inferior pointer of view was. */
|
||
prior = view->inferior;
|
||
|
||
/* Update the entire view hierarchy's inferior pointers, starting
|
||
from view. */
|
||
for (parent = view; parent; parent = parent->parent)
|
||
{
|
||
/* The last inferior of this view has been changed already;
|
||
update it. */
|
||
if (parent->inferior == prior)
|
||
parent->inferior = child->inferior;
|
||
}
|
||
|
||
/* Now that the view hierarchy has been changed, garbage the
|
||
subcompositor. */
|
||
|
||
if (view->subcompositor)
|
||
SetGarbaged (view->subcompositor);
|
||
|
||
#ifndef TEST
|
||
/* Also update the absolute positions of the child. */
|
||
child->abs_x = view->abs_x + child->x;
|
||
child->abs_y = view->abs_y + child->y;
|
||
ViewRecomputeChildren (child, NULL);
|
||
|
||
/* And update bounds. */
|
||
ViewUpdateBoundsForInsert (view);
|
||
#endif
|
||
}
|
||
|
||
TEST_STATIC void
|
||
ViewInsertAfter (View *view, View *child, View *sibling)
|
||
{
|
||
View *parent;
|
||
List *prior;
|
||
|
||
/* Make child's parent view. */
|
||
child->parent = view;
|
||
|
||
/* Insert child into the hierarchy list. */
|
||
ListInsertAfter (sibling->self, child->self);
|
||
|
||
/* Insert child's inferior list. */
|
||
ListRelinkAfter (child->link, child->inferior,
|
||
sibling->inferior);
|
||
|
||
/* Change the inferior pointers if sibling->inferior was the old
|
||
one. */
|
||
|
||
if (sibling->inferior == view->inferior)
|
||
{
|
||
/* Note what the previous last inferior pointer of view was. */
|
||
prior = sibling->inferior;
|
||
|
||
/* Update the entire view hierarchy's inferior pointers, starting
|
||
from view. */
|
||
for (parent = view; parent; parent = parent->parent)
|
||
{
|
||
/* The last inferior of this view has been changed already;
|
||
update it. */
|
||
if (parent->inferior == prior)
|
||
parent->inferior = child->inferior;
|
||
}
|
||
}
|
||
|
||
/* Now that the view hierarchy has been changed, garbage the
|
||
subcompositor. */
|
||
|
||
if (view->subcompositor)
|
||
SetGarbaged (view->subcompositor);
|
||
|
||
#ifndef TEST
|
||
/* Also update the absolute positions of the child. */
|
||
child->abs_x = view->abs_x + child->x;
|
||
child->abs_y = view->abs_y + child->y;
|
||
ViewRecomputeChildren (child, NULL);
|
||
|
||
/* And update bounds. */
|
||
ViewUpdateBoundsForInsert (view);
|
||
#endif
|
||
}
|
||
|
||
TEST_STATIC void
|
||
ViewInsertBefore (View *view, View *child, View *sibling)
|
||
{
|
||
/* Make child's parent view. */
|
||
child->parent = view;
|
||
|
||
/* Insert child into the hierarchy list. */
|
||
ListInsertBefore (sibling->self, child->self);
|
||
|
||
/* Insert child's inferior list. */
|
||
ListRelinkBefore (child->link, child->inferior,
|
||
sibling->link);
|
||
|
||
/* Now that the view hierarchy has been changed, garbage the
|
||
subcompositor. */
|
||
|
||
if (view->subcompositor)
|
||
SetGarbaged (view->subcompositor);
|
||
|
||
#ifndef TEST
|
||
/* Also update the absolute positions of the child. */
|
||
child->abs_x = view->abs_x + child->x;
|
||
child->abs_y = view->abs_y + child->y;
|
||
ViewRecomputeChildren (child, NULL);
|
||
|
||
/* Update subcompositor bounds. Inserting a view cannot shrink
|
||
anything. */
|
||
ViewUpdateBoundsForInsert (view);
|
||
#endif
|
||
|
||
/* Inserting inferiors before a sibling can never bump the inferior
|
||
pointer. */
|
||
}
|
||
|
||
TEST_STATIC void
|
||
ViewInsertStart (View *view, View *child)
|
||
{
|
||
/* If view has no children, just call ViewInsert. Note that
|
||
view->children is a sentinel node whose value is NULL. */
|
||
if (view->children->next == view->children)
|
||
ViewInsert (view, child);
|
||
else
|
||
/* Otherwise, insert child before the first child. */
|
||
ViewInsertBefore (view, child,
|
||
view->children->next->view);
|
||
}
|
||
|
||
TEST_STATIC void
|
||
ViewUnparent (View *child)
|
||
{
|
||
View *parent;
|
||
|
||
/* Parent is either the subcompositor or another view. */
|
||
ListUnlink (child->self, child->self);
|
||
|
||
if (child->parent)
|
||
{
|
||
/* Now update the inferior pointer of each parent currently
|
||
pointing to child->inferior to the inferior of its leftmost
|
||
sibling, or its parent itself. */
|
||
|
||
for (parent = child->parent; parent; parent = parent->parent)
|
||
{
|
||
if (parent->inferior == child->inferior)
|
||
/* If this is the bottom-most child, then
|
||
child->link->last will be the parent itself. */
|
||
parent->inferior = child->link->last;
|
||
}
|
||
|
||
/* And reset the pointer to the parent. */
|
||
child->parent = NULL;
|
||
}
|
||
|
||
/* Unlink the sub-list between the link pointer and the last
|
||
inferior pointer from that of the parent. */
|
||
ListUnlink (child->link, child->inferior);
|
||
|
||
/* Reset the absolute positions of child, and recompute that of its
|
||
children. This is done after unlinking, because
|
||
ViewRecomputeChildren will otherwise try to operate on the
|
||
subcompositor. */
|
||
#ifndef TEST
|
||
child->abs_x = child->x;
|
||
child->abs_y = child->y;
|
||
|
||
ViewRecomputeChildren (child, NULL);
|
||
#endif
|
||
|
||
/* Now that the view hierarchy has been changed, garbage the
|
||
subcompositor. TODO: an optimization for removing views would be
|
||
to damage each intersecting view before child->link instead, if
|
||
view bounds did not change. */
|
||
if (child->subcompositor)
|
||
{
|
||
#ifndef TEST
|
||
/* Update the bounds of the subcompositor. */
|
||
SubcompositorUpdateBounds (child->subcompositor, DoAll);
|
||
#endif
|
||
|
||
/* Then, garbage the subcompositor. */
|
||
SetGarbaged (child->subcompositor);
|
||
}
|
||
}
|
||
|
||
TEST_STATIC void
|
||
ViewSetSubcompositor (View *view, Subcompositor *subcompositor)
|
||
{
|
||
List *list;
|
||
|
||
list = view->link;
|
||
|
||
/* Attach the subcompositor recursively for all of view's
|
||
inferiors. */
|
||
|
||
do
|
||
{
|
||
if (list->view)
|
||
list->view->subcompositor = subcompositor;
|
||
|
||
list = list->next;
|
||
}
|
||
while (list != view->link);
|
||
}
|
||
|
||
#ifdef TEST
|
||
|
||
/* The depth of the current view being printed. */
|
||
|
||
static int print_level;
|
||
|
||
static void
|
||
PrintView (View *view)
|
||
{
|
||
List *list;
|
||
|
||
printf ("%*c%s\n", print_level * 2, ' ',
|
||
view->label);
|
||
|
||
print_level++;
|
||
list = view->children;
|
||
do
|
||
{
|
||
if (list->view)
|
||
PrintView (list->view);
|
||
list = list->next;
|
||
}
|
||
while (list != view->children);
|
||
print_level--;
|
||
}
|
||
|
||
static void
|
||
PrintSubcompositor (Subcompositor *compositor)
|
||
{
|
||
List *list;
|
||
|
||
list = compositor->children;
|
||
do
|
||
{
|
||
if (list->view)
|
||
PrintView (list->view);
|
||
list = list->next;
|
||
}
|
||
while (list != compositor->children);
|
||
|
||
list = compositor->inferiors;
|
||
do
|
||
{
|
||
if (list->view)
|
||
printf ("[%s], ", list->view->label);
|
||
list = list->next;
|
||
fflush (stdout);
|
||
}
|
||
while (list != compositor->inferiors);
|
||
|
||
fflush (stdout);
|
||
printf ("\n");
|
||
fflush (stdout);
|
||
|
||
for (list = compositor->last->last;
|
||
list != compositor->last; list = list->last)
|
||
{
|
||
if (list->view)
|
||
printf ("(%s), ", list->view->label);
|
||
fflush (stdout);
|
||
}
|
||
printf ("\n");
|
||
fflush (stdout);
|
||
}
|
||
|
||
static View *
|
||
TestView (Subcompositor *compositor, const char *label)
|
||
{
|
||
View *view;
|
||
|
||
view = MakeView ();
|
||
view->label = label;
|
||
|
||
ViewSetSubcompositor (view, compositor);
|
||
|
||
return view;
|
||
}
|
||
|
||
static void
|
||
TestSubcompositor (void)
|
||
{
|
||
Subcompositor *compositor;
|
||
View *a, *b, *c, *d, *e, *f, *g, *h, *i, *j;
|
||
View *k, *l, *m, *n, *o, *p;
|
||
|
||
compositor = MakeSubcompositor ();
|
||
a = TestView (compositor, "A");
|
||
b = TestView (compositor, "B");
|
||
c = TestView (compositor, "C");
|
||
d = TestView (compositor, "D");
|
||
e = TestView (compositor, "E");
|
||
f = TestView (compositor, "F");
|
||
g = TestView (compositor, "G");
|
||
h = TestView (compositor, "H");
|
||
i = TestView (compositor, "I");
|
||
j = TestView (compositor, "J");
|
||
k = TestView (compositor, "K");
|
||
l = TestView (compositor, "L");
|
||
m = TestView (compositor, "M");
|
||
n = TestView (compositor, "N");
|
||
o = TestView (compositor, "O");
|
||
p = TestView (compositor, "P");
|
||
|
||
printf ("SubcompositorInsert (COMPOSITOR, A)\n");
|
||
SubcompositorInsert (compositor, a);
|
||
PrintSubcompositor (compositor);
|
||
printf ("ViewInsert (A, D)\n");
|
||
ViewInsert (a, d);
|
||
PrintSubcompositor (compositor);
|
||
printf ("ViewInsert (A, E)\n");
|
||
ViewInsert (a, e);
|
||
PrintSubcompositor (compositor);
|
||
printf ("ViewInsert (B, F)\n");
|
||
ViewInsert (b, f);
|
||
printf ("ViewInsert (B, G)\n");
|
||
ViewInsert (b, g);
|
||
printf ("SubcompositorInsert (COMPOSITOR, B)\n");
|
||
SubcompositorInsert (compositor, b);
|
||
PrintSubcompositor (compositor);
|
||
printf ("ViewInsert (C, H)\n");
|
||
ViewInsert (c, h);
|
||
printf ("SubcompositorInsert (COMPOSITOR, C)\n");
|
||
SubcompositorInsert (compositor, c);
|
||
PrintSubcompositor (compositor);
|
||
printf ("ViewInsert (C, I)\n");
|
||
ViewInsert (c, i);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewInsert (A, J)\n");
|
||
ViewInsert (a, j);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewUnparent (A)\n");
|
||
ViewUnparent (a);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewUnparent (C)\n");
|
||
ViewUnparent (c);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewUnparent (G)\n");
|
||
ViewUnparent (g);
|
||
printf ("ViewUnparent (J)\n");
|
||
ViewUnparent (j);
|
||
printf ("ViewInsert (G, J)\n");
|
||
ViewInsert (g, j);
|
||
printf ("SubcompositorInsert (COMPOSITOR, G)\n");
|
||
SubcompositorInsert (compositor, g);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewInsertBefore (G, C, J)\n");
|
||
ViewInsertBefore (g, c, j);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewInsertAfter (C, A, H)\n");
|
||
ViewInsertAfter (c, a, h);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewInsert (K, L)\n");
|
||
ViewInsert (k, l);
|
||
|
||
printf ("SubcompositorInsertBefore (COMPOSITOR, K, G)\n");
|
||
SubcompositorInsertBefore (compositor, k, g);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("SubcompositorInsertAfter (COMPOSITOR, M, B)\n");
|
||
SubcompositorInsertAfter (compositor, m, b);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewInsert (M, N)\n");
|
||
ViewInsert (m, n);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewInsertStart (M, O)\n");
|
||
ViewInsertStart (m, o);
|
||
PrintSubcompositor (compositor);
|
||
|
||
printf ("ViewInsertStart (L, P)\n");
|
||
ViewInsertStart (l, p);
|
||
PrintSubcompositor (compositor);
|
||
}
|
||
|
||
int
|
||
main (int argc, char **argv)
|
||
{
|
||
TestSubcompositor ();
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
/* The subcompositor composites its inferior views to a drawable,
|
||
normally a window, each time the SubcompositorUpdate function is
|
||
called. Since it is not very efficient to draw every view every
|
||
time an update occurs, the subcompositor keeps track of which parts
|
||
of the inferiors have changed, and uses that information to only
|
||
composite a reasonable minimum set of inferiors and screen areas on
|
||
each update (reasonable meaning whatever can be computed quickly
|
||
while keeping graphics updates fast). The subcompositor also keeps
|
||
track of which areas of an inferior are opaque, and uses that
|
||
information to avoid compositing in response to damage on inferiors
|
||
that are obscured from above.
|
||
|
||
The subcompositor normally assumes that the contents of the target
|
||
drawable are what was drawn by the subcompositor during previous
|
||
updates. With that in mind, the subcompositor tries to calculate a
|
||
"global damage region" consisting of the areas of the target that
|
||
have to be updated, and a "update inferior", the first inferior
|
||
that will be composited onto the target drawable, by unioning up
|
||
damage and opaque regions of each inferior until the first
|
||
unobscured inferior is found. Then, the contents of all inferiors
|
||
that intersect with the global damage region are composited onto
|
||
the target drawable. Afterwards, the damage region of each
|
||
inferior is cleared, and the process can begin again.
|
||
|
||
Such computation is not reliable, however, if the size or position
|
||
of a view changes. In the interest of keeping thing simple, every
|
||
inferior is composited onto the target drawable whenever a view
|
||
change is detected. These changes are marked by calls to the macro
|
||
SetGarbaged.
|
||
|
||
Further more, the X server can sometimes erase the contents of an
|
||
area of the target window, in response to it being obscured. When
|
||
that happens, that area is entirely composited to the target
|
||
window. See SubcompositorExpose for more details. */
|
||
|
||
#ifndef TEST
|
||
|
||
static void
|
||
ViewAfterSizeUpdate (View *view)
|
||
{
|
||
int doflags;
|
||
Bool mapped;
|
||
|
||
if (!view->subcompositor || !ViewVisibilityState (view, &mapped)
|
||
|| !mapped)
|
||
return;
|
||
|
||
/* First, assume we will have to compute both max_x and max_y. */
|
||
doflags = DoMaxX | DoMaxY;
|
||
|
||
/* 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);
|
||
}
|
||
|
||
void
|
||
ViewAttachBuffer (View *view, ExtBuffer *buffer)
|
||
{
|
||
ExtBuffer *old;
|
||
|
||
old = view->buffer;
|
||
view->buffer = buffer;
|
||
|
||
if (!old != !buffer)
|
||
{
|
||
/* TODO: just damage intersecting views before view->link if the
|
||
buffer was removed. */
|
||
if (view->subcompositor)
|
||
SetGarbaged (view->subcompositor);
|
||
}
|
||
|
||
if ((buffer && !old)
|
||
|| (old && !buffer)
|
||
|| (buffer && old
|
||
&& (XLBufferWidth (buffer) != XLBufferWidth (old)
|
||
|| XLBufferHeight (buffer) != XLBufferHeight (old))))
|
||
{
|
||
if (view->subcompositor)
|
||
{
|
||
/* A new buffer was attached, so garbage the subcompositor
|
||
as well. */
|
||
SetGarbaged (view->subcompositor);
|
||
|
||
/* Recompute view bounds. */
|
||
ViewAfterSizeUpdate (view);
|
||
}
|
||
}
|
||
|
||
if (buffer && IsViewUnmapped (view))
|
||
{
|
||
/* A buffer is now attached. Automatically map the view, should
|
||
it be unmapped. */
|
||
ClearUnmapped (view);
|
||
|
||
if (view->subcompositor)
|
||
{
|
||
/* Garbage the subcompositor and recompute bounds. */
|
||
SetGarbaged (view->subcompositor);
|
||
SubcompositorUpdateBounds (view->subcompositor, DoAll);
|
||
}
|
||
}
|
||
|
||
if (old)
|
||
XLDereferenceBuffer (old);
|
||
|
||
if (view->buffer)
|
||
XLRetainBuffer (buffer);
|
||
}
|
||
|
||
void
|
||
ViewMove (View *view, int x, int y)
|
||
{
|
||
int doflags;
|
||
Bool mapped;
|
||
|
||
doflags = 0;
|
||
|
||
if (x != view->x || y != view->y)
|
||
{
|
||
view->x = x;
|
||
view->y = y;
|
||
|
||
if (view->parent)
|
||
{
|
||
view->abs_x = view->parent->abs_x + x;
|
||
view->abs_y = view->parent->abs_y + y;
|
||
}
|
||
else
|
||
{
|
||
view->abs_x = x;
|
||
view->abs_y = x;
|
||
}
|
||
|
||
if (view->subcompositor && ViewVisibilityState (view, &mapped)
|
||
/* If this view isn't mapped, then do nothing. The bounds
|
||
will be recomputed later. */
|
||
&& mapped)
|
||
{
|
||
/* First assume everything will have to be updated. */
|
||
doflags |= DoMaxX | DoMaxY | DoMinY | DoMinX;
|
||
|
||
/* If this view was moved before subcompositor.min_x and/or
|
||
subcompositor.min_y, don't recompute those values
|
||
unnecessarily. */
|
||
|
||
if (view->abs_x < view->subcompositor->min_x)
|
||
{
|
||
view->subcompositor->min_x = view->abs_x;
|
||
|
||
/* min_x has already been updated so there is no need to
|
||
recompute it later. */
|
||
doflags &= ~DoMinX;
|
||
}
|
||
|
||
if (view->abs_y < view->subcompositor->min_x)
|
||
{
|
||
view->subcompositor->min_y = view->abs_y;
|
||
|
||
/* min_y has already been updated so there is no need to
|
||
recompute it later. */
|
||
doflags &= ~DoMinY;
|
||
}
|
||
|
||
/* If moving this biew bumps subcompositor.max_x and/or
|
||
subcompositor.max_y, don't recompute either. */
|
||
|
||
if (view->subcompositor->max_x < ViewMaxX (view))
|
||
{
|
||
view->subcompositor->max_x = ViewMaxX (view);
|
||
|
||
/* max_x has been updated so there is no need to
|
||
recompute it later. If a child is bigger, then
|
||
ViewRecomputeChildren will handle it as well. */
|
||
doflags &= ~DoMaxX;
|
||
}
|
||
|
||
if (view->subcompositor->max_y < ViewMaxX (view))
|
||
{
|
||
view->subcompositor->max_y = ViewMaxX (view);
|
||
|
||
/* max_y has been updated so there is no need to
|
||
recompute it later. If a child is bigger, then
|
||
ViewRecomputeChildren will handle it as well. */
|
||
doflags &= ~DoMaxY;
|
||
}
|
||
|
||
/* Also garbage the subcompositor since those values
|
||
changed. TODO: just damage intersecting views before
|
||
view->link. */
|
||
SetGarbaged (view->subcompositor);
|
||
}
|
||
|
||
/* Now calculate the absolute position for this view and all of
|
||
its children. N.B. that this operation can also update
|
||
subcompositor.min_x or subcompositor.min_y. */
|
||
ViewRecomputeChildren (view, &doflags);
|
||
|
||
/* Update subcompositor bounds. */
|
||
if (view->subcompositor)
|
||
SubcompositorUpdateBounds (view->subcompositor, doflags);
|
||
}
|
||
}
|
||
|
||
void
|
||
ViewDetach (View *view)
|
||
{
|
||
ViewAttachBuffer (view, NULL);
|
||
}
|
||
|
||
void
|
||
ViewMap (View *view)
|
||
{
|
||
if (!IsViewUnmapped (view))
|
||
return;
|
||
|
||
ClearUnmapped (view);
|
||
|
||
if (view->subcompositor
|
||
&& (view->link != view->inferior || view->buffer))
|
||
{
|
||
/* Garbage the subcompositor and recompute bounds, if something
|
||
is attached to the view or it is not empty. */
|
||
SetGarbaged (view->subcompositor);
|
||
SubcompositorUpdateBounds (view->subcompositor, DoAll);
|
||
}
|
||
}
|
||
|
||
void
|
||
ViewUnmap (View *view)
|
||
{
|
||
if (IsViewUnmapped (view))
|
||
return;
|
||
|
||
/* Mark the view as unmapped. */
|
||
SetUnmapped (view);
|
||
|
||
if (view->subcompositor)
|
||
{
|
||
/* Mark the subcompositor as having unmapped views. */
|
||
SetPartiallyMapped (view->subcompositor);
|
||
|
||
/* If the link pointer is the inferior pointer and there is no
|
||
buffer attached to the view, it is empty. There is no need
|
||
to do anything other than marking the subcompositor as
|
||
partially mapped. */
|
||
if (view->link != view->inferior || view->buffer)
|
||
{
|
||
/* Recompute the bounds of the subcompositor. */
|
||
SubcompositorUpdateBounds (view->subcompositor,
|
||
DoAll);
|
||
|
||
/* Garbage the view's subcompositor. */
|
||
SetGarbaged (view->subcompositor);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
ViewFree (View *view)
|
||
{
|
||
/* It's not valid to call this function on a view with children or a
|
||
parent. */
|
||
XLAssert (view->link == view->inferior);
|
||
XLAssert (view->link->last == view->link);
|
||
|
||
if (view->buffer)
|
||
ViewDetach (view);
|
||
|
||
XLFree (view->link);
|
||
XLFree (view->self);
|
||
XLFree (view->children);
|
||
|
||
pixman_region32_fini (&view->damage);
|
||
pixman_region32_fini (&view->opaque);
|
||
pixman_region32_fini (&view->input);
|
||
|
||
XLFree (view);
|
||
}
|
||
|
||
void
|
||
ViewDamage (View *view, pixman_region32_t *damage)
|
||
{
|
||
pixman_region32_union (&view->damage,
|
||
&view->damage,
|
||
damage);
|
||
}
|
||
|
||
void
|
||
ViewSetOpaque (View *view, pixman_region32_t *opaque)
|
||
{
|
||
pixman_region32_copy (&view->opaque, opaque);
|
||
|
||
if (view->subcompositor)
|
||
SetOpaqueDirty (view->subcompositor);
|
||
}
|
||
|
||
void
|
||
ViewSetInput (View *view, pixman_region32_t *input)
|
||
{
|
||
if (pixman_region32_equal (input, &view->input))
|
||
return;
|
||
|
||
pixman_region32_copy (&view->input, input);
|
||
|
||
if (view->subcompositor)
|
||
SetInputDirty (view->subcompositor);
|
||
}
|
||
|
||
Subcompositor *
|
||
ViewGetSubcompositor (View *view)
|
||
{
|
||
return view->subcompositor;
|
||
}
|
||
|
||
int
|
||
ViewWidth (View *view)
|
||
{
|
||
int width;
|
||
|
||
if (!view->buffer)
|
||
return 0;
|
||
|
||
width = XLBufferWidth (view->buffer);
|
||
|
||
if (view->scale < 0)
|
||
return width * (abs (view->scale) + 1);
|
||
else
|
||
return width / (view->scale + 1);
|
||
}
|
||
|
||
int
|
||
ViewHeight (View *view)
|
||
{
|
||
int height;
|
||
|
||
if (!view->buffer)
|
||
return 0;
|
||
|
||
height = XLBufferHeight (view->buffer);
|
||
|
||
if (view->scale < 0)
|
||
return height * (abs (view->scale) + 1);
|
||
else
|
||
return 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);
|
||
}
|
||
}
|
||
|
||
static double
|
||
GetTxTy (int scale)
|
||
{
|
||
if (scale > 0)
|
||
return scale + 1;
|
||
|
||
return 1.0 / (-scale + 1);
|
||
}
|
||
|
||
static XTransform
|
||
ViewGetTransform (View *view)
|
||
{
|
||
XTransform transform;
|
||
double transform_value;
|
||
|
||
/* Perform scaling in the transform matrix. */
|
||
|
||
memset (&transform, 0, sizeof transform);
|
||
transform_value = GetTxTy (view->scale);
|
||
transform.matrix[0][0] = XDoubleToFixed (transform_value);
|
||
transform.matrix[1][1] = XDoubleToFixed (transform_value);
|
||
transform.matrix[2][2] = XDoubleToFixed (1);
|
||
|
||
return transform;
|
||
}
|
||
|
||
/* TODO: the callers of this can be optimized by setting the picture
|
||
transform on the attached buffer if that buffer is not attached to
|
||
any other view. */
|
||
|
||
static Bool
|
||
ViewHaveTransform (View *view)
|
||
{
|
||
/* view->scale is the amount by which to scale _down_ the view. If
|
||
it is 0, then no scaling will be performed. */
|
||
return view->scale;
|
||
}
|
||
|
||
void
|
||
SubcompositorSetOpaqueCallback (Subcompositor *subcompositor,
|
||
void (*opaque_changed) (Subcompositor *,
|
||
void *,
|
||
pixman_region32_t *),
|
||
void *data)
|
||
{
|
||
subcompositor->opaque_change = opaque_changed;
|
||
subcompositor->opaque_change_data = data;
|
||
}
|
||
|
||
void
|
||
SubcompositorSetInputCallback (Subcompositor *subcompositor,
|
||
void (*input_changed) (Subcompositor *,
|
||
void *,
|
||
pixman_region32_t *),
|
||
void *data)
|
||
{
|
||
subcompositor->input_change = input_changed;
|
||
subcompositor->input_change_data = data;
|
||
}
|
||
|
||
void
|
||
SubcompositorSetBoundsCallback (Subcompositor *subcompositor,
|
||
void (*note_bounds) (void *, int, int,
|
||
int, int),
|
||
void *data)
|
||
{
|
||
subcompositor->note_bounds = note_bounds;
|
||
subcompositor->note_bounds_data = data;
|
||
}
|
||
|
||
static void
|
||
FillBoxesWithTransparency (Subcompositor *subcompositor,
|
||
pixman_box32_t *boxes, int nboxes)
|
||
{
|
||
XRectangle *rects;
|
||
static XRenderColor color;
|
||
int i;
|
||
Picture picture;
|
||
|
||
picture = subcompositor->target;
|
||
|
||
if (nboxes < 256)
|
||
rects = alloca (sizeof *rects * nboxes);
|
||
else
|
||
rects = XLMalloc (sizeof *rects * nboxes);
|
||
|
||
for (i = 0; i < nboxes; ++i)
|
||
{
|
||
rects[i].x = BoxStartX (boxes[i]) - subcompositor->min_x;
|
||
rects[i].y = BoxStartY (boxes[i]) - subcompositor->min_y;
|
||
rects[i].width = BoxWidth (boxes[i]);
|
||
rects[i].height = BoxHeight (boxes[i]);
|
||
}
|
||
|
||
XRenderFillRectangles (compositor.display, PictOpClear, picture,
|
||
&color, rects, nboxes);
|
||
|
||
if (nboxes >= 256)
|
||
XLFree (rects);
|
||
}
|
||
|
||
static Bool
|
||
ViewContainsExtents (View *view, pixman_box32_t *box)
|
||
{
|
||
int x, y, width, height;
|
||
|
||
x = view->abs_x;
|
||
y = view->abs_y;
|
||
width = ViewWidth (view);
|
||
height = ViewHeight (view);
|
||
|
||
return (box->x1 >= x && box->y1 >= y
|
||
&& box->x2 <= x + width
|
||
&& box->y2 <= x + height);
|
||
}
|
||
|
||
void
|
||
SubcompositorBounds (Subcompositor *subcompositor,
|
||
int *min_x, int *min_y, int *max_x, int *max_y)
|
||
{
|
||
*min_x = subcompositor->min_x;
|
||
*min_y = subcompositor->min_y;
|
||
*max_x = subcompositor->max_x;
|
||
*max_y = subcompositor->max_y;
|
||
}
|
||
|
||
Bool
|
||
SubcompositorIsEmpty (Subcompositor *subcompositor)
|
||
{
|
||
return (subcompositor->min_x == subcompositor->max_x
|
||
&& subcompositor->min_y == subcompositor->max_y);
|
||
}
|
||
|
||
void
|
||
SubcompositorUpdate (Subcompositor *subcompositor)
|
||
{
|
||
pixman_region32_t update_region, temp, start_opaque;
|
||
pixman_region32_t total_opaque, total_input;
|
||
View *start, *original_start, *view, *first;
|
||
List *list;
|
||
pixman_box32_t *boxes, *extents, temp_boxes;
|
||
int nboxes, i, op, tx, ty;
|
||
Picture picture;
|
||
XTransform transform;
|
||
int min_x, min_y;
|
||
|
||
/* Just return if no target was specified. */
|
||
if (subcompositor->target == None)
|
||
return;
|
||
|
||
/* Likewise if the subcompositor is "frozen". */
|
||
if (IsFrozen (subcompositor))
|
||
return;
|
||
|
||
list = subcompositor->inferiors;
|
||
min_x = subcompositor->min_x;
|
||
min_y = subcompositor->min_y;
|
||
tx = subcompositor->tx;
|
||
ty = subcompositor->ty;
|
||
start = NULL;
|
||
original_start = NULL;
|
||
pixman_region32_init (&temp);
|
||
pixman_region32_init (&update_region);
|
||
|
||
start = subcompositor->inferiors->next->view;
|
||
original_start = subcompositor->inferiors->next->view;
|
||
|
||
/* Clear the "is partially mapped" flag. It will be set later on if
|
||
there is actually a partially mapped view. */
|
||
subcompositor->state &= ~SubcompositorIsPartiallyMapped;
|
||
|
||
if (subcompositor->note_bounds)
|
||
subcompositor->note_bounds (subcompositor->note_bounds_data,
|
||
min_x, min_y, subcompositor->max_x,
|
||
subcompositor->max_y);
|
||
|
||
if (!IsGarbaged (subcompositor))
|
||
{
|
||
start = NULL;
|
||
original_start = NULL;
|
||
|
||
if (IsOpaqueDirty (subcompositor))
|
||
pixman_region32_init (&total_opaque);
|
||
|
||
if (IsInputDirty (subcompositor))
|
||
pixman_region32_init (&total_input);
|
||
|
||
pixman_region32_init (&start_opaque);
|
||
|
||
do
|
||
{
|
||
view = list->view;
|
||
|
||
if (!view)
|
||
goto next;
|
||
|
||
if (IsViewUnmapped (view))
|
||
{
|
||
/* The view is unmapped. Skip past it and all its
|
||
children. */
|
||
list = view->inferior;
|
||
|
||
/* Set the "is partially mapped" flag. This is an
|
||
optimization used to make inserting views in deeply
|
||
nested hierarchies faster. */
|
||
SetPartiallyMapped (subcompositor);
|
||
goto next;
|
||
}
|
||
|
||
if (!view->buffer)
|
||
goto next;
|
||
|
||
if (!start)
|
||
{
|
||
start = view;
|
||
original_start = view;
|
||
}
|
||
|
||
if (pixman_region32_not_empty (&list->view->opaque))
|
||
{
|
||
/* Translate the region into the subcompositor
|
||
coordinate space. */
|
||
pixman_region32_translate (&list->view->opaque,
|
||
list->view->abs_x,
|
||
list->view->abs_y);
|
||
|
||
/* Only use the intersection between the opaque region
|
||
and the rectangle of the view, since the opaque areas
|
||
cannot extend outside it. */
|
||
|
||
pixman_region32_intersect_rect (&temp, &view->opaque,
|
||
view->abs_x, view->abs_y,
|
||
ViewWidth (view),
|
||
ViewHeight (view));
|
||
|
||
if (IsOpaqueDirty (subcompositor))
|
||
pixman_region32_union (&total_opaque, &total_opaque, &temp);
|
||
|
||
pixman_region32_subtract (&update_region,
|
||
&update_region, &temp);
|
||
|
||
/* This view will obscure all preceding damaged areas,
|
||
so make start here. */
|
||
if (!pixman_region32_not_empty (&update_region))
|
||
{
|
||
start = list->view;
|
||
|
||
/* Now that start changed, record the opaque region.
|
||
That way, if some damage happens outside the
|
||
opaque region in the future, this operation can
|
||
be undone. */
|
||
pixman_region32_copy (&start_opaque, &view->opaque);
|
||
}
|
||
|
||
pixman_region32_translate (&list->view->opaque,
|
||
-list->view->abs_x,
|
||
-list->view->abs_y);
|
||
}
|
||
|
||
if (pixman_region32_not_empty (&list->view->input)
|
||
&& IsInputDirty (subcompositor))
|
||
{
|
||
/* Translate the region into the subcompositor
|
||
coordinate space. */
|
||
pixman_region32_translate (&list->view->input,
|
||
list->view->abs_x,
|
||
list->view->abs_y);
|
||
|
||
pixman_region32_intersect_rect (&temp, &view->input,
|
||
view->abs_x, view->abs_y,
|
||
ViewWidth (view),
|
||
ViewHeight (view));
|
||
|
||
pixman_region32_union (&total_input, &total_input, &temp);
|
||
|
||
/* Restore the original input region. */
|
||
pixman_region32_translate (&list->view->input,
|
||
-list->view->abs_x,
|
||
-list->view->abs_y);
|
||
}
|
||
|
||
if (pixman_region32_not_empty (&list->view->damage))
|
||
{
|
||
/* Translate the region into the subcompositor
|
||
coordinate space. */
|
||
pixman_region32_translate (&list->view->damage,
|
||
list->view->abs_x,
|
||
list->view->abs_y);
|
||
|
||
/* Similarly intersect the damage region with the
|
||
clipping. */
|
||
pixman_region32_intersect_rect (&temp, &list->view->damage,
|
||
view->abs_x, view->abs_y,
|
||
ViewWidth (view),
|
||
ViewHeight (view));
|
||
|
||
pixman_region32_union (&update_region, &temp, &update_region);
|
||
|
||
/* If the damage extends outside the area known to be
|
||
obscured by the current start, reset start back to
|
||
the original starting point. */
|
||
if (start != original_start)
|
||
{
|
||
pixman_region32_subtract (&temp, &list->view->damage,
|
||
&start_opaque);
|
||
|
||
if (pixman_region32_not_empty (&temp))
|
||
start = original_start;
|
||
}
|
||
|
||
/* Clear the damaged area, since it will either be drawn
|
||
or be obscured. */
|
||
pixman_region32_clear (&list->view->damage);
|
||
}
|
||
|
||
next:
|
||
list = list->next;
|
||
}
|
||
while (list != subcompositor->inferiors);
|
||
|
||
if (IsOpaqueDirty (subcompositor))
|
||
{
|
||
/* The opaque region changed, so run any callbacks. */
|
||
if (subcompositor->opaque_change)
|
||
{
|
||
/* Translate this to appear in the "virtual" coordinate
|
||
space. */
|
||
pixman_region32_translate (&total_opaque, -min_x, -min_y);
|
||
|
||
subcompositor->opaque_change (subcompositor,
|
||
subcompositor->opaque_change_data,
|
||
&total_opaque);
|
||
}
|
||
|
||
pixman_region32_fini (&total_opaque);
|
||
}
|
||
|
||
if (IsInputDirty (subcompositor))
|
||
{
|
||
/* The input region changed, so run any callbacks. */
|
||
if (subcompositor->input_change)
|
||
{
|
||
/* Translate this to appear in the "virtual" coordinate
|
||
space. */
|
||
pixman_region32_translate (&total_input, -min_x, -min_y);
|
||
|
||
subcompositor->input_change (subcompositor,
|
||
subcompositor->input_change_data,
|
||
&total_input);
|
||
}
|
||
|
||
pixman_region32_fini (&total_input);
|
||
}
|
||
|
||
pixman_region32_fini (&start_opaque);
|
||
}
|
||
else
|
||
{
|
||
/* To save from iterating over all the views twice, perform the
|
||
input and opaque region updates in the draw loop instead. */
|
||
pixman_region32_init (&total_opaque);
|
||
pixman_region32_init (&total_input);
|
||
}
|
||
|
||
/* If there's nothing to do, return. */
|
||
|
||
if (!start)
|
||
goto complete;
|
||
|
||
/* Now update all views from start onwards. */
|
||
|
||
list = start->link;
|
||
first = NULL;
|
||
|
||
do
|
||
{
|
||
view = list->view;
|
||
|
||
if (!view)
|
||
goto next_1;
|
||
|
||
if (IsViewUnmapped (view))
|
||
{
|
||
/* Skip the unmapped view. */
|
||
list = view->inferior;
|
||
|
||
/* Set the "is partially mapped" flag. This is an
|
||
optimization used to make inserting views in deeply
|
||
nested hierarchies faster. */
|
||
SetPartiallyMapped (subcompositor);
|
||
goto next_1;
|
||
}
|
||
|
||
if (!view->buffer)
|
||
goto next_1;
|
||
|
||
picture = XLPictureFromBuffer (view->buffer);
|
||
|
||
if (!first)
|
||
{
|
||
/* The first view with an attached buffer should be drawn
|
||
with PictOpSrc so that transparency is applied correctly,
|
||
if it contains the entire update region. */
|
||
|
||
if (IsGarbaged (subcompositor))
|
||
{
|
||
extents = &temp_boxes;
|
||
|
||
/* Make extents the entire region, since that's what is
|
||
being updated. */
|
||
temp_boxes.x1 = min_x;
|
||
temp_boxes.y1 = min_y;
|
||
temp_boxes.x2 = subcompositor->max_x + 1;
|
||
temp_boxes.y2 = subcompositor->max_y + 1;
|
||
}
|
||
else
|
||
extents = pixman_region32_extents (&update_region);
|
||
|
||
if (ViewContainsExtents (view, extents))
|
||
/* The update region is contained by the entire view, so
|
||
use PictOpSrc. */
|
||
op = PictOpSrc;
|
||
else
|
||
{
|
||
/* Otherwise, fill the whole update region with
|
||
transparency. */
|
||
|
||
if (IsGarbaged (subcompositor))
|
||
{
|
||
/* Use the entire subcompositor bounds if
|
||
garbaged. */
|
||
boxes = &temp_boxes;
|
||
nboxes = 1;
|
||
}
|
||
else
|
||
boxes = pixman_region32_rectangles (&update_region,
|
||
&nboxes);
|
||
|
||
/* Fill with transparency. */
|
||
FillBoxesWithTransparency (subcompositor,
|
||
boxes, nboxes);
|
||
|
||
/* And use PictOpOver as usual. */
|
||
op = PictOpOver;
|
||
}
|
||
}
|
||
else
|
||
op = PictOpOver;
|
||
|
||
first = view;
|
||
|
||
if (ViewHaveTransform (view))
|
||
{
|
||
transform = ViewGetTransform (view);
|
||
|
||
XRenderSetPictureTransform (compositor.display, picture,
|
||
&transform);
|
||
}
|
||
|
||
if (!IsGarbaged (subcompositor))
|
||
{
|
||
/* First, obtain a new region that is the intersection of
|
||
the view with the global update region. */
|
||
pixman_region32_intersect_rect (&temp, &update_region,
|
||
view->abs_x, view->abs_y,
|
||
ViewWidth (view),
|
||
ViewHeight (view));
|
||
|
||
/* Next, composite every rectangle in that region. */
|
||
boxes = pixman_region32_rectangles (&temp, &nboxes);
|
||
|
||
for (i = 0; i < nboxes; ++i)
|
||
XRenderComposite (compositor.display, op, picture,
|
||
None, subcompositor->target,
|
||
/* src-x. */
|
||
BoxStartX (boxes[i]) - view->abs_x,
|
||
/* src-y. */
|
||
BoxStartY (boxes[i]) - view->abs_y,
|
||
/* mask-x, mask-y. */
|
||
0, 0,
|
||
/* dst-x. */
|
||
BoxStartX (boxes[i]) - min_x + tx,
|
||
/* dst-y. */
|
||
BoxStartY (boxes[i]) - min_y + ty,
|
||
/* width, height. */
|
||
BoxWidth (boxes[i]), BoxHeight (boxes[i]));
|
||
}
|
||
else
|
||
{
|
||
/* Clear the damaged area, since it will either be drawn or
|
||
be obscured. We didn't get a chance to clear the damage
|
||
earlier, since the compositor was garbaged. */
|
||
pixman_region32_clear (&view->damage);
|
||
|
||
/* If the subcompositor is garbaged, composite the entire view
|
||
to the right location. */
|
||
XRenderComposite (compositor.display, op, picture,
|
||
None, subcompositor->target,
|
||
/* src-x, src-y. */
|
||
0, 0,
|
||
/* mask-x, mask-y. */
|
||
0, 0,
|
||
/* dst-x. */
|
||
view->abs_x - min_x + tx,
|
||
/* dst-y. */
|
||
view->abs_y - min_y + ty,
|
||
/* width. */
|
||
ViewWidth (view),
|
||
/* height. */
|
||
ViewHeight (view));
|
||
|
||
/* Also adjust the opaque and input regions here. */
|
||
|
||
if (pixman_region32_not_empty (&view->opaque))
|
||
{
|
||
/* Translate the region into the global coordinate
|
||
space. */
|
||
pixman_region32_translate (&list->view->opaque,
|
||
list->view->abs_x,
|
||
list->view->abs_y);
|
||
|
||
pixman_region32_intersect_rect (&temp, &view->opaque,
|
||
view->abs_x, view->abs_y,
|
||
ViewWidth (view),
|
||
ViewHeight (view));
|
||
pixman_region32_union (&total_opaque, &temp, &total_opaque);
|
||
|
||
/* Translate it back. */
|
||
pixman_region32_translate (&list->view->opaque,
|
||
-list->view->abs_x,
|
||
-list->view->abs_y);
|
||
}
|
||
|
||
if (pixman_region32_not_empty (&view->input))
|
||
{
|
||
/* Translate the region into the global coordinate
|
||
space. */
|
||
pixman_region32_translate (&list->view->input,
|
||
list->view->abs_x,
|
||
list->view->abs_y);
|
||
pixman_region32_intersect_rect (&temp, &view->input,
|
||
view->abs_x, view->abs_y,
|
||
ViewWidth (view),
|
||
ViewHeight (view));
|
||
pixman_region32_union (&total_input, &temp, &total_input);
|
||
|
||
/* Translate it back. */
|
||
pixman_region32_translate (&list->view->input,
|
||
-list->view->abs_x,
|
||
-list->view->abs_y);
|
||
}
|
||
}
|
||
|
||
if (ViewHaveTransform (view))
|
||
XRenderSetPictureTransform (compositor.display, picture,
|
||
&identity_transform);
|
||
|
||
next_1:
|
||
list = list->next;
|
||
}
|
||
while (list != subcompositor->inferiors);
|
||
|
||
complete:
|
||
|
||
if (IsGarbaged (subcompositor))
|
||
{
|
||
/* The opaque region changed, so run any callbacks. */
|
||
if (subcompositor->opaque_change)
|
||
{
|
||
/* Translate this to appear in the "virtual" coordinate
|
||
space. */
|
||
pixman_region32_translate (&total_opaque, -min_x, -min_y);
|
||
|
||
subcompositor->opaque_change (subcompositor,
|
||
subcompositor->opaque_change_data,
|
||
&total_opaque);
|
||
}
|
||
|
||
pixman_region32_fini (&total_opaque);
|
||
|
||
/* The input region changed, so run any callbacks. */
|
||
if (subcompositor->input_change)
|
||
{
|
||
/* Translate this to appear in the "virtual" coordinate
|
||
space. */
|
||
pixman_region32_translate (&total_input, -min_x, -min_y);
|
||
|
||
subcompositor->input_change (subcompositor,
|
||
subcompositor->input_change_data,
|
||
&total_input);
|
||
}
|
||
|
||
pixman_region32_fini (&total_input);
|
||
}
|
||
|
||
pixman_region32_fini (&temp);
|
||
pixman_region32_fini (&update_region);
|
||
|
||
/* The update has completed, so the compositor is no longer
|
||
garbaged. */
|
||
subcompositor->state &= ~SubcompositorIsGarbaged;
|
||
subcompositor->state &= ~SubcompositorIsOpaqueDirty;
|
||
subcompositor->state &= ~SubcompositorIsInputDirty;
|
||
}
|
||
|
||
void
|
||
SubcompositorExpose (Subcompositor *subcompositor, XEvent *event)
|
||
{
|
||
List *list;
|
||
View *view;
|
||
int x, y, width, height, nboxes, min_x, min_y, tx, ty;
|
||
pixman_box32_t extents, *boxes;
|
||
int op, i;
|
||
pixman_region32_t temp;
|
||
Picture picture;
|
||
XTransform transform;
|
||
|
||
/* Graphics exposures are not yet handled. */
|
||
if (event->type == GraphicsExpose)
|
||
return;
|
||
|
||
/* No target? No update. */
|
||
if (subcompositor->target == None)
|
||
return;
|
||
|
||
x = event->xexpose.x + subcompositor->min_x;
|
||
y = event->xexpose.y + subcompositor->min_y;
|
||
width = event->xexpose.width;
|
||
height = event->xexpose.height;
|
||
|
||
min_x = subcompositor->min_x;
|
||
min_y = subcompositor->min_y;
|
||
tx = subcompositor->tx;
|
||
ty = subcompositor->ty;
|
||
|
||
extents.x1 = x;
|
||
extents.y1 = y;
|
||
extents.x2 = x + width;
|
||
extents.y2 = y + height;
|
||
|
||
view = NULL;
|
||
|
||
/* Draw every subsurface overlapping the exposure region from the
|
||
subcompositor onto the target. Most importantly, do NOT update
|
||
the bounds of the target, in case the exposure is in response to
|
||
a resize. */
|
||
|
||
list = subcompositor->inferiors;
|
||
|
||
do
|
||
{
|
||
if (!list->view)
|
||
goto next;
|
||
|
||
if (IsViewUnmapped (list->view))
|
||
{
|
||
list = list->view->inferior;
|
||
goto next;
|
||
}
|
||
|
||
if (!list->view->buffer)
|
||
goto next;
|
||
|
||
/* If the first mapped view contains everything, draw it with
|
||
PictOpSrc. */
|
||
if (!view && ViewContainsExtents (list->view, &extents))
|
||
op = PictOpSrc;
|
||
else
|
||
{
|
||
/* Otherwise, fill the region with transparency for the
|
||
first update, and then use PictOpOver. */
|
||
|
||
if (!view)
|
||
FillBoxesWithTransparency (subcompositor,
|
||
&extents, 1);
|
||
|
||
op = PictOpOver;
|
||
}
|
||
|
||
view = list->view;
|
||
|
||
/* Now, get the intersection of the rectangle with the view
|
||
bounds. */
|
||
pixman_region32_init_rect (&temp, x, y, width, height);
|
||
pixman_region32_intersect_rect (&temp, &temp, view->abs_x,
|
||
view->abs_y, ViewWidth (view),
|
||
ViewHeight (view));
|
||
|
||
/* Composite the contents according to OP. */
|
||
picture = XLPictureFromBuffer (view->buffer);
|
||
boxes = pixman_region32_rectangles (&temp, &nboxes);
|
||
|
||
if (ViewHaveTransform (view))
|
||
{
|
||
/* Set up transforms if necessary. */
|
||
transform = ViewGetTransform (view);
|
||
|
||
XRenderSetPictureTransform (compositor.display, picture,
|
||
&transform);
|
||
}
|
||
|
||
for (i = 0; i < nboxes; ++i)
|
||
XRenderComposite (compositor.display, op, picture,
|
||
None, subcompositor->target,
|
||
/* src-x. */
|
||
BoxStartX (boxes[i]) - view->abs_x,
|
||
/* src-y. */
|
||
BoxStartY (boxes[i]) - view->abs_y,
|
||
/* mask-x, mask-y. */
|
||
0, 0,
|
||
/* dst-x. */
|
||
BoxStartX (boxes[i]) - min_x + tx,
|
||
/* dst-y. */
|
||
BoxStartY (boxes[i]) - min_y + ty,
|
||
/* width, height. */
|
||
BoxWidth (boxes[i]), BoxHeight (boxes[i]));
|
||
|
||
/* Undo transforms that were applied. */
|
||
if (ViewHaveTransform (view))
|
||
XRenderSetPictureTransform (compositor.display, picture,
|
||
&identity_transform);
|
||
|
||
/* Free the scratch region used to compute the intersection. */
|
||
pixman_region32_fini (&temp);
|
||
|
||
next:
|
||
/* Move onto the next view. */
|
||
list = list->next;
|
||
}
|
||
while (list != subcompositor->inferiors);
|
||
}
|
||
|
||
void
|
||
SubcompositorGarbage (Subcompositor *subcompositor)
|
||
{
|
||
SetGarbaged (subcompositor);
|
||
}
|
||
|
||
void
|
||
SubcompositorSetProjectiveTransform (Subcompositor *subcompositor,
|
||
int tx, int ty)
|
||
{
|
||
subcompositor->tx = tx;
|
||
subcompositor->ty = ty;
|
||
}
|
||
|
||
void
|
||
SubcompositorFree (Subcompositor *subcompositor)
|
||
{
|
||
/* It isn't valid to call this function with children attached. */
|
||
XLAssert (subcompositor->children->next
|
||
== subcompositor->children);
|
||
XLAssert (subcompositor->inferiors->next
|
||
== subcompositor->inferiors);
|
||
|
||
XLFree (subcompositor->children);
|
||
XLFree (subcompositor->inferiors);
|
||
|
||
XLFree (subcompositor);
|
||
}
|
||
|
||
View *
|
||
SubcompositorLookupView (Subcompositor *subcompositor, int x, int y,
|
||
int *view_x, int *view_y)
|
||
{
|
||
List *list;
|
||
int temp_x, temp_y;
|
||
pixman_box32_t box;
|
||
|
||
x += subcompositor->min_x;
|
||
y += subcompositor->min_y;
|
||
|
||
for (list = subcompositor->inferiors->last;
|
||
list != subcompositor->inferiors;
|
||
list = list->last)
|
||
{
|
||
if (!list->view)
|
||
continue;
|
||
|
||
if (IsViewUnmapped (list->view))
|
||
{
|
||
list = list->view->inferior;
|
||
continue;
|
||
}
|
||
|
||
if (!list->view->buffer)
|
||
continue;
|
||
|
||
temp_x = x - list->view->abs_x;
|
||
temp_y = y - list->view->abs_y;
|
||
|
||
/* If the coordinates don't fit in the view bounds, skip the
|
||
view. This test is the equivalent to intersecting the view's
|
||
input region with the bounds of the view. */
|
||
if (temp_x < 0 || temp_y < 0
|
||
|| temp_x >= ViewWidth (list->view)
|
||
|| temp_y >= ViewHeight (list->view))
|
||
continue;
|
||
|
||
/* Now see if the input region contains the given
|
||
coordinates. If it does, return the view. */
|
||
if (pixman_region32_contains_point (&list->view->input, x, y,
|
||
&box))
|
||
{
|
||
*view_x = list->view->abs_x - subcompositor->min_x;
|
||
*view_y = list->view->abs_y - subcompositor->min_y;
|
||
|
||
return list->view;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
void *
|
||
ViewGetData (View *view)
|
||
{
|
||
return view->data;
|
||
}
|
||
|
||
void
|
||
ViewSetData (View *view, void *data)
|
||
{
|
||
view->data = data;
|
||
}
|
||
|
||
void
|
||
ViewTranslate (View *view, int x, int y, int *x_out, int *y_out)
|
||
{
|
||
if (view->subcompositor)
|
||
{
|
||
/* X and Y are assumed to be in the "virtual" coordinate
|
||
space. */
|
||
x += view->subcompositor->min_x;
|
||
y += view->subcompositor->min_y;
|
||
}
|
||
|
||
*x_out = x - view->abs_x;
|
||
*y_out = y - view->abs_y;
|
||
}
|
||
|
||
View *
|
||
ViewGetParent (View *view)
|
||
{
|
||
return view->parent;
|
||
}
|
||
|
||
void
|
||
SubcompositorInit (void)
|
||
{
|
||
identity_transform.matrix[0][0] = 1;
|
||
identity_transform.matrix[1][1] = 1;
|
||
identity_transform.matrix[2][2] = 1;
|
||
}
|
||
|
||
int
|
||
SubcompositorWidth (Subcompositor *subcompositor)
|
||
{
|
||
return subcompositor->max_x - subcompositor->min_x + 1;
|
||
}
|
||
|
||
int
|
||
SubcompositorHeight (Subcompositor *subcompositor)
|
||
{
|
||
return subcompositor->max_y - subcompositor->min_y + 1;
|
||
}
|
||
|
||
void
|
||
SubcompositorFreeze (Subcompositor *subcompositor)
|
||
{
|
||
SetFrozen (subcompositor);
|
||
}
|
||
|
||
void
|
||
SubcompositorUnfreeze (Subcompositor *subcompositor)
|
||
{
|
||
subcompositor->state &= ~SubcompositorIsFrozen;
|
||
}
|
||
|
||
#endif
|