Compare commits

...

50 Commits

Author SHA1 Message Date
Carlos Garnacho
9321249b8d wayland: Implement the "inputfd" wayland protocols
This allows lending control to applications of evdev devices,
and withdrawing it with focus.
2020-06-10 20:32:19 +02:00
Jonas Ådahl
932340a989 background-content: Shut up warning
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1303
2020-06-10 10:42:18 +02:00
Georges Basile Stavracas Neto
ca0156e9a7 background-content: Render background slices relative to actor box
The current code assumes that the actor will always have the same
size and position of the background texture, but part of the implicit
contract of being a ClutterContent is being able to render itself
at any given actor, at any given size.

For example, if the current code is given an actor with 0x0+100+100
as geometry, and no clipped region, it'll render not the whole
background, but the 0x0+100+100 rectangle of the background. In
practice, the actor geometry acts like a "clip mask" over the
background texture, due to the assumption that the actor will
always have the same size of the monitor.

Make the calculation of the texture slices relative to the actor
box.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1302
2020-06-09 17:07:02 -03:00
Georges Basile Stavracas Neto
6d75b4fc53 background-content: Simplify method call
It's always passing the same pipeline and texture rect, simplify
by passing the MetaBackgroundContent instance itself.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1302
2020-06-09 17:07:02 -03:00
Georges Basile Stavracas Neto
6bd382ad23 background-actor: Use MetaBackgroundContent
MetaBackgroundActor is still necessary for culling purposes,
but now the actual rendering of the background is delegated
to MetaBackgroundContent, as well as the sizing information.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1302
2020-06-09 17:07:02 -03:00
Georges Basile Stavracas Neto
a1b3d1a2a7 Introduce MetaBackgroundContent
MetaBackgroundContent is a ClutterContent implementation
that can render a background to any attached actor. Right
now, it preserves all the properties and the rendering
model of MetaBackgroundActor.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1302
2020-06-09 17:07:02 -03:00
Jonas Ådahl
53f61f3778 stack-tracker: Don't log warnings on race conditions
X11 window stacking operations are by nature prone to race conditions.
For example, we might queue a "raise above" operation, but before it
actually takes place, the sibling the window was to be rased above, is
withdrawn.

In these cases we'd log warnings even though they are expected to
happen. Downgrade these warnings to debug messages, only printed when
MUTTER_VERBOSE is set.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1300
2020-06-09 18:46:38 +00:00
Jonas Ådahl
74c0d9140c stack-tracker: Fix coding style of meta_stack_op_apply()
Change tabs to spaces, clean up variable declarations.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1300
2020-06-09 18:46:38 +00:00
Georges Basile Stavracas Neto
268336c21a clutter/actor: Allow animating content properties
ClutterActor allows animating effects, constraints, actions,
and the layout manager for property transitions. Extend this
functionality to the content as well.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1301
2020-06-09 15:09:26 -03:00
Jonas Dreßler
49e983b06e tests: Add a test for ClutterActors stage_views() API
Test that the stage-views list of ClutterActor is correct when moving an
actor, reparenting it, or hiding an actor up the hierarchy. Also test
that the "stage-views-changed" signal works as expected.

Don't test actor transforms for now because those aren't supported yet.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196
2020-06-09 16:07:46 +00:00
Jonas Dreßler
0f97196d84 clutter/actor: Don't traverse whole actor tree for updating stage-views
We currently go through the whole tree of mapped actors on every paint
cycle to update the stage views actors are on. Even if no actors need
updating of their stage views, traversing the actor tree is still quite
expensive and shows up when using a profiler.

So tone down the amounts of full-tree traversals we have to do on every
paint cycle and only traverse a subtree if it includes an actor which
actually needs updating of its stage views.

We do that by setting the `needs_update_stage_views` flag to TRUE
recursively for all parents up to the stage when the stage-views list of
an actor gets invalidated. This way we end up updating a few more actors
than necessary, but can avoid searching the whole actor tree for actors
which have `needs_update_stage_views` set to TRUE.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196
2020-06-09 16:07:46 +00:00
Jonas Dreßler
18c792d6f8 clutter/actor: Emit a signal when the stage-views an actor is on change
Add a new signal that's emitted when the stage views an actor being
painted on have changed, "stage-views-changed". For example this signal
can be helpful when tracking whether an actor is painted on multiple
stage views or only one.

Since we must clear the stage-views list when an actor leaves the stage
(actors that aren't attached to a stage don't get notified about the
stage views being changed/replaced), we also emit the new signal when an
actor gets detached from the stage (otherwise there would be an edge
case where no signal is emitted but it really should: An actor is
visible on a stage view, then detached from the stage, and then attached
again and immeditely moved outside the view).

Also skip the comparison of the old stage-views list and the new one if
nobody is listening to the signal to save some resources.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196
2020-06-09 16:07:46 +00:00
Jonas Dreßler
675a97d58e clutter/actor: Add API to get the stage-views an actor is painted on
There are certain rendering techniques and optimizations, for example
the unredirection of non-fullscreen windows, where information about the
output/stage-view an actor is on is needed to determine whether the
optimization can be enabled.

So add a new method to ClutterActor that allows listing the stage-views
the actor is being painted on: clutter_actor_peek_stage_views()

With the way Clutter works, the only point where we can reliably get
this information is during or right before the paint phase, when the
layout phase of the stage has been completed and no more changes to the
actors transformation matrices happen. So to get the stage views the
actor is on, introduce a new step that's done on every master clock tick
between layout and paint cycle: Traversing through the actor tree and
updating the stage-views the mapped actors are going to be painted on.

We're doing this in a separate step instead of inside
clutter_actor_paint() itself for a few reasons: It keeps the code
separate from the painting code, making profiling easier and issues
easier to track down (hopefully), it allows for a new
"stage-views-changed" signal that doesn't interfere with painting, and
finally, it will make it very easy to update the resource scales in the
same step in the future.

Currently, this list is only invalidated on allocation changes of
actors, but not on changes to the transformation matrices. That's
because there's no proper API to invalidate the transformation matrices
ClutterActor implementations can apply through the apply_transform()
vfunc.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196
2020-06-09 16:07:46 +00:00
Jonas Dreßler
8127494e52 clutter/stage: Rename update_resource_scales to clear_stage_views
When the stage views the stage is shown on are changed, ClutterStage
currently provides a clutter_stage_update_resource_scales() method
that allows invalidating the resource scales of all actors. With the new
stage-views API that's going to be added to ClutterActor, we also need a
method to invalidate the stage-views lists of actors in case the stage
views are rebuilt and fortunately we can re-use the infrastructure for
invalidating resource scales for that.

So since resource scales depend on the stage views an actor is on,
rename clutter_stage_update_resource_scales() and related methods to
clutter_stage_clear_stage_views(), which also covers resource scales.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196
2020-06-09 16:07:46 +00:00
Jonas Dreßler
670f4f22fe clutter/stage: Remove _clutter_stage_get_max_view_scale_factor_for_rect
Since we now have _clutter_stage_get_views_for_rect(), we can easily
replace _clutter_stage_get_max_view_scale_factor_for_rect() with it and
remove that private method.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196
2020-06-09 16:07:46 +00:00
Jonas Dreßler
e27d2702a8 clutter/stage: Add private method to get the stage-views of a rectangle
We'll need this method for implementing a new stage-views-on API for
ClutterActor which lists the stage views an actor is on.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196
2020-06-09 16:07:46 +00:00
Daniel van Vugt
dfe33897db meson_options: Use libGLESv2.so.2 for COGL_DRIVER=gles2, not libGLESv2.so
The former is present on any system that supports OpenGL|ES 2. The latter
is just provided in developer packages. Since we access the library via
`g_module_open` it's safe to just rely on `libGLESv2.so.2`.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1282

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1298
2020-06-09 18:32:01 +08:00
Jonas Dreßler
90c20b185b clutter/actor: Disconnect from "layout-changed" signal on destroy
While the layout manager of a ClutterActor does get properly unset when
destroying an actor, we currently forget to disconnect the
"layout-changed" signal from it.

So do that, and while at it, also switch to using the signal id for
disconnecting from the signal instead of
g_signal_handlers_disconnect_by_func(), which caused problems before
because it might traverse the signal handler list.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1281
2020-06-08 17:23:05 +00:00
Jonas Dreßler
4d7c8d68bc clutter/actor: Mark offscreen-redirect property as flag everywhere
We currently are confusing g_param_spec_enum and g_param_spec_flags for
the offscreen-redirect property of ClutterActor. Since it's actually a
flag, make it a flag everywhere.

Fun fact: This was already partly done with
d7814cf63e, but that commit missed the
setter.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1292
2020-06-08 15:25:11 +00:00
Jonas Dreßler
455de5d6d3 clutter/align-constraint: Listen to queue-relayout signal of source
Just like the ClutterBindConstraint, the ClutterAlignConstraint should
listen to "queue-relayout" of its source actor, not
"notify::allocation". That's because the latter will queue a relayout
during an allocation cycle and might cause relayout loops.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1296
2020-06-08 14:58:15 +00:00
Sebastian Keller
99c9f4c1fa wayland/data-device: Don't create and leak unused memory on dnd
"offer" is overwritten with the result of meta_wayland_data_offer_new a
few lines later.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1293
2020-06-08 12:11:11 +00:00
Sebastian Keller
dd32ff018a wayland: Free selection streams streams after transfer
They were only being closed but never freed.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1293
2020-06-08 12:11:11 +00:00
Sebastian Keller
019643bad0 core: Free clipboard selection source on shutdown
The clipboard manager is the only code to ever set the display selection
source, so it should also be responsible for unsetting it when the
clipboard manager gets shut down.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1293
2020-06-08 12:11:11 +00:00
Sebastian Keller
a031ac067e core: Fix memory selection source leak after clipboard owner disappears
When an app disappears after some data from it has been copied to the
clipboard, the owner of the clipboard selection becomes a new memory
selection source. The initial reference this new selection source is
never unref'ed, which leads to this being leaked on the next clipboard
selection owner change.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1293
2020-06-08 12:11:11 +00:00
Peter Hutterer
f712387325 compositor: use XDG_CONFIG_HOME as initial lookup path for xkb
Using XDG_CONFIG_HOME allows users to place their keyboard configuration into
their home directory and have them loaded automatically.
libxkbcommon now defaults to XDG_CONFIG_HOME/xkb/ first, see
https://github.com/xkbcommon/libxkbcommon/pull/117

However - libxkbcommon uses secure_getenv() to obtain XDG_CONFIG_HOME and thus
fails to load this for the mutter context which has cap_sys_nice.
We need to manually add that search path as lookup path.

As we can only append paths to libxkbcommon's context, we need to start with
an empty search path set, add our custom path, then append the default search
paths.

The net effect is nil where a user doesn't have XDG_CONFIG_HOME/xkb/.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/936
2020-06-08 11:29:30 +00:00
Jonas Ådahl
1eaf9e5f63 tests/clutter/timeline-interpolate: Maximize error tolerance
Simply to make it less flaky.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1291
2020-06-06 00:27:40 +02:00
Jonas Ådahl
7222bdde57 tests/clutter/timeline: Lower FPS even further
It's still flaky running in CI, lets run it even slower.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1291
2020-06-06 00:27:40 +02:00
Jonas Ådahl
007d27fa40 tests/conform: Use the clutter stage from mutter
The tests created their own stage, which caused various issues. Lets use
the one from mutter instead.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
01609de587 clutter/stage: Clear pick stack when hiding
Hiding a compositor stage is not something that's really supported, but
will still be used by tests, to get closer to a "fresh" stage for each
test case, when the tests eventually start using the mutter provided
stage.

It'll use that stage simply because creating standalone stages isn't
supported.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
83ee122fad cogl/frame-info: Stop passing the CoglOutput
It's unused, and if it would be, it'd be unreliable, as it'd only be
valid on the Xlib and GLX cogl backends.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
55302dbb38 cursor-renderer: Pass backend to constructor
Then use the backend passed instead of the global singleton.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
7876018755 backends/native: Get clutter backend from backend
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
e3c332fa10 backend: Remove unused freeze/thaw functions
They are no longer used, so remove them.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
faa831f2f6 backend: Remove cursor renderer construction fallback
All backends have their own cursor renderer constuctors, so remove the
unused fallback.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
1a915f06cf clutter: Remove CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD
It was unused, and will simplify things when we're without a master
clock.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
6754195580 clutter/script-parser: Don't skip construct parameters when constructing
The script parser only included G_PARAM_CONSTRUCT_ONLY parameters when
constructing objects. This causes issues if an object requires a
parameter to be set during construction, but may also change after. Fix
this by including G_PARAM_CONSTRUCT parameters when constructing script
objects as well.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
25f9406e69 compositor: Get the stage via the backend
We would get the MetaDisplay from the backend singleton before creating
the MetaCompositor, then in MetaCompositor, get the backend singleton
again to get the stage. To get rid of the extra singleton fetching, just
pass the backend the MetaCompositor constructors, and fetch the stage
directly from the backend everytime it's needed.

This also makes it available earlier than before, as we didn't set our
instance private stage pointer until the manage() call.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:27 +00:00
Jonas Ådahl
9fc428f2e7 clutter/stage: Remove unused function clutter_stage_ensure_redraw()
Not used by anything, and redraws are eventually not going to be stage
global anyway.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
a05dd813da clutter/timeline: Remove clutter_timeline_clone()
It was deprecated long ago, and users should switch to using te regular
constructors.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
08b30d6fe2 clutter/timeline: Remove deprecated 'loop' property
It was since long ago replaced by a 'repeat-count' property.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
84f55d38dd clutter/pan-action: Clean up line lengths
The file stood out and was hard to read because lines were very long
compared to the rest of clutter and mutter. Clean that up by cutting
lines into pieces where appropriate.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
75b5e079cf clutter/timeline: Add meaning to constructor argument variable name
Start follow the convention used in ClutterFrameClock by including the
meaning as well as time granularity in the variable name. The
constructor takes the intended duration of the constructed timeline in
milli seconds, so call the constructor argument `duration_ms`. This is
done in preparation for adding more constuctors.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
d742f9331c tests/clutter: Port timeline-rewind to current test suite
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
420ca31f0b tests/clutter: Port timeline-progress to current test suite
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
73da901cd3 tests/clutter: Port timeline-interpolate to current test suite
The error tolerance is increased dramatically to make the test less
flaky when running in CI.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
dd16fac0c7 tests/clutter: Port timeline tests to current test suite
Also fix a test that dependends on a specific element order in a list
that wasn't defined to have any particular order.

The frames per second is decreased from 30 to 10, to make the test less
flaky when running in CI.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
1b2af2891f tests/clutter/conform: Default to print test logs
To change to the old behavior, pass --quiet. The aim is to be make it
easier to debug issues only reproducing in the CI.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
050c21c24f tests/clutter/conform: Don't run tests in parallel
Might end up failing to acquire D-Bus names, resulting in warnings.
Avoid that by not running the tests in parallel.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1289
2020-06-05 21:39:26 +00:00
Jonas Ådahl
7b45de941b tests/test-client: Disable shadow for Wayland client too
The shadow was disabled for the X11 client as it was far to unreliable
when comparing sizes.

It seems that the Wayland backend has been somewhat unreliable as well,
where some race condition causing incorrect sizes thus a flaky test.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1288
2020-06-05 00:15:52 +02:00
Jonas Ådahl
8d84449941 test-runner: Wait before finding MetaWindow when showing
A "show" command calls gtk_window_show() and gdk_display_sync(), then
returns. This means that the X11 window objects are guaranteed to have
been created in the X11 server.

After that, the test runner will look up the window's associated
MetaWindow and wait for it to be shown.

What this doesn't account for is if mutter didn't get enough CPU time to
see the new window. When this happens, the 'default-size' stacking test
sometimes failed after hiding and showing the X11 window.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1288
2020-06-05 00:15:52 +02:00
99 changed files with 3351 additions and 1479 deletions

View File

@@ -313,7 +313,7 @@ void _clutter_actor_detach_clone
void _clutter_actor_queue_redraw_on_clones (ClutterActor *actor);
void _clutter_actor_queue_relayout_on_clones (ClutterActor *actor);
void _clutter_actor_queue_only_relayout (ClutterActor *actor);
void _clutter_actor_queue_update_resource_scale_recursive (ClutterActor *actor);
void clutter_actor_clear_stage_views_recursive (ClutterActor *actor);
gboolean _clutter_actor_get_real_resource_scale (ClutterActor *actor,
float *resource_scale);
@@ -321,6 +321,8 @@ gboolean _clutter_actor_get_real_resource_scale
ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self,
CoglTexture *texture);
void clutter_actor_update_stage_views (ClutterActor *self);
G_END_DECLS
#endif /* __CLUTTER_ACTOR_PRIVATE_H__ */

View File

@@ -446,7 +446,8 @@
* ]|
*
* - the initial `@` is mandatory
* - the `section` fragment can be one between "actions", "constraints" and "effects"
* - the `section` fragment can be one between "actions", "constraints", "content",
* and "effects"
* - the `meta-name` fragment is the name of the action, effect, or constraint, as
* specified by the #ClutterActorMeta:name property of #ClutterActorMeta
* - the `property-name` fragment is the name of the action, effect, or constraint
@@ -811,6 +812,9 @@ struct _ClutterActorPrivate
gulong resolution_changed_id;
gulong font_changed_id;
gulong layout_changed_id;
GList *stage_views;
/* bitfields: KEEP AT THE END */
@@ -854,6 +858,7 @@ struct _ClutterActorPrivate
guint had_effects_on_last_paint_volume_update : 1;
guint needs_compute_resource_scale : 1;
guint absolute_origin_changed : 1;
guint needs_update_stage_views : 1;
};
enum
@@ -1016,6 +1021,7 @@ enum
TRANSITIONS_COMPLETED,
TOUCH_EVENT,
TRANSITION_STOPPED,
STAGE_VIEWS_CHANGED,
LAST_SIGNAL
};
@@ -1613,6 +1619,22 @@ clutter_actor_update_map_state (ClutterActor *self,
#endif
}
static void
queue_update_stage_views (ClutterActor *actor)
{
while (actor && !actor->priv->needs_update_stage_views)
{
actor->priv->needs_update_stage_views = TRUE;
/* We don't really need to update the stage-views of the actors up the
* hierarchy, we set the flag anyway though so we can avoid traversing
* the whole scenegraph when looking for actors which need an update
* in clutter_actor_update_stage_views().
*/
actor = actor->priv->parent;
}
}
static void
clutter_actor_real_map (ClutterActor *self)
{
@@ -1627,6 +1649,18 @@ clutter_actor_real_map (ClutterActor *self)
self->priv->needs_paint_volume_update = TRUE;
/* We skip unmapped actors when updating the stage-views list, so if
* an actors list got invalidated while it was unmapped make sure to
* set priv->needs_update_stage_views to TRUE for all actors up the
* hierarchy now.
*/
if (self->priv->needs_update_stage_views)
{
/* Avoid the early return in queue_update_stage_views() */
self->priv->needs_update_stage_views = FALSE;
queue_update_stage_views (self);
}
clutter_actor_ensure_resource_scale (self);
/* notify on parent mapped before potentially mapping
@@ -2564,6 +2598,7 @@ static void
absolute_allocation_changed (ClutterActor *actor)
{
actor->priv->needs_compute_resource_scale = TRUE;
queue_update_stage_views (actor);
}
static ClutterActorTraverseVisitFlags
@@ -4281,6 +4316,7 @@ typedef enum
REMOVE_CHILD_FLUSH_QUEUE = 1 << 4,
REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5,
REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6,
REMOVE_CHILD_CLEAR_STAGE_VIEWS = 1 << 7,
/* default flags for public API */
REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
@@ -4289,14 +4325,16 @@ typedef enum
REMOVE_CHILD_EMIT_ACTOR_REMOVED |
REMOVE_CHILD_CHECK_STATE |
REMOVE_CHILD_FLUSH_QUEUE |
REMOVE_CHILD_NOTIFY_FIRST_LAST,
REMOVE_CHILD_NOTIFY_FIRST_LAST |
REMOVE_CHILD_CLEAR_STAGE_VIEWS,
/* flags for legacy/deprecated API */
REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
REMOVE_CHILD_CHECK_STATE |
REMOVE_CHILD_FLUSH_QUEUE |
REMOVE_CHILD_EMIT_PARENT_SET |
REMOVE_CHILD_NOTIFY_FIRST_LAST
REMOVE_CHILD_NOTIFY_FIRST_LAST |
REMOVE_CHILD_CLEAR_STAGE_VIEWS
} ClutterActorRemoveChildFlags;
/*< private >
@@ -4318,6 +4356,7 @@ clutter_actor_remove_child_internal (ClutterActor *self,
gboolean notify_first_last;
gboolean was_mapped;
gboolean stop_transitions;
gboolean clear_stage_views;
GObject *obj;
if (self == child)
@@ -4334,6 +4373,7 @@ clutter_actor_remove_child_internal (ClutterActor *self,
flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
clear_stage_views = (flags & REMOVE_CHILD_CLEAR_STAGE_VIEWS) != 0;
obj = G_OBJECT (self);
g_object_freeze_notify (obj);
@@ -4407,6 +4447,13 @@ clutter_actor_remove_child_internal (ClutterActor *self,
clutter_actor_queue_compute_expand (self);
}
/* Only actors which are attached to a stage get notified about changes
* to the stage views, so make sure all the stage-views lists are
* cleared as the child and its children leave the actor tree.
*/
if (clear_stage_views && !CLUTTER_ACTOR_IN_DESTRUCTION (child))
clutter_actor_clear_stage_views_recursive (child);
if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child))
{
child->priv->needs_compute_resource_scale = TRUE;
@@ -5197,7 +5244,7 @@ clutter_actor_set_property (GObject *object,
break;
case PROP_OFFSCREEN_REDIRECT:
clutter_actor_set_offscreen_redirect (actor, g_value_get_enum (value));
clutter_actor_set_offscreen_redirect (actor, g_value_get_flags (value));
break;
case PROP_NAME:
@@ -6062,6 +6109,7 @@ clutter_actor_dispose (GObject *object)
if (priv->layout_manager != NULL)
{
g_clear_signal_handler (&priv->layout_changed_id, priv->layout_manager);
clutter_layout_manager_set_container (priv->layout_manager, NULL);
g_clear_object (&priv->layout_manager);
}
@@ -6078,6 +6126,8 @@ clutter_actor_dispose (GObject *object)
priv->clones = NULL;
}
g_clear_pointer (&priv->stage_views, g_list_free);
G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
}
@@ -8614,6 +8664,27 @@ clutter_actor_class_init (ClutterActorClass *klass)
g_signal_set_va_marshaller (actor_signals[TOUCH_EVENT],
G_TYPE_FROM_CLASS (object_class),
_clutter_marshal_BOOLEAN__BOXEDv);
/**
* ClutterActor::stage-views-changed:
* @actor: a #ClutterActor
*
* The ::stage-views-changed signal is emitted when the position or
* size an actor is being painted at have changed so that it's visible
* on different stage views.
*
* This signal is also emitted when the actor gets detached from the stage
* or when the views of the stage have been invalidated and will be
* replaced; it's not emitted when the actor gets hidden.
*/
actor_signals[STAGE_VIEWS_CHANGED] =
g_signal_new (I_("stage-views-changed"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
@@ -8632,6 +8703,7 @@ clutter_actor_init (ClutterActor *self)
priv->needs_allocation = TRUE;
priv->needs_paint_volume_update = TRUE;
priv->needs_compute_resource_scale = TRUE;
priv->needs_update_stage_views = TRUE;
priv->cached_width_age = 1;
priv->cached_height_age = 1;
@@ -14551,6 +14623,37 @@ get_layout_from_animation_property (ClutterActor *actor,
return TRUE;
}
static gboolean
get_content_from_animation_property (ClutterActor *actor,
const gchar *name,
gchar **name_p)
{
g_auto (GStrv) tokens = NULL;
if (!g_str_has_prefix (name, "@content"))
return FALSE;
if (!actor->priv->content)
{
CLUTTER_NOTE (ANIMATION, "No ClutterContent available for '%s'",
name + 1);
return FALSE;
}
tokens = g_strsplit (name, ".", -1);
if (tokens == NULL || g_strv_length (tokens) != 2)
{
CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'",
name + 1);
return FALSE;
}
if (name_p != NULL)
*name_p = g_strdup (tokens[1]);
return TRUE;
}
static ClutterActorMeta *
get_meta_from_animation_property (ClutterActor *actor,
const gchar *name,
@@ -14618,6 +14721,7 @@ clutter_actor_find_property (ClutterAnimatable *animatable,
GObjectClass *klass = NULL;
GParamSpec *pspec = NULL;
gchar *p_name = NULL;
gboolean use_content = FALSE;
gboolean use_layout;
use_layout = get_layout_from_animation_property (actor,
@@ -14625,6 +14729,11 @@ clutter_actor_find_property (ClutterAnimatable *animatable,
&p_name);
if (!use_layout)
use_content = get_content_from_animation_property (actor,
property_name,
&p_name);
if (!use_layout && !use_content)
meta = get_meta_from_animation_property (actor,
property_name,
&p_name);
@@ -14639,6 +14748,12 @@ clutter_actor_find_property (ClutterAnimatable *animatable,
{
klass = G_OBJECT_GET_CLASS (actor->priv->layout_manager);
pspec = g_object_class_find_property (klass, p_name);
}
else if (use_content)
{
klass = G_OBJECT_GET_CLASS (actor->priv->content);
pspec = g_object_class_find_property (klass, p_name);
}
else
@@ -14661,6 +14776,7 @@ clutter_actor_get_initial_state (ClutterAnimatable *animatable,
ClutterActor *actor = CLUTTER_ACTOR (animatable);
ClutterActorMeta *meta = NULL;
gchar *p_name = NULL;
gboolean use_content = FALSE;
gboolean use_layout;
use_layout = get_layout_from_animation_property (actor,
@@ -14668,6 +14784,11 @@ clutter_actor_get_initial_state (ClutterAnimatable *animatable,
&p_name);
if (!use_layout)
use_content = get_content_from_animation_property (actor,
property_name,
&p_name);
if (!use_layout && !use_content)
meta = get_meta_from_animation_property (actor,
property_name,
&p_name);
@@ -14676,6 +14797,8 @@ clutter_actor_get_initial_state (ClutterAnimatable *animatable,
g_object_get_property (G_OBJECT (meta), p_name, initial);
else if (use_layout)
g_object_get_property (G_OBJECT (actor->priv->layout_manager), p_name, initial);
else if (use_content)
g_object_get_property (G_OBJECT (actor->priv->content), p_name, initial);
else
g_object_get_property (G_OBJECT (animatable), property_name, initial);
@@ -14826,6 +14949,7 @@ clutter_actor_set_final_state (ClutterAnimatable *animatable,
ClutterActor *actor = CLUTTER_ACTOR (animatable);
ClutterActorMeta *meta = NULL;
gchar *p_name = NULL;
gboolean use_content = FALSE;
gboolean use_layout;
use_layout = get_layout_from_animation_property (actor,
@@ -14833,6 +14957,11 @@ clutter_actor_set_final_state (ClutterAnimatable *animatable,
&p_name);
if (!use_layout)
use_content = get_content_from_animation_property (actor,
property_name,
&p_name);
if (!use_layout && !use_content)
meta = get_meta_from_animation_property (actor,
property_name,
&p_name);
@@ -14841,6 +14970,8 @@ clutter_actor_set_final_state (ClutterAnimatable *animatable,
g_object_set_property (G_OBJECT (meta), p_name, final);
else if (use_layout)
g_object_set_property (G_OBJECT (actor->priv->layout_manager), p_name, final);
else if (use_content)
g_object_set_property (G_OBJECT (actor->priv->content), p_name, final);
else
{
GObjectClass *obj_class = G_OBJECT_GET_CLASS (animatable);
@@ -17419,17 +17550,27 @@ _clutter_actor_get_resource_scale_for_rect (ClutterActor *self,
float *resource_scale)
{
ClutterActor *stage;
g_autoptr (GList) views = NULL;
GList *l;
float max_scale = 0;
stage = _clutter_actor_get_stage_internal (self);
if (!stage)
return FALSE;
if (!_clutter_stage_get_max_view_scale_factor_for_rect (CLUTTER_STAGE (stage),
bounding_rect,
&max_scale))
views = clutter_stage_get_views_for_rect (CLUTTER_STAGE (stage),
bounding_rect);
if (!views)
return FALSE;
for (l = views; l; l = l->next)
{
ClutterStageView *view = l->data;
max_scale = MAX (clutter_stage_view_get_scale (view), max_scale);
}
*resource_scale = max_scale;
return TRUE;
@@ -17505,20 +17646,29 @@ _clutter_actor_compute_resource_scale (ClutterActor *self,
}
static ClutterActorTraverseVisitFlags
queue_update_resource_scale_cb (ClutterActor *actor,
int depth,
void *user_data)
clear_stage_views_cb (ClutterActor *actor,
int depth,
gpointer user_data)
{
g_autoptr (GList) old_stage_views = NULL;
actor->priv->needs_update_stage_views = TRUE;
actor->priv->needs_compute_resource_scale = TRUE;
old_stage_views = g_steal_pointer (&actor->priv->stage_views);
if (old_stage_views)
g_signal_emit (actor, actor_signals[STAGE_VIEWS_CHANGED], 0);
return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
}
void
_clutter_actor_queue_update_resource_scale_recursive (ClutterActor *self)
clutter_actor_clear_stage_views_recursive (ClutterActor *self)
{
_clutter_actor_traverse (self,
CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
queue_update_resource_scale_cb,
clear_stage_views_cb,
NULL,
NULL);
}
@@ -17610,6 +17760,125 @@ clutter_actor_get_resource_scale (ClutterActor *self,
return FALSE;
}
static gboolean
sorted_lists_equal (GList *list_a,
GList *list_b)
{
GList *a, *b;
if (!list_a && !list_b)
return TRUE;
for (a = list_a, b = list_b;
a && b;
a = a->next, b = b->next)
{
if (a->data != b->data)
break;
if (!a->next && !b->next)
return TRUE;
}
return FALSE;
}
static void
update_stage_views (ClutterActor *self)
{
ClutterActorPrivate *priv = self->priv;
g_autoptr (GList) old_stage_views = NULL;
ClutterStage *stage;
graphene_rect_t bounding_rect;
old_stage_views = g_steal_pointer (&priv->stage_views);
if (priv->needs_allocation)
{
g_warning ("Can't update stage views actor %s is on because it needs an "
"allocation.", _clutter_actor_get_debug_name (self));
goto out;
}
stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
g_return_if_fail (stage);
clutter_actor_get_transformed_position (self,
&bounding_rect.origin.x,
&bounding_rect.origin.y);
clutter_actor_get_transformed_size (self,
&bounding_rect.size.width,
&bounding_rect.size.height);
if (bounding_rect.size.width == 0.0 ||
bounding_rect.size.height == 0.0)
goto out;
priv->stage_views = clutter_stage_get_views_for_rect (stage,
&bounding_rect);
out:
if (g_signal_has_handler_pending (self, actor_signals[STAGE_VIEWS_CHANGED],
0, TRUE))
{
if (!sorted_lists_equal (old_stage_views, priv->stage_views))
g_signal_emit (self, actor_signals[STAGE_VIEWS_CHANGED], 0);
}
}
void
clutter_actor_update_stage_views (ClutterActor *self)
{
ClutterActorPrivate *priv = self->priv;
ClutterActor *child;
if (!CLUTTER_ACTOR_IS_MAPPED (self) ||
CLUTTER_ACTOR_IN_DESTRUCTION (self))
return;
if (!priv->needs_update_stage_views)
return;
update_stage_views (self);
priv->needs_update_stage_views = FALSE;
for (child = priv->first_child; child; child = child->priv->next_sibling)
clutter_actor_update_stage_views (child);
}
/**
* clutter_actor_peek_stage_views:
* @self: A #ClutterActor
*
* Retrieves the list of #ClutterStageView<!-- -->s the actor is being
* painted on.
*
* If this function is called during the paint cycle, the list is guaranteed
* to be up-to-date, if called outside the paint cycle, the list will
* contain the views the actor was painted on last.
*
* The list returned by this function is not updated when the actors
* visibility changes: If an actor gets hidden and is not being painted
* anymore, this function will return the list of views the actor was
* painted on last.
*
* If an actor is not attached to a stage (realized), this function will
* always return an empty list.
*
* Returns: (transfer none) (element-type Clutter.StageView): The list of
* #ClutterStageView<!-- -->s the actor is being painted on. The list and
* its contents are owned by the #ClutterActor and the list may not be
* freed or modified.
*/
GList *
clutter_actor_peek_stage_views (ClutterActor *self)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
return self->priv->stage_views;
}
/**
* clutter_actor_has_overlaps:
* @self: A #ClutterActor
@@ -17971,9 +18240,7 @@ clutter_actor_set_layout_manager (ClutterActor *self,
if (priv->layout_manager != NULL)
{
g_signal_handlers_disconnect_by_func (priv->layout_manager,
G_CALLBACK (on_layout_manager_changed),
self);
g_clear_signal_handler (&priv->layout_changed_id, priv->layout_manager);
clutter_layout_manager_set_container (priv->layout_manager, NULL);
g_clear_object (&priv->layout_manager);
}
@@ -17985,9 +18252,10 @@ clutter_actor_set_layout_manager (ClutterActor *self,
g_object_ref_sink (priv->layout_manager);
clutter_layout_manager_set_container (priv->layout_manager,
CLUTTER_CONTAINER (self));
g_signal_connect (priv->layout_manager, "layout-changed",
G_CALLBACK (on_layout_manager_changed),
self);
priv->layout_changed_id =
g_signal_connect (priv->layout_manager, "layout-changed",
G_CALLBACK (on_layout_manager_changed),
self);
}
clutter_actor_queue_relayout (self);

View File

@@ -919,6 +919,9 @@ void clutter_actor_pick_box (ClutterActor *self,
ClutterPickContext *pick_context,
const ClutterActorBox *box);
CLUTTER_EXPORT
GList * clutter_actor_peek_stage_views (ClutterActor *self);
G_END_DECLS
#endif /* __CLUTTER_ACTOR_H__ */

View File

@@ -84,12 +84,11 @@ G_DEFINE_TYPE (ClutterAlignConstraint,
CLUTTER_TYPE_CONSTRAINT);
static void
source_position_changed (ClutterActor *actor,
GParamSpec *pspec,
ClutterAlignConstraint *align)
source_queue_relayout (ClutterActor *actor,
ClutterAlignConstraint *align)
{
if (align->actor != NULL)
clutter_actor_queue_relayout (align->actor);
_clutter_actor_queue_only_relayout (align->actor);
}
static void
@@ -186,7 +185,7 @@ clutter_align_constraint_dispose (GObject *gobject)
G_CALLBACK (source_destroyed),
align);
g_signal_handlers_disconnect_by_func (align->source,
G_CALLBACK (source_position_changed),
G_CALLBACK (source_queue_relayout),
align);
align->source = NULL;
}
@@ -402,15 +401,15 @@ clutter_align_constraint_set_source (ClutterAlignConstraint *align,
G_CALLBACK (source_destroyed),
align);
g_signal_handlers_disconnect_by_func (old_source,
G_CALLBACK (source_position_changed),
G_CALLBACK (source_queue_relayout),
align);
}
align->source = source;
if (align->source != NULL)
{
g_signal_connect (align->source, "notify::allocation",
G_CALLBACK (source_position_changed),
g_signal_connect (align->source, "queue-relayout",
G_CALLBACK (source_queue_relayout),
align);
g_signal_connect (align->source, "destroy",
G_CALLBACK (source_destroyed),

View File

@@ -1317,8 +1317,6 @@ typedef enum
* painting the stages
* @CLUTTER_REPAINT_FLAGS_POST_PAINT: Run the repaint function after
* painting the stages
* @CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD: Ensure that a new frame
* is queued after adding the repaint function
*
* Flags to pass to clutter_threads_add_repaint_func_full().
*
@@ -1328,7 +1326,6 @@ typedef enum
{
CLUTTER_REPAINT_FLAGS_PRE_PAINT = 1 << 0,
CLUTTER_REPAINT_FLAGS_POST_PAINT = 1 << 1,
CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD = 1 << 2
} ClutterRepaintFlags;
/**

View File

@@ -2392,8 +2392,7 @@ clutter_threads_add_repaint_func_full (ClutterRepaintFlags flags,
repaint_func->id = context->last_repaint_id++;
/* mask out QUEUE_REDRAW_ON_ADD, since we're going to consume it */
repaint_func->flags = flags & ~CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD;
repaint_func->flags = flags;
repaint_func->func = func;
repaint_func->data = data;
repaint_func->notify = notify;
@@ -2403,13 +2402,6 @@ clutter_threads_add_repaint_func_full (ClutterRepaintFlags flags,
_clutter_context_unlock ();
if ((flags & CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD) != 0)
{
ClutterMasterClock *master_clock = _clutter_master_clock_get_default ();
_clutter_master_clock_ensure_next_iteration (master_clock);
}
return repaint_func->id;
}

View File

@@ -75,7 +75,7 @@ CLUTTER_EXPORT
void clutter_stage_thaw_updates (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_update_resource_scales (ClutterStage *stage);
void clutter_stage_clear_stage_views (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_view_assign_next_scanout (ClutterStageView *stage_view,

View File

@@ -136,7 +136,8 @@ enum
static guint pan_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE_WITH_PRIVATE (ClutterPanAction, clutter_pan_action, CLUTTER_TYPE_GESTURE_ACTION)
G_DEFINE_TYPE_WITH_PRIVATE (ClutterPanAction, clutter_pan_action,
CLUTTER_TYPE_GESTURE_ACTION)
static void
emit_pan (ClutterPanAction *self,
@@ -156,14 +157,18 @@ emit_pan (ClutterPanAction *self,
gfloat scroll_threshold = G_PI_4/2;
gfloat drag_angle;
clutter_gesture_action_get_motion_delta (CLUTTER_GESTURE_ACTION (self), 0, &delta_x, &delta_y);
clutter_gesture_action_get_motion_delta (CLUTTER_GESTURE_ACTION (self),
0,
&delta_x,
&delta_y);
if (delta_x != 0.0f)
drag_angle = atanf (delta_y / delta_x);
else
drag_angle = G_PI_2;
if ((drag_angle > -scroll_threshold) && (drag_angle < scroll_threshold))
if ((drag_angle > -scroll_threshold) &&
(drag_angle < scroll_threshold))
priv->pin_state = SCROLL_PINNED_HORIZONTAL;
else if ((drag_angle > (G_PI_2 - scroll_threshold)) ||
(drag_angle < -(G_PI_2 - scroll_threshold)))
@@ -282,7 +287,10 @@ gesture_end (ClutterGestureAction *gesture,
gfloat tau;
gint duration;
clutter_gesture_action_get_release_coords (CLUTTER_GESTURE_ACTION (self), 0, &priv->release_x, &priv->release_y);
clutter_gesture_action_get_release_coords (CLUTTER_GESTURE_ACTION (self),
0,
&priv->release_x,
&priv->release_y);
if (!priv->should_interpolate)
{
@@ -293,7 +301,9 @@ gesture_end (ClutterGestureAction *gesture,
priv->state = PAN_STATE_INTERPOLATING;
clutter_gesture_action_get_motion_delta (gesture, 0, &delta_x, &delta_y);
velocity = clutter_gesture_action_get_velocity (gesture, 0, &velocity_x, &velocity_y);
velocity = clutter_gesture_action_get_velocity (gesture, 0,
&velocity_x,
&velocity_y);
/* Exponential timing constant v(t) = v(0) * exp(-t/tau)
* tau = 1000ms / (frame_per_second * - ln(decay_per_frame))
@@ -304,17 +314,22 @@ gesture_end (ClutterGestureAction *gesture,
/* See where the decreasing velocity reaches $min_velocity px/ms
* v(t) = v(0) * exp(-t/tau) = min_velocity
* t = - tau * ln( min_velocity / |v(0)|) */
duration = - tau * logf (min_velocity / (ABS (velocity) * priv->acceleration_factor));
duration = - tau * logf (min_velocity / (ABS (velocity) *
priv->acceleration_factor));
/* Target point: x(t) = v(0) * tau * [1 - exp(-t/tau)] */
priv->target_x = velocity_x * priv->acceleration_factor * tau * (1 - exp ((float)-duration / tau));
priv->target_y = velocity_y * priv->acceleration_factor * tau * (1 - exp ((float)-duration / tau));
priv->target_x = (velocity_x * priv->acceleration_factor * tau *
(1 - exp ((float)-duration / tau)));
priv->target_y = (velocity_y * priv->acceleration_factor * tau *
(1 - exp ((float)-duration / tau)));
if (ABS (velocity) * priv->acceleration_factor > min_velocity && duration > FLOAT_EPSILON)
if (ABS (velocity) * priv->acceleration_factor > min_velocity &&
duration > FLOAT_EPSILON)
{
priv->interpolated_x = priv->interpolated_y = 0.0f;
priv->deceleration_timeline = clutter_timeline_new (duration);
clutter_timeline_set_progress_mode (priv->deceleration_timeline, CLUTTER_EASE_OUT_EXPO);
clutter_timeline_set_progress_mode (priv->deceleration_timeline,
CLUTTER_EASE_OUT_EXPO);
g_signal_connect (priv->deceleration_timeline, "new_frame",
G_CALLBACK (on_deceleration_new_frame), self);
@@ -367,7 +382,8 @@ clutter_pan_action_set_property (GObject *gobject,
break;
case PROP_ACCELERATION_FACTOR :
clutter_pan_action_set_acceleration_factor (self, g_value_get_double (value));
clutter_pan_action_set_acceleration_factor (self,
g_value_get_double (value));
break;
default:
@@ -411,9 +427,11 @@ static void
clutter_pan_action_constructed (GObject *gobject)
{
ClutterGestureAction *gesture;
ClutterGestureTriggerEdge edge;
gesture = CLUTTER_GESTURE_ACTION (gobject);
clutter_gesture_action_set_threshold_trigger_edge (gesture, CLUTTER_GESTURE_TRIGGER_EDGE_AFTER);
edge = CLUTTER_GESTURE_TRIGGER_EDGE_AFTER;
clutter_gesture_action_set_threshold_trigger_edge (gesture, edge);
}
static void
@@ -442,7 +460,8 @@ clutter_pan_action_set_actor (ClutterActorMeta *meta,
g_clear_object (&priv->deceleration_timeline);
}
CLUTTER_ACTOR_META_CLASS (clutter_pan_action_parent_class)->set_actor (meta, actor);
CLUTTER_ACTOR_META_CLASS (clutter_pan_action_parent_class)->set_actor (meta,
actor);
}
@@ -880,7 +899,9 @@ clutter_pan_action_get_constrained_motion_delta (ClutterPanAction *self,
priv = self->priv;
distance = clutter_pan_action_get_motion_delta (self, point, &delta_x, &delta_y);
distance = clutter_pan_action_get_motion_delta (self, point,
&delta_x,
&delta_y);
switch (priv->pan_axis)
{

View File

@@ -1523,7 +1523,7 @@ clutter_script_construct_parameters (ClutterScript *script,
continue;
}
if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY))
if (!(pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)))
{
unparsed = g_list_prepend (unparsed, pinfo);
continue;

View File

@@ -125,9 +125,6 @@ gboolean _clutter_stage_update_state (ClutterStage *stag
void _clutter_stage_set_scale_factor (ClutterStage *stage,
int factor);
gboolean _clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage,
graphene_rect_t *rect,
float *view_scale);
void _clutter_stage_presented (ClutterStage *stage,
CoglFrameEvent frame_event,
@@ -136,6 +133,9 @@ void _clutter_stage_presented (ClutterStage *stag
void clutter_stage_queue_actor_relayout (ClutterStage *stage,
ClutterActor *actor);
GList * clutter_stage_get_views_for_rect (ClutterStage *stage,
const graphene_rect_t *rect);
G_END_DECLS
#endif /* __CLUTTER_STAGE_PRIVATE_H__ */

View File

@@ -1071,6 +1071,7 @@ clutter_stage_hide (ClutterActor *self)
ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv;
g_assert (priv->impl != NULL);
_clutter_stage_clear_pick_stack (CLUTTER_STAGE (self));
_clutter_stage_window_hide (priv->impl);
CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->hide (self);
@@ -1468,6 +1469,14 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage)
return updating;
}
static void
update_actor_stage_views (ClutterStage *stage)
{
ClutterActor *actor = CLUTTER_ACTOR (stage);
clutter_actor_update_stage_views (actor);
}
/**
* _clutter_stage_do_update:
* @stage: A #ClutterStage
@@ -1515,6 +1524,10 @@ _clutter_stage_do_update (ClutterStage *stage)
if (stage_was_relayout)
pointers = _clutter_stage_check_updated_pointers (stage);
COGL_TRACE_BEGIN (ClutterStageUpdateActorStageViews, "Actor stage-views");
update_actor_stage_views (stage);
COGL_TRACE_END (ClutterStageUpdateActorStageViews);
COGL_TRACE_BEGIN (ClutterStagePaint, "Paint");
clutter_stage_maybe_finish_queue_redraws (stage);
@@ -3171,37 +3184,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage,
#undef _DEG_TO_RAD
/**
* clutter_stage_ensure_redraw:
* @stage: a #ClutterStage
*
* Ensures that @stage is redrawn
*
* This function should not be called by applications: it is
* used when embedding a #ClutterStage into a toolkit with
* another windowing system, like GTK+.
*
* Since: 1.0
*/
void
clutter_stage_ensure_redraw (ClutterStage *stage)
{
ClutterMasterClock *master_clock;
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
if (!_clutter_stage_needs_update (stage))
clutter_stage_schedule_update (stage);
priv->redraw_pending = TRUE;
master_clock = _clutter_master_clock_get_default ();
_clutter_master_clock_start_running (master_clock);
}
/**
* clutter_stage_is_redraw_queued: (skip)
*/
@@ -4107,20 +4089,29 @@ clutter_stage_get_capture_final_size (ClutterStage *stage,
int *out_height,
float *out_scale)
{
float max_scale;
float max_scale = 1.0;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
if (rect)
{
graphene_rect_t capture_rect;
g_autoptr (GList) views = NULL;
GList *l;
_clutter_util_rect_from_rectangle (rect, &capture_rect);
if (!_clutter_stage_get_max_view_scale_factor_for_rect (stage,
&capture_rect,
&max_scale))
views = clutter_stage_get_views_for_rect (stage, &capture_rect);
if (!views)
return FALSE;
for (l = views; l; l = l->next)
{
ClutterStageView *view = l->data;
max_scale = MAX (clutter_stage_view_get_scale (view), max_scale);
}
if (out_width)
*out_width = (gint) roundf (rect->width * max_scale);
@@ -4401,18 +4392,17 @@ clutter_stage_peek_stage_views (ClutterStage *stage)
}
void
clutter_stage_update_resource_scales (ClutterStage *stage)
clutter_stage_clear_stage_views (ClutterStage *stage)
{
_clutter_actor_queue_update_resource_scale_recursive (CLUTTER_ACTOR (stage));
clutter_actor_clear_stage_views_recursive (CLUTTER_ACTOR (stage));
}
gboolean
_clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage,
graphene_rect_t *rect,
float *view_scale)
GList *
clutter_stage_get_views_for_rect (ClutterStage *stage,
const graphene_rect_t *rect)
{
ClutterStagePrivate *priv = stage->priv;
float scale = 0.0f;
GList *views_for_rect = NULL;
GList *l;
for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
@@ -4425,12 +4415,8 @@ _clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage,
_clutter_util_rect_from_rectangle (&view_layout, &view_rect);
if (graphene_rect_intersection (&view_rect, rect, NULL))
scale = MAX (clutter_stage_view_get_scale (view), scale);
views_for_rect = g_list_prepend (views_for_rect, view);
}
if (scale == 0.0)
return FALSE;
*view_scale = scale;
return TRUE;
return views_for_rect;
}

View File

@@ -195,8 +195,6 @@ guchar * clutter_stage_read_pixels (ClutterStage
CLUTTER_EXPORT
void clutter_stage_ensure_viewport (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_ensure_redraw (ClutterStage *stage);
CLUTTER_EXPORT
gboolean clutter_stage_is_redraw_queued (ClutterStage *stage);

View File

@@ -172,7 +172,6 @@ enum
{
PROP_0,
PROP_LOOP,
PROP_DELAY,
PROP_DURATION,
PROP_DIRECTION,
@@ -290,23 +289,6 @@ clutter_timeline_add_marker_internal (ClutterTimeline *timeline,
g_hash_table_insert (priv->markers_by_name, marker->name, marker);
}
static inline void
clutter_timeline_set_loop_internal (ClutterTimeline *timeline,
gboolean loop)
{
gint old_repeat_count;
old_repeat_count = timeline->priv->repeat_count;
if (loop)
clutter_timeline_set_repeat_count (timeline, -1);
else
clutter_timeline_set_repeat_count (timeline, 0);
if (old_repeat_count != timeline->priv->repeat_count)
g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_LOOP]);
}
/* Scriptable */
typedef struct _ParseClosure {
ClutterTimeline *timeline;
@@ -448,10 +430,6 @@ clutter_timeline_set_property (GObject *object,
switch (prop_id)
{
case PROP_LOOP:
clutter_timeline_set_loop_internal (timeline, g_value_get_boolean (value));
break;
case PROP_DELAY:
clutter_timeline_set_delay (timeline, g_value_get_uint (value));
break;
@@ -493,10 +471,6 @@ clutter_timeline_get_property (GObject *object,
switch (prop_id)
{
case PROP_LOOP:
g_value_set_boolean (value, priv->repeat_count != 0);
break;
case PROP_DELAY:
g_value_set_uint (value, priv->delay);
break;
@@ -572,25 +546,6 @@ clutter_timeline_class_init (ClutterTimelineClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
/**
* ClutterTimeline:loop:
*
* Whether the timeline should automatically rewind and restart.
*
* As a side effect, setting this property to %TRUE will set the
* #ClutterTimeline:repeat-count property to -1, while setting this
* property to %FALSE will set the #ClutterTimeline:repeat-count
* property to 0.
*
* Deprecated: 1.10: Use the #ClutterTimeline:repeat-count property instead.
*/
obj_props[PROP_LOOP] =
g_param_spec_boolean ("loop",
P_("Loop"),
P_("Should the timeline automatically restart"),
FALSE,
CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED);
/**
* ClutterTimeline:delay:
*
@@ -1251,45 +1206,6 @@ clutter_timeline_stop (ClutterTimeline *timeline)
g_signal_emit (timeline, timeline_signals[STOPPED], 0, FALSE);
}
/**
* clutter_timeline_set_loop:
* @timeline: a #ClutterTimeline
* @loop: %TRUE for enable looping
*
* Sets whether @timeline should loop.
*
* This function is equivalent to calling clutter_timeline_set_repeat_count()
* with -1 if @loop is %TRUE, and with 0 if @loop is %FALSE.
*
* Deprecated: 1.10: Use clutter_timeline_set_repeat_count() instead.
*/
void
clutter_timeline_set_loop (ClutterTimeline *timeline,
gboolean loop)
{
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
clutter_timeline_set_loop_internal (timeline, loop);
}
/**
* clutter_timeline_get_loop:
* @timeline: a #ClutterTimeline
*
* Gets whether @timeline is looping
*
* Return value: %TRUE if the timeline is looping
*
* Deprecated: 1.10: Use clutter_timeline_get_repeat_count() instead.
*/
gboolean
clutter_timeline_get_loop (ClutterTimeline *timeline)
{
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE);
return timeline->priv->repeat_count != 0;
}
/**
* clutter_timeline_rewind:
* @timeline: A #ClutterTimeline
@@ -1405,49 +1321,11 @@ clutter_timeline_is_playing (ClutterTimeline *timeline)
return timeline->priv->is_playing;
}
/**
* clutter_timeline_clone:
* @timeline: #ClutterTimeline to duplicate.
*
* Create a new #ClutterTimeline instance which has property values
* matching that of supplied timeline. The cloned timeline will not
* be started and will not be positioned to the current position of
* the original @timeline: you will have to start it with
* clutter_timeline_start().
*
* The only cloned properties are:
*
* - #ClutterTimeline:duration
* - #ClutterTimeline:loop
* - #ClutterTimeline:delay
* - #ClutterTimeline:direction
*
* Return value: (transfer full): a new #ClutterTimeline, cloned
* from @timeline
*
* Since: 0.4
*
* Deprecated: 1.10: Use clutter_timeline_new() or g_object_new()
* instead
*/
ClutterTimeline *
clutter_timeline_clone (ClutterTimeline *timeline)
{
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
return g_object_new (CLUTTER_TYPE_TIMELINE,
"duration", timeline->priv->duration,
"loop", timeline->priv->repeat_count != 0,
"delay", timeline->priv->delay,
"direction", timeline->priv->direction,
NULL);
}
/**
* clutter_timeline_new:
* @msecs: Duration of the timeline in milliseconds
* @duration_ms: Duration of the timeline in milliseconds
*
* Creates a new #ClutterTimeline with a duration of @msecs.
* Creates a new #ClutterTimeline with a duration of @duration_ms milli seconds.
*
* Return value: the newly created #ClutterTimeline instance. Use
* g_object_unref() when done using it
@@ -1455,10 +1333,10 @@ clutter_timeline_clone (ClutterTimeline *timeline)
* Since: 0.6
*/
ClutterTimeline *
clutter_timeline_new (guint msecs)
clutter_timeline_new (guint duration_ms)
{
return g_object_new (CLUTTER_TYPE_TIMELINE,
"duration", msecs,
"duration", duration_ms,
NULL);
}

View File

@@ -119,7 +119,7 @@ CLUTTER_EXPORT
GType clutter_timeline_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterTimeline * clutter_timeline_new (guint msecs);
ClutterTimeline * clutter_timeline_new (guint duration_ms);
CLUTTER_EXPORT
guint clutter_timeline_get_duration (ClutterTimeline *timeline);

View File

@@ -26,16 +26,6 @@
G_BEGIN_DECLS
CLUTTER_DEPRECATED_FOR(clutter_timeline_new)
ClutterTimeline * clutter_timeline_clone (ClutterTimeline *timeline);
CLUTTER_DEPRECATED_FOR(clutter_timeline_set_repeat_count)
void clutter_timeline_set_loop (ClutterTimeline *timeline,
gboolean loop);
CLUTTER_DEPRECATED_FOR(clutter_timeline_get_repeat_count)
gboolean clutter_timeline_get_loop (ClutterTimeline *timeline);
G_END_DECLS
#endif /* __CLUTTER_TIMELINE_PRIVATE_H__ */

View File

@@ -43,8 +43,6 @@ struct _CoglFrameInfo
float refresh_rate;
int64_t global_frame_counter;
CoglOutput *output;
};
CoglFrameInfo *_cogl_frame_info_new (void);

View File

@@ -72,12 +72,6 @@ cogl_frame_info_get_refresh_rate (CoglFrameInfo *info)
return info->refresh_rate;
}
CoglOutput *
cogl_frame_info_get_output (CoglFrameInfo *info)
{
return info->output;
}
int64_t
cogl_frame_info_get_global_frame_counter (CoglFrameInfo *info)
{

View File

@@ -126,20 +126,6 @@ int64_t cogl_frame_info_get_presentation_time (CoglFrameInfo *info);
COGL_EXPORT
float cogl_frame_info_get_refresh_rate (CoglFrameInfo *info);
/**
* cogl_frame_info_get_output:
* @info: a #CoglFrameInfo object
*
* Gets the #CoglOutput that the swapped frame was presented to.
*
* Return value: (transfer none): The #CoglOutput that the frame was
* presented to, or %NULL if this could not be determined.
* Since: 1.14
* Stability: unstable
*/
COGL_EXPORT CoglOutput *
cogl_frame_info_get_output (CoglFrameInfo *info);
/**
* cogl_frame_info_get_global_frame_counter: (skip)
*/

View File

@@ -1662,8 +1662,6 @@ set_frame_info_output (CoglOnscreen *onscreen,
{
CoglFrameInfo *info = g_queue_peek_tail (&onscreen->pending_frame_infos);
info->output = output;
if (output)
{
float refresh_rate = cogl_output_get_refresh_rate (output);

View File

@@ -12,7 +12,7 @@ option('opengl_libname',
option('gles2_libname',
type: 'string',
value: 'libGLESv2.so',
value: 'libGLESv2.so.2',
description: 'GLESv2 library file name'
)

View File

@@ -156,10 +156,6 @@ xkb_layout_index_t meta_backend_get_keymap_layout_group (MetaBackend *backend);
gboolean meta_backend_is_lid_closed (MetaBackend *backend);
void meta_backend_freeze_updates (MetaBackend *backend);
void meta_backend_thaw_updates (MetaBackend *backend);
void meta_backend_update_last_device (MetaBackend *backend,
ClutterInputDevice *device);

View File

@@ -21,6 +21,8 @@
#ifndef META_BACKEND_TYPE_H
#define META_BACKEND_TYPE_H
typedef struct _MetaBackend MetaBackend;
typedef struct _MetaMonitorManager MetaMonitorManager;
typedef struct _MetaMonitorConfigManager MetaMonitorConfigManager;

View File

@@ -575,12 +575,6 @@ meta_backend_real_post_init (MetaBackend *backend)
}
}
static MetaCursorRenderer *
meta_backend_real_create_cursor_renderer (MetaBackend *backend)
{
return meta_cursor_renderer_new ();
}
static gboolean
meta_backend_real_grab_device (MetaBackend *backend,
int device_id,
@@ -762,7 +756,6 @@ meta_backend_class_init (MetaBackendClass *klass)
object_class->constructed = meta_backend_constructed;
klass->post_init = meta_backend_real_post_init;
klass->create_cursor_renderer = meta_backend_real_create_cursor_renderer;
klass->grab_device = meta_backend_real_grab_device;
klass->ungrab_device = meta_backend_real_ungrab_device;
klass->select_stage_events = meta_backend_real_select_stage_events;
@@ -1275,24 +1268,6 @@ meta_backend_get_stage (MetaBackend *backend)
return priv->stage;
}
void
meta_backend_freeze_updates (MetaBackend *backend)
{
ClutterStage *stage;
stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
clutter_stage_freeze_updates (stage);
}
void
meta_backend_thaw_updates (MetaBackend *backend)
{
ClutterStage *stage;
stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
clutter_stage_thaw_updates (stage);
}
static gboolean
update_last_device (MetaBackend *backend)
{

View File

@@ -38,8 +38,21 @@
G_DEFINE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor,
G_TYPE_OBJECT)
enum
{
PROP_0,
PROP_BACKEND,
N_PROPS
};
static GParamSpec *obj_props[N_PROPS];
struct _MetaCursorRendererPrivate
{
MetaBackend *backend;
float current_x;
float current_y;
@@ -89,8 +102,7 @@ align_cursor_position (MetaCursorRenderer *renderer,
{
MetaCursorRendererPrivate *priv =
meta_cursor_renderer_get_instance_private (renderer);
MetaBackend *backend = meta_get_backend ();
ClutterActor *stage = meta_backend_get_stage (backend);
ClutterActor *stage = meta_backend_get_stage (priv->backend);
ClutterStageView *view;
cairo_rectangle_int_t view_layout;
float view_scale;
@@ -115,8 +127,7 @@ queue_redraw (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{
MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
MetaBackend *backend = meta_get_backend ();
ClutterActor *stage = meta_backend_get_stage (backend);
ClutterActor *stage = meta_backend_get_stage (priv->backend);
CoglTexture *texture;
graphene_rect_t rect = GRAPHENE_RECT_INIT_ZERO;
@@ -165,13 +176,54 @@ meta_cursor_renderer_real_update_cursor (MetaCursorRenderer *renderer,
return FALSE;
}
static void
meta_cursor_renderer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaCursorRenderer *renderer = META_CURSOR_RENDERER (object);
MetaCursorRendererPrivate *priv =
meta_cursor_renderer_get_instance_private (renderer);
switch (prop_id)
{
case PROP_BACKEND:
g_value_set_object (value, priv->backend);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_cursor_renderer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaCursorRenderer *renderer = META_CURSOR_RENDERER (object);
MetaCursorRendererPrivate *priv =
meta_cursor_renderer_get_instance_private (renderer);
switch (prop_id)
{
case PROP_BACKEND:
priv->backend = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_cursor_renderer_finalize (GObject *object)
{
MetaCursorRenderer *renderer = META_CURSOR_RENDERER (object);
MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer);
MetaBackend *backend = meta_get_backend ();
ClutterActor *stage = meta_backend_get_stage (backend);
ClutterActor *stage = meta_backend_get_stage (priv->backend);
if (priv->stage_overlay)
meta_stage_remove_cursor_overlay (META_STAGE (stage), priv->stage_overlay);
@@ -186,9 +238,21 @@ meta_cursor_renderer_class_init (MetaCursorRendererClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = meta_cursor_renderer_get_property;
object_class->set_property = meta_cursor_renderer_set_property;
object_class->finalize = meta_cursor_renderer_finalize;
klass->update_cursor = meta_cursor_renderer_real_update_cursor;
obj_props[PROP_BACKEND] =
g_param_spec_object ("backend",
"backend",
"MetaBackend",
META_TYPE_BACKEND,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, obj_props);
signals[CURSOR_PAINTED] = g_signal_new ("cursor-painted",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
@@ -273,9 +337,11 @@ meta_cursor_renderer_update_cursor (MetaCursorRenderer *renderer,
}
MetaCursorRenderer *
meta_cursor_renderer_new (void)
meta_cursor_renderer_new (MetaBackend *backend)
{
return g_object_new (META_TYPE_CURSOR_RENDERER, NULL);
return g_object_new (META_TYPE_CURSOR_RENDERER,
"backend", backend,
NULL);
}
void

View File

@@ -54,7 +54,7 @@ struct _MetaCursorRendererClass
MetaCursorSprite *cursor_sprite);
};
MetaCursorRenderer * meta_cursor_renderer_new (void);
MetaCursorRenderer * meta_cursor_renderer_new (MetaBackend *backend);
void meta_cursor_renderer_set_cursor (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite);

View File

@@ -0,0 +1,56 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Utilities for use with libxkbcommon
*
* Copyright 2019 Red Hat, Inc.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "backends/meta-keymap-utils.h"
#include <glib.h>
#include <limits.h>
struct xkb_context *
meta_create_xkb_context (void)
{
struct xkb_context *ctx;
char xdg[PATH_MAX] = {0};
const char *env;
/*
* We can only append search paths in libxkbcommon, so we start with an
* emtpy set, then add the XDG dir, then add the default search paths.
*/
ctx = xkb_context_new (XKB_CONTEXT_NO_DEFAULT_INCLUDES);
env = g_getenv ("XDG_CONFIG_HOME");
if (env)
{
g_snprintf (xdg, sizeof xdg, "%s/xkb", env);
}
else if ((env = g_getenv ("HOME")))
{
g_snprintf (xdg, sizeof xdg, "%s/.config/xkb", env);
}
if (env)
xkb_context_include_path_append (ctx, xdg);
xkb_context_include_path_append_default (ctx);
return ctx;
}

View File

@@ -0,0 +1,28 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Utilities for use with libxkbcommon
*
* Copyright 2020 Red Hat, Inc.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef META_KEYMAP_UTILS_H
#define META_KEYMAP_UTILS_H
#include <xkbcommon/xkbcommon.h>
struct xkb_context * meta_create_xkb_context (void);
#endif /* META_KEYMAP_UTILS_H */

View File

@@ -42,6 +42,7 @@
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-idle-monitor-private.h"
#include "backends/meta-keymap-utils.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-monitor-manager-private.h"
#include "backends/meta-pointer-constraint.h"
@@ -340,7 +341,7 @@ meta_backend_native_create_clutter_backend (MetaBackend *backend)
static void
meta_backend_native_post_init (MetaBackend *backend)
{
ClutterBackend *clutter_backend = clutter_get_default_backend ();
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
MetaSettings *settings = meta_backend_get_settings (backend);
@@ -425,6 +426,7 @@ meta_backend_native_set_keymap (MetaBackend *backend,
const char *variants,
const char *options)
{
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
struct xkb_rule_names names;
struct xkb_keymap *keymap;
struct xkb_context *context;
@@ -436,11 +438,11 @@ meta_backend_native_set_keymap (MetaBackend *backend,
names.variant = variants;
names.options = options;
context = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
context = meta_create_xkb_context ();
keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
xkb_context_unref (context);
seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
seat = clutter_backend_get_default_seat (clutter_backend);
meta_seat_native_set_keyboard_map (META_SEAT_NATIVE (seat), keymap);
meta_backend_notify_keymap_changed (backend);
@@ -451,18 +453,20 @@ meta_backend_native_set_keymap (MetaBackend *backend,
static struct xkb_keymap *
meta_backend_native_get_keymap (MetaBackend *backend)
{
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
ClutterSeat *seat;
seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
seat = clutter_backend_get_default_seat (clutter_backend);
return meta_seat_native_get_keyboard_map (META_SEAT_NATIVE (seat));
}
static xkb_layout_index_t
meta_backend_native_get_keymap_layout_group (MetaBackend *backend)
{
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
ClutterSeat *seat;
seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
seat = clutter_backend_get_default_seat (clutter_backend);
return meta_seat_native_get_keyboard_layout_index (META_SEAT_NATIVE (seat));
}
@@ -470,6 +474,7 @@ static void
meta_backend_native_lock_layout_group (MetaBackend *backend,
guint idx)
{
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
xkb_layout_index_t old_idx;
ClutterSeat *seat;
@@ -477,7 +482,7 @@ meta_backend_native_lock_layout_group (MetaBackend *backend,
if (old_idx == idx)
return;
seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
seat = clutter_backend_get_default_seat (clutter_backend);
meta_seat_native_set_keyboard_layout_index (META_SEAT_NATIVE (seat), idx);
meta_backend_notify_keymap_layout_group_changed (backend, idx);
}
@@ -486,9 +491,10 @@ static void
meta_backend_native_set_numlock (MetaBackend *backend,
gboolean numlock_state)
{
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
ClutterSeat *seat;
seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
seat = clutter_backend_get_default_seat (clutter_backend);
meta_seat_native_set_keyboard_numlock (META_SEAT_NATIVE (seat),
numlock_state);
}
@@ -780,7 +786,7 @@ meta_backend_native_pause (MetaBackendNative *native)
meta_backend_get_monitor_manager (backend);
MetaMonitorManagerKms *monitor_manager_kms =
META_MONITOR_MANAGER_KMS (monitor_manager);
ClutterBackend *clutter_backend = clutter_get_default_backend ();
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
MetaSeatNative *seat =
META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));
@@ -805,7 +811,7 @@ void meta_backend_native_resume (MetaBackendNative *native)
META_MONITOR_MANAGER_KMS (monitor_manager);
MetaInputSettings *input_settings;
MetaIdleMonitor *idle_monitor;
ClutterBackend *clutter_backend = clutter_get_default_backend ();
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
MetaSeatNative *seat =
META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend));

View File

@@ -1719,8 +1719,9 @@ meta_cursor_renderer_native_new (MetaBackend *backend)
MetaCursorRendererNative *cursor_renderer_native;
MetaCursorRendererNativePrivate *priv;
cursor_renderer_native =
g_object_new (META_TYPE_CURSOR_RENDERER_NATIVE, NULL);
cursor_renderer_native = g_object_new (META_TYPE_CURSOR_RENDERER_NATIVE,
"backend", backend,
NULL);
priv =
meta_cursor_renderer_native_get_instance_private (cursor_renderer_native);

View File

@@ -21,6 +21,7 @@
#include "config.h"
#include "backends/meta-keymap-utils.h"
#include "backends/native/meta-keymap-native.h"
#include "backends/native/meta-seat-native.h"
@@ -111,7 +112,7 @@ meta_keymap_native_init (MetaKeymapNative *keymap)
names.variant = option_xkb_variant;
names.options = option_xkb_options;
ctx = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
ctx = meta_create_xkb_context ();
g_assert (ctx);
keymap->keymap = xkb_keymap_new_from_names (ctx, &names, 0);
xkb_context_unref (ctx);

View File

@@ -140,7 +140,7 @@ meta_stage_native_rebuild_views (MetaStageNative *stage_native)
ClutterActor *stage = meta_backend_get_stage (backend);
meta_renderer_rebuild_views (renderer);
clutter_stage_update_resource_scales (CLUTTER_STAGE (stage));
clutter_stage_clear_stage_views (CLUTTER_STAGE (stage));
ensure_frame_callbacks (stage_native);
}

View File

@@ -117,7 +117,9 @@ meta_backend_x11_cm_create_monitor_manager (MetaBackend *backend,
static MetaCursorRenderer *
meta_backend_x11_cm_create_cursor_renderer (MetaBackend *backend)
{
return g_object_new (META_TYPE_CURSOR_RENDERER_X11, NULL);
return g_object_new (META_TYPE_CURSOR_RENDERER_X11,
"backend", backend,
NULL);
}
static MetaInputSettings *

View File

@@ -43,6 +43,7 @@
#include <xkbcommon/xkbcommon-x11.h>
#include "backends/meta-idle-monitor-private.h"
#include "backends/meta-keymap-utils.h"
#include "backends/meta-stage-private.h"
#include "backends/x11/meta-clutter-backend-x11.h"
#include "backends/x11/meta-event-x11.h"
@@ -704,7 +705,7 @@ meta_backend_x11_get_keymap (MetaBackend *backend)
if (priv->keymap == NULL)
{
struct xkb_context *context = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
struct xkb_context *context = meta_create_xkb_context ();
priv->keymap = xkb_x11_keymap_new_from_device (context,
priv->xcb,
xkb_x11_get_core_keyboard_device_id (priv->xcb),

View File

@@ -65,7 +65,9 @@ meta_backend_x11_nested_create_monitor_manager (MetaBackend *backend,
static MetaCursorRenderer *
meta_backend_x11_nested_create_cursor_renderer (MetaBackend *backend)
{
return g_object_new (META_TYPE_CURSOR_RENDERER_X11_NESTED, NULL);
return g_object_new (META_TYPE_CURSOR_RENDERER_X11_NESTED,
"backend", backend,
NULL);
}
static MetaInputSettings *
@@ -85,7 +87,7 @@ meta_backend_x11_nested_update_screen_size (MetaBackend *backend,
if (meta_is_stage_views_enabled ())
{
meta_renderer_rebuild_views (renderer);
clutter_stage_update_resource_scales (CLUTTER_STAGE (stage));
clutter_stage_clear_stage_views (CLUTTER_STAGE (stage));
}
clutter_actor_set_size (stage, width, height);
}

View File

@@ -91,6 +91,7 @@ enum
PROP_0,
PROP_DISPLAY,
PROP_BACKEND,
N_PROPS
};
@@ -111,6 +112,7 @@ typedef struct _MetaCompositorPrivate
GObject parent;
MetaDisplay *display;
MetaBackend *backend;
guint pre_paint_func_id;
guint post_paint_func_id;
@@ -231,7 +233,7 @@ meta_get_stage_for_display (MetaDisplay *display)
g_return_val_if_fail (compositor, NULL);
priv = meta_compositor_get_instance_private (compositor);
return priv->stage;
return meta_backend_get_stage (priv->backend);
}
/**
@@ -564,12 +566,11 @@ meta_compositor_do_manage (MetaCompositor *compositor,
MetaCompositorPrivate *priv =
meta_compositor_get_instance_private (compositor);
MetaDisplay *display = priv->display;
MetaBackend *backend = meta_get_backend ();
priv->stage = meta_backend_get_stage (backend);
MetaBackend *backend = priv->backend;
ClutterActor *stage = meta_backend_get_stage (backend);
priv->stage_presented_id =
g_signal_connect (priv->stage, "presented",
g_signal_connect (stage, "presented",
G_CALLBACK (on_presented),
compositor);
@@ -582,18 +583,18 @@ meta_compositor_do_manage (MetaCompositor *compositor,
* matter.
*/
priv->stage_after_paint_id =
g_signal_connect_after (priv->stage, "after-paint",
g_signal_connect_after (stage, "after-paint",
G_CALLBACK (after_stage_paint), compositor);
clutter_stage_set_sync_delay (CLUTTER_STAGE (priv->stage), META_SYNC_DELAY);
clutter_stage_set_sync_delay (CLUTTER_STAGE (stage), META_SYNC_DELAY);
priv->window_group = meta_window_group_new (display);
priv->top_window_group = meta_window_group_new (display);
priv->feedback_group = meta_window_group_new (display);
clutter_actor_add_child (priv->stage, priv->window_group);
clutter_actor_add_child (priv->stage, priv->top_window_group);
clutter_actor_add_child (priv->stage, priv->feedback_group);
clutter_actor_add_child (stage, priv->window_group);
clutter_actor_add_child (stage, priv->top_window_group);
clutter_actor_add_child (stage, priv->feedback_group);
if (!META_COMPOSITOR_GET_CLASS (compositor)->manage (compositor, error))
return FALSE;
@@ -1162,7 +1163,7 @@ meta_compositor_real_post_paint (MetaCompositor *compositor)
case COGL_GRAPHICS_RESET_STATUS_PURGED_CONTEXT_RESET:
g_signal_emit_by_name (priv->display, "gl-video-memory-purged");
clutter_actor_queue_redraw (CLUTTER_ACTOR (priv->stage));
clutter_actor_queue_redraw (meta_backend_get_stage (priv->backend));
break;
default:
@@ -1211,6 +1212,9 @@ meta_compositor_set_property (GObject *object,
case PROP_DISPLAY:
priv->display = g_value_get_object (value);
break;
case PROP_BACKEND:
priv->backend = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -1231,6 +1235,9 @@ meta_compositor_get_property (GObject *object,
case PROP_DISPLAY:
g_value_set_object (value, priv->display);
break;
case PROP_BACKEND:
g_value_set_object (value, priv->backend);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -1266,11 +1273,12 @@ meta_compositor_dispose (GObject *object)
MetaCompositor *compositor = META_COMPOSITOR (object);
MetaCompositorPrivate *priv =
meta_compositor_get_instance_private (compositor);
ClutterActor *stage = meta_backend_get_stage (priv->backend);
g_clear_pointer (&priv->laters, meta_laters_free);
g_clear_signal_handler (&priv->stage_after_paint_id, priv->stage);
g_clear_signal_handler (&priv->stage_presented_id, priv->stage);
g_clear_signal_handler (&priv->stage_after_paint_id, stage);
g_clear_signal_handler (&priv->stage_presented_id, stage);
g_clear_handle_id (&priv->pre_paint_func_id,
clutter_threads_remove_repaint_func);
@@ -1309,6 +1317,14 @@ meta_compositor_class_init (MetaCompositorClass *klass)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_props[PROP_BACKEND] =
g_param_spec_object ("backend",
"backend",
"MetaBackend",
META_TYPE_BACKEND,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, obj_props);
signals[PRE_PAINT] =
@@ -1615,7 +1631,7 @@ meta_compositor_get_stage (MetaCompositor *compositor)
MetaCompositorPrivate *priv =
meta_compositor_get_instance_private (compositor);
return CLUTTER_STAGE (priv->stage);
return CLUTTER_STAGE (meta_backend_get_stage (priv->backend));
}
MetaWindowActor *

View File

@@ -19,142 +19,19 @@
* Portions adapted from gnome-shell/src/shell-global.c
*/
/**
* SECTION:meta-background-actor
* @title: MetaBackgroundActor
* @short_description: Actor for painting the root window background
*
*/
/*
* The overall model drawing model of this widget is that we have one
* texture, or two interpolated textures, possibly with alpha or
* margins that let the underlying background show through, blended
* over a solid color or a gradient. The result of that combination
* can then be affected by a "vignette" that darkens the background
* away from a central point (or as a no-GLSL fallback, simply darkens
* the background) and by overall opacity.
*
* As of GNOME 3.14, GNOME is only using a fraction of this when the
* user sets the background through the control center - what can be
* set is:
*
* A single image without a border
* An animation of images without a border that blend together,
* with the blend changing every 4-5 minutes
* A solid color with a repeated noise texture blended over it
*
* This all is pretty easy to do in a fragment shader, except when:
*
* A) We don't have GLSL - in this case, the operation of
* interpolating the two textures and blending the result over the
* background can't be expressed with Cogl's fixed-function layer
* combining (which is confined to what GL's texture environment
* combining can do) So we can only handle the above directly if
* there are no margins or alpha.
*
* B) The image textures are sliced. Texture size limits on older
* hardware (pre-965 intel hardware, r300, etc.) is often 2048,
* and it would be common to use a texture larger than this for a
* background and expect it to be scaled down. Cogl can compensate
* for this by breaking the texture up into multiple textures, but
* can't multitexture with sliced textures. So we can only handle
* the above if there's a single texture.
*
* However, even when we *can* represent everything in a single pass,
* it's not necessarily efficient. If we want to draw a 1024x768
* background, it's pretty inefficient to bilinearly texture from
* two 2560x1440 images and mix that. So the drawing model we take
* here is that MetaBackground generates a single texture (which
* might be a 1x1 texture for a solid color, or a 1x2 texture for a
* gradient, or a repeated texture for wallpaper, or a pre-rendered
* texture the size of the screen), and we draw with that, possibly
* adding the vignette and opacity.
*/
#include "config.h"
#include "compositor/meta-background-actor-private.h"
#include "compositor/meta-background-content-private.h"
#include "clutter/clutter.h"
#include "compositor/clutter-utils.h"
#include "compositor/cogl-utils.h"
#include "compositor/meta-background-private.h"
#include "compositor/meta-cullable.h"
#include "meta/display.h"
#include "meta/meta-x11-errors.h"
enum
{
PROP_META_DISPLAY = 1,
PROP_MONITOR,
PROP_BACKGROUND,
PROP_GRADIENT,
PROP_GRADIENT_HEIGHT,
PROP_GRADIENT_MAX_DARKNESS,
PROP_VIGNETTE,
PROP_VIGNETTE_SHARPNESS,
PROP_VIGNETTE_BRIGHTNESS
};
typedef enum
{
CHANGED_BACKGROUND = 1 << 0,
CHANGED_EFFECTS = 1 << 2,
CHANGED_VIGNETTE_PARAMETERS = 1 << 3,
CHANGED_GRADIENT_PARAMETERS = 1 << 4,
CHANGED_ALL = 0xFFFF
} ChangedFlags;
#define GRADIENT_VERTEX_SHADER_DECLARATIONS \
"uniform vec2 scale;\n" \
"varying vec2 position;\n" \
#define GRADIENT_VERTEX_SHADER_CODE \
"position = cogl_tex_coord0_in.xy * scale;\n" \
#define GRADIENT_FRAGMENT_SHADER_DECLARATIONS \
"uniform float gradient_height_perc;\n" \
"uniform float gradient_max_darkness;\n" \
"varying vec2 position;\n" \
#define GRADIENT_FRAGMENT_SHADER_CODE \
"float min_brightness = 1.0 - gradient_max_darkness;\n" \
"float gradient_y_pos = min(position.y, gradient_height_perc) / gradient_height_perc;\n" \
"float pixel_brightness = (1.0 - min_brightness) * gradient_y_pos + min_brightness;\n" \
"cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness;\n" \
#define VIGNETTE_VERTEX_SHADER_DECLARATIONS \
"uniform vec2 scale;\n" \
"uniform vec2 offset;\n" \
"varying vec2 position;\n" \
#define VIGNETTE_VERTEX_SHADER_CODE \
"position = cogl_tex_coord0_in.xy * scale + offset;\n" \
#define VIGNETTE_SQRT_2 "1.4142"
#define VIGNETTE_FRAGMENT_SHADER_DECLARATIONS \
"uniform float vignette_sharpness;\n" \
"varying vec2 position;\n" \
"float rand(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453123); }\n" \
#define VIGNETTE_FRAGMENT_SHADER_CODE \
"float t = " VIGNETTE_SQRT_2 " * length(position);\n" \
"t = min(t, 1.0);\n" \
"float pixel_brightness = 1.0 - t * vignette_sharpness;\n" \
"cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness;\n" \
"cogl_color_out.rgb += (rand(position) - 0.5) / 255.0;\n" \
typedef struct _MetaBackgroundLayer MetaBackgroundLayer;
typedef enum
{
PIPELINE_VIGNETTE = (1 << 0),
PIPELINE_BLEND = (1 << 1),
PIPELINE_GRADIENT = (1 << 2),
} PipelineFlags;
struct _MetaBackgroundActor
{
ClutterActor parent;
@@ -162,24 +39,7 @@ struct _MetaBackgroundActor
MetaDisplay *display;
int monitor;
MetaBackground *background;
gboolean gradient;
double gradient_max_darkness;
int gradient_height;
gboolean vignette;
double vignette_brightness;
double vignette_sharpness;
ChangedFlags changed;
CoglPipeline *pipeline;
PipelineFlags pipeline_flags;
cairo_rectangle_int_t texture_area;
gboolean force_bilinear;
cairo_region_t *clip_region;
cairo_region_t *unobscured_region;
MetaBackgroundContent *content;
};
static void cullable_iface_init (MetaCullableInterface *iface);
@@ -188,407 +48,16 @@ G_DEFINE_TYPE_WITH_CODE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYP
G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
static void
set_clip_region (MetaBackgroundActor *self,
cairo_region_t *clip_region)
maybe_create_content (MetaBackgroundActor *self)
{
g_clear_pointer (&self->clip_region, cairo_region_destroy);
if (clip_region)
{
if (cairo_region_is_empty (clip_region))
self->clip_region = cairo_region_reference (clip_region);
else
self->clip_region = cairo_region_copy (clip_region);
}
}
g_autoptr (ClutterContent) content = NULL;
static void
set_unobscured_region (MetaBackgroundActor *self,
cairo_region_t *unobscured_region)
{
g_clear_pointer (&self->unobscured_region, cairo_region_destroy);
if (unobscured_region)
{
if (cairo_region_is_empty (unobscured_region))
self->unobscured_region = cairo_region_reference (unobscured_region);
else
self->unobscured_region = cairo_region_copy (unobscured_region);
}
}
static void
meta_background_actor_dispose (GObject *object)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
set_clip_region (self, NULL);
set_unobscured_region (self, NULL);
meta_background_actor_set_background (self, NULL);
if (self->pipeline)
{
cogl_object_unref (self->pipeline);
self->pipeline = NULL;
}
G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object);
}
static void
get_preferred_size (MetaBackgroundActor *self,
gfloat *width,
gfloat *height)
{
MetaRectangle monitor_geometry;
meta_display_get_monitor_geometry (self->display,
self->monitor,
&monitor_geometry);
if (width != NULL)
*width = monitor_geometry.width;
if (height != NULL)
*height = monitor_geometry.height;
}
static void
meta_background_actor_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
gfloat width;
get_preferred_size (META_BACKGROUND_ACTOR (actor), &width, NULL);
if (min_width_p)
*min_width_p = width;
if (natural_width_p)
*natural_width_p = width;
}
static void
meta_background_actor_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
gfloat height;
get_preferred_size (META_BACKGROUND_ACTOR (actor), NULL, &height);
if (min_height_p)
*min_height_p = height;
if (natural_height_p)
*natural_height_p = height;
}
static CoglPipeline *
make_pipeline (PipelineFlags pipeline_flags)
{
static CoglPipeline *templates[8];
CoglPipeline **templatep;
templatep = &templates[pipeline_flags];
if (*templatep == NULL)
{
/* Cogl automatically caches pipelines with no eviction policy,
* so we need to prevent identical pipelines from getting cached
* separately, by reusing the same shader snippets.
*/
*templatep = COGL_PIPELINE (meta_create_texture_pipeline (NULL));
if ((pipeline_flags & PIPELINE_VIGNETTE) != 0)
{
static CoglSnippet *vignette_vertex_snippet;
static CoglSnippet *vignette_fragment_snippet;
if (!vignette_vertex_snippet)
vignette_vertex_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
VIGNETTE_VERTEX_SHADER_DECLARATIONS,
VIGNETTE_VERTEX_SHADER_CODE);
cogl_pipeline_add_snippet (*templatep, vignette_vertex_snippet);
if (!vignette_fragment_snippet)
vignette_fragment_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
VIGNETTE_FRAGMENT_SHADER_DECLARATIONS,
VIGNETTE_FRAGMENT_SHADER_CODE);
cogl_pipeline_add_snippet (*templatep, vignette_fragment_snippet);
}
if ((pipeline_flags & PIPELINE_GRADIENT) != 0)
{
static CoglSnippet *gradient_vertex_snippet;
static CoglSnippet *gradient_fragment_snippet;
if (!gradient_vertex_snippet)
gradient_vertex_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
GRADIENT_VERTEX_SHADER_DECLARATIONS,
GRADIENT_VERTEX_SHADER_CODE);
cogl_pipeline_add_snippet (*templatep, gradient_vertex_snippet);
if (!gradient_fragment_snippet)
gradient_fragment_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
GRADIENT_FRAGMENT_SHADER_DECLARATIONS,
GRADIENT_FRAGMENT_SHADER_CODE);
cogl_pipeline_add_snippet (*templatep, gradient_fragment_snippet);
}
if ((pipeline_flags & PIPELINE_BLEND) == 0)
cogl_pipeline_set_blend (*templatep, "RGBA = ADD (SRC_COLOR, 0)", NULL);
}
return cogl_pipeline_copy (*templatep);
}
static void
setup_pipeline (MetaBackgroundActor *self,
ClutterPaintContext *paint_context,
cairo_rectangle_int_t *actor_pixel_rect)
{
PipelineFlags pipeline_flags = 0;
guint8 opacity;
float color_component;
CoglFramebuffer *fb;
CoglPipelineFilter filter;
opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self));
if (opacity < 255)
pipeline_flags |= PIPELINE_BLEND;
if (self->vignette && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
pipeline_flags |= PIPELINE_VIGNETTE;
if (self->gradient && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
pipeline_flags |= PIPELINE_GRADIENT;
if (self->pipeline &&
pipeline_flags != self->pipeline_flags)
{
cogl_object_unref (self->pipeline);
self->pipeline = NULL;
}
if (self->pipeline == NULL)
{
self->pipeline_flags = pipeline_flags;
self->pipeline = make_pipeline (pipeline_flags);
self->changed = CHANGED_ALL;
}
if ((self->changed & CHANGED_BACKGROUND) != 0)
{
CoglPipelineWrapMode wrap_mode;
CoglTexture *texture = meta_background_get_texture (self->background,
self->monitor,
&self->texture_area,
&wrap_mode);
self->force_bilinear = texture &&
(self->texture_area.width != (int)cogl_texture_get_width (texture) ||
self->texture_area.height != (int)cogl_texture_get_height (texture));
cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
cogl_pipeline_set_layer_wrap_mode (self->pipeline, 0, wrap_mode);
self->changed &= ~CHANGED_BACKGROUND;
}
if ((self->changed & CHANGED_VIGNETTE_PARAMETERS) != 0)
{
cogl_pipeline_set_uniform_1f (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"vignette_sharpness"),
self->vignette_sharpness);
self->changed &= ~CHANGED_VIGNETTE_PARAMETERS;
}
if ((self->changed & CHANGED_GRADIENT_PARAMETERS) != 0)
{
MetaRectangle monitor_geometry;
float gradient_height_perc;
meta_display_get_monitor_geometry (self->display,
self->monitor, &monitor_geometry);
gradient_height_perc = MAX (0.0001, self->gradient_height / (float)monitor_geometry.height);
cogl_pipeline_set_uniform_1f (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"gradient_height_perc"),
gradient_height_perc);
cogl_pipeline_set_uniform_1f (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"gradient_max_darkness"),
self->gradient_max_darkness);
self->changed &= ~CHANGED_GRADIENT_PARAMETERS;
}
if (self->vignette)
{
color_component = self->vignette_brightness * opacity / 255.;
if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
{
/* Darken everything to match the average brightness that would
* be there if we were drawing the vignette, which is
* (1 - (pi/12.) * vignette_sharpness) [exercise for the reader :]
*/
color_component *= (1 - 0.74 * self->vignette_sharpness);
}
}
else
color_component = opacity / 255.;
cogl_pipeline_set_color4f (self->pipeline,
color_component,
color_component,
color_component,
opacity / 255.);
fb = clutter_paint_context_get_framebuffer (paint_context);
if (!self->force_bilinear &&
meta_actor_painting_untransformed (fb,
actor_pixel_rect->width,
actor_pixel_rect->height,
actor_pixel_rect->width,
actor_pixel_rect->height,
NULL, NULL))
filter = COGL_PIPELINE_FILTER_NEAREST;
else
filter = COGL_PIPELINE_FILTER_LINEAR;
cogl_pipeline_set_layer_filters (self->pipeline, 0, filter, filter);
}
static void
set_glsl_parameters (MetaBackgroundActor *self,
cairo_rectangle_int_t *actor_pixel_rect)
{
float scale[2];
float offset[2];
/* Compute a scale and offset for transforming texture coordinates to the
* coordinate system from [-0.5 to 0.5] across the area of the actor
*/
scale[0] = self->texture_area.width / (float)actor_pixel_rect->width;
scale[1] = self->texture_area.height / (float)actor_pixel_rect->height;
offset[0] = self->texture_area.x / (float)actor_pixel_rect->width - 0.5;
offset[1] = self->texture_area.y / (float)actor_pixel_rect->height - 0.5;
cogl_pipeline_set_uniform_float (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"scale"),
2, 1, scale);
cogl_pipeline_set_uniform_float (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"offset"),
2, 1, offset);
}
static void
paint_clipped_rectangle (CoglFramebuffer *fb,
CoglPipeline *pipeline,
cairo_rectangle_int_t *rect,
cairo_rectangle_int_t *texture_area)
{
float x1, y1, x2, y2;
float tx1, ty1, tx2, ty2;
x1 = rect->x;
y1 = rect->y;
x2 = rect->x + rect->width;
y2 = rect->y + rect->height;
tx1 = (x1 - texture_area->x) / texture_area->width;
ty1 = (y1 - texture_area->y) / texture_area->height;
tx2 = (x2 - texture_area->x) / texture_area->width;
ty2 = (y2 - texture_area->y) / texture_area->height;
cogl_framebuffer_draw_textured_rectangle (fb, pipeline,
x1, y1, x2, y2,
tx1, ty1, tx2, ty2);
}
static gboolean
meta_background_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume)
{
return clutter_paint_volume_set_from_allocation (volume, actor);
}
static void
meta_background_actor_paint (ClutterActor *actor,
ClutterPaintContext *paint_context)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
ClutterActorBox actor_box;
cairo_rectangle_int_t actor_pixel_rect;
CoglFramebuffer *fb;
cairo_region_t *region;
int i, n_rects;
if ((self->clip_region && cairo_region_is_empty (self->clip_region)))
return;
clutter_actor_get_content_box (actor, &actor_box);
actor_pixel_rect.x = actor_box.x1;
actor_pixel_rect.y = actor_box.y1;
actor_pixel_rect.width = actor_box.x2 - actor_box.x1;
actor_pixel_rect.height = actor_box.y2 - actor_box.y1;
setup_pipeline (self, paint_context, &actor_pixel_rect);
set_glsl_parameters (self, &actor_pixel_rect);
/* Limit to how many separate rectangles we'll draw; beyond this just
* fall back and draw the whole thing */
#define MAX_RECTS 64
fb = clutter_paint_context_get_framebuffer (paint_context);
/* Now figure out what to actually paint.
*/
if (self->clip_region)
{
region = cairo_region_copy (self->clip_region);
cairo_region_intersect_rectangle (region, &actor_pixel_rect);
}
else
{
region = cairo_region_create_rectangle (&actor_pixel_rect);
}
if (self->unobscured_region)
cairo_region_intersect (region, self->unobscured_region);
if (cairo_region_is_empty (region))
{
cairo_region_destroy (region);
if (self->content || !self->display || self->monitor == -1)
return;
}
n_rects = cairo_region_num_rectangles (region);
if (n_rects <= MAX_RECTS)
{
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
paint_clipped_rectangle (fb, self->pipeline, &rect,
&self->texture_area);
}
}
else
{
cairo_rectangle_int_t rect;
cairo_region_get_extents (region, &rect);
paint_clipped_rectangle (fb, self->pipeline, &rect,
&self->texture_area);
}
cairo_region_destroy (region);
content = meta_background_content_new (self->display, self->monitor);
self->content = META_BACKGROUND_CONTENT (content);
clutter_actor_set_content (CLUTTER_ACTOR (self), content);
}
static void
@@ -603,48 +72,11 @@ meta_background_actor_set_property (GObject *object,
{
case PROP_META_DISPLAY:
self->display = g_value_get_object (value);
maybe_create_content (self);
break;
case PROP_MONITOR:
meta_background_actor_set_monitor (self, g_value_get_int (value));
break;
case PROP_BACKGROUND:
meta_background_actor_set_background (self, g_value_get_object (value));
break;
case PROP_GRADIENT:
meta_background_actor_set_gradient (self,
g_value_get_boolean (value),
self->gradient_height,
self->gradient_max_darkness);
break;
case PROP_GRADIENT_HEIGHT:
meta_background_actor_set_gradient (self,
self->gradient,
g_value_get_int (value),
self->gradient_max_darkness);
break;
case PROP_GRADIENT_MAX_DARKNESS:
meta_background_actor_set_gradient (self,
self->gradient,
self->gradient_height,
g_value_get_double (value));
break;
case PROP_VIGNETTE:
meta_background_actor_set_vignette (self,
g_value_get_boolean (value),
self->vignette_brightness,
self->vignette_sharpness);
break;
case PROP_VIGNETTE_SHARPNESS:
meta_background_actor_set_vignette (self,
self->vignette,
self->vignette_brightness,
g_value_get_double (value));
break;
case PROP_VIGNETTE_BRIGHTNESS:
meta_background_actor_set_vignette (self,
self->vignette,
g_value_get_double (value),
self->vignette_sharpness);
self->monitor = g_value_get_int (value);
maybe_create_content (self);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -668,27 +100,6 @@ meta_background_actor_get_property (GObject *object,
case PROP_MONITOR:
g_value_set_int (value, self->monitor);
break;
case PROP_BACKGROUND:
g_value_set_object (value, self->background);
break;
case PROP_GRADIENT:
g_value_set_boolean (value, self->gradient);
break;
case PROP_GRADIENT_HEIGHT:
g_value_set_int (value, self->gradient_height);
break;
case PROP_GRADIENT_MAX_DARKNESS:
g_value_set_double (value, self->gradient_max_darkness);
break;
case PROP_VIGNETTE:
g_value_set_boolean (value, self->vignette);
break;
case PROP_VIGNETTE_BRIGHTNESS:
g_value_set_double (value, self->vignette_brightness);
break;
case PROP_VIGNETTE_SHARPNESS:
g_value_set_double (value, self->vignette_sharpness);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -699,18 +110,11 @@ static void
meta_background_actor_class_init (MetaBackgroundActorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *param_spec;
object_class->dispose = meta_background_actor_dispose;
object_class->set_property = meta_background_actor_set_property;
object_class->get_property = meta_background_actor_get_property;
actor_class->get_preferred_width = meta_background_actor_get_preferred_width;
actor_class->get_preferred_height = meta_background_actor_get_preferred_height;
actor_class->get_paint_volume = meta_background_actor_get_paint_volume;
actor_class->paint = meta_background_actor_paint;
param_spec = g_param_spec_object ("meta-display",
"MetaDisplay",
"MetaDisplay",
@@ -730,88 +134,15 @@ meta_background_actor_class_init (MetaBackgroundActorClass *klass)
g_object_class_install_property (object_class,
PROP_MONITOR,
param_spec);
param_spec = g_param_spec_object ("background",
"Background",
"MetaBackground object holding background parameters",
META_TYPE_BACKGROUND,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_BACKGROUND,
param_spec);
param_spec = g_param_spec_boolean ("gradient",
"Gradient",
"Whether gradient effect is enabled",
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_GRADIENT,
param_spec);
param_spec = g_param_spec_int ("gradient-height",
"Gradient Height",
"Height of gradient effect",
0, G_MAXINT, 0,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_GRADIENT_HEIGHT,
param_spec);
param_spec = g_param_spec_double ("gradient-max-darkness",
"Gradient Max Darkness",
"How dark is the gradient initially",
0.0, 1.0, 0.0,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_GRADIENT_MAX_DARKNESS,
param_spec);
param_spec = g_param_spec_boolean ("vignette",
"Vignette",
"Whether vignette effect is enabled",
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_VIGNETTE,
param_spec);
param_spec = g_param_spec_double ("brightness",
"Vignette Brightness",
"Brightness of vignette effect",
0.0, 1.0, 1.0,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_VIGNETTE_BRIGHTNESS,
param_spec);
param_spec = g_param_spec_double ("vignette-sharpness",
"Vignette Sharpness",
"Sharpness of vignette effect",
0.0, G_MAXDOUBLE, 0.0,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_VIGNETTE_SHARPNESS,
param_spec);
}
static void
meta_background_actor_init (MetaBackgroundActor *self)
{
self->gradient = FALSE;
self->gradient_height = 0;
self->gradient_max_darkness = 0.0;
self->monitor = -1;
self->vignette = FALSE;
self->vignette_brightness = 1.0;
self->vignette_sharpness = 0.0;
clutter_actor_set_request_mode (CLUTTER_ACTOR (self),
CLUTTER_REQUEST_CONTENT_SIZE);
}
/**
@@ -843,8 +174,12 @@ meta_background_actor_cull_out (MetaCullable *cullable,
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable);
set_unobscured_region (self, unobscured_region);
set_clip_region (self, clip_region);
if (!self->content)
return;
meta_background_content_cull_out (self->content,
unobscured_region,
clip_region);
}
static void
@@ -852,8 +187,10 @@ meta_background_actor_reset_culling (MetaCullable *cullable)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable);
set_unobscured_region (self, NULL);
set_clip_region (self, NULL);
if (!self->content)
return;
meta_background_content_reset_culling (self->content);
}
static void
@@ -874,135 +211,8 @@ cullable_iface_init (MetaCullableInterface *iface)
cairo_region_t *
meta_background_actor_get_clip_region (MetaBackgroundActor *self)
{
return self->clip_region;
}
static void
invalidate_pipeline (MetaBackgroundActor *self,
ChangedFlags changed)
{
self->changed |= changed;
}
static void
on_background_changed (MetaBackground *background,
MetaBackgroundActor *self)
{
invalidate_pipeline (self, CHANGED_BACKGROUND);
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
}
void
meta_background_actor_set_background (MetaBackgroundActor *self,
MetaBackground *background)
{
g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
g_return_if_fail (background == NULL || META_IS_BACKGROUND (background));
if (background == self->background)
return;
if (self->background)
{
g_signal_handlers_disconnect_by_func (self->background,
(gpointer)on_background_changed,
self);
g_object_unref (self->background);
self->background = NULL;
}
if (background)
{
self->background = g_object_ref (background);
g_signal_connect (self->background, "changed",
G_CALLBACK (on_background_changed), self);
}
invalidate_pipeline (self, CHANGED_BACKGROUND);
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
}
void
meta_background_actor_set_gradient (MetaBackgroundActor *self,
gboolean enabled,
int height,
double max_darkness)
{
gboolean changed = FALSE;
g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
g_return_if_fail (height >= 0);
g_return_if_fail (max_darkness >= 0. && max_darkness <= 1.);
enabled = enabled != FALSE && height != 0;
if (enabled != self->gradient)
{
self->gradient = enabled;
invalidate_pipeline (self, CHANGED_EFFECTS);
changed = TRUE;
}
if (height != self->gradient_height || max_darkness != self->gradient_max_darkness)
{
self->gradient_height = height;
self->gradient_max_darkness = max_darkness;
invalidate_pipeline (self, CHANGED_GRADIENT_PARAMETERS);
changed = TRUE;
}
if (changed)
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
}
void
meta_background_actor_set_monitor (MetaBackgroundActor *self,
int monitor)
{
MetaRectangle old_monitor_geometry;
MetaRectangle new_monitor_geometry;
MetaDisplay *display = self->display;
if(self->monitor == monitor)
return;
meta_display_get_monitor_geometry (display, self->monitor, &old_monitor_geometry);
meta_display_get_monitor_geometry (display, monitor, &new_monitor_geometry);
if(old_monitor_geometry.height != new_monitor_geometry.height)
invalidate_pipeline (self, CHANGED_GRADIENT_PARAMETERS);
self->monitor = monitor;
}
void
meta_background_actor_set_vignette (MetaBackgroundActor *self,
gboolean enabled,
double brightness,
double sharpness)
{
gboolean changed = FALSE;
g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
g_return_if_fail (brightness >= 0. && brightness <= 1.);
g_return_if_fail (sharpness >= 0.);
enabled = enabled != FALSE;
if (enabled != self->vignette)
{
self->vignette = enabled;
invalidate_pipeline (self, CHANGED_EFFECTS);
changed = TRUE;
}
if (brightness != self->vignette_brightness || sharpness != self->vignette_sharpness)
{
self->vignette_brightness = brightness;
self->vignette_sharpness = sharpness;
invalidate_pipeline (self, CHANGED_VIGNETTE_PARAMETERS);
changed = TRUE;
}
if (changed)
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
if (!self->content)
return NULL;
return meta_background_content_get_clip_region (self->content);
}

View File

@@ -0,0 +1,16 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef META_BACKGROUND_CONTENT_PRIVATE_H
#define META_BACKGROUND_CONTENT_PRIVATE_H
#include "meta/meta-background-content.h"
cairo_region_t *meta_background_content_get_clip_region (MetaBackgroundContent *self);
void meta_background_content_cull_out (MetaBackgroundContent *self,
cairo_region_t *unobscured_region,
cairo_region_t *clip_region);
void meta_background_content_reset_culling (MetaBackgroundContent *self);
#endif /* META_BACKGROUND_CONTENT_PRIVATE_H */

View File

@@ -0,0 +1,937 @@
/*
* Copyright 2009 Sander Dijkhuis
* Copyright 2014 Red Hat, Inc.
* Copyright 2020 Endless Foundation.
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
*
* Portions adapted from gnome-shell/src/shell-global.c
*/
/**
* SECTION:meta-background-content
* @title: MetaBackgroundContent
* @short_description: ClutterContent for painting the root window background
*
*/
/*
* The overall model drawing model of this content is that we have one
* texture, or two interpolated textures, possibly with alpha or
* margins that let the underlying background show through, blended
* over a solid color or a gradient. The result of that combination
* can then be affected by a "vignette" that darkens the background
* away from a central point (or as a no-GLSL fallback, simply darkens
* the background) and by overall opacity.
*
* As of GNOME 3.14, GNOME is only using a fraction of this when the
* user sets the background through the control center - what can be
* set is:
*
* A single image without a border
* An animation of images without a border that blend together,
* with the blend changing every 4-5 minutes
* A solid color with a repeated noise texture blended over it
*
* This all is pretty easy to do in a fragment shader, except when:
*
* A) We don't have GLSL - in this case, the operation of
* interpolating the two textures and blending the result over the
* background can't be expressed with Cogl's fixed-function layer
* combining (which is confined to what GL's texture environment
* combining can do) So we can only handle the above directly if
* there are no margins or alpha.
*
* B) The image textures are sliced. Texture size limits on older
* hardware (pre-965 intel hardware, r300, etc.) is often 2048,
* and it would be common to use a texture larger than this for a
* background and expect it to be scaled down. Cogl can compensate
* for this by breaking the texture up into multiple textures, but
* can't multitexture with sliced textures. So we can only handle
* the above if there's a single texture.
*
* However, even when we *can* represent everything in a single pass,
* it's not necessarily efficient. If we want to draw a 1024x768
* background, it's pretty inefficient to bilinearly texture from
* two 2560x1440 images and mix that. So the drawing model we take
* here is that MetaBackground generates a single texture (which
* might be a 1x1 texture for a solid color, or a 1x2 texture for a
* gradient, or a repeated texture for wallpaper, or a pre-rendered
* texture the size of the screen), and we draw with that, possibly
* adding the vignette and opacity.
*/
#include "config.h"
#include "compositor/meta-background-content-private.h"
#include "clutter/clutter.h"
#include "compositor/clutter-utils.h"
#include "compositor/cogl-utils.h"
#include "compositor/meta-background-private.h"
#include "compositor/meta-cullable.h"
#include "meta/display.h"
typedef enum
{
CHANGED_BACKGROUND = 1 << 0,
CHANGED_EFFECTS = 1 << 2,
CHANGED_VIGNETTE_PARAMETERS = 1 << 3,
CHANGED_GRADIENT_PARAMETERS = 1 << 4,
CHANGED_ALL = 0xFFFF
} ChangedFlags;
#define GRADIENT_VERTEX_SHADER_DECLARATIONS \
"uniform vec2 scale;\n" \
"varying vec2 position;\n" \
#define GRADIENT_VERTEX_SHADER_CODE \
"position = cogl_tex_coord0_in.xy * scale;\n" \
#define GRADIENT_FRAGMENT_SHADER_DECLARATIONS \
"uniform float gradient_height_perc;\n" \
"uniform float gradient_max_darkness;\n" \
"varying vec2 position;\n" \
#define GRADIENT_FRAGMENT_SHADER_CODE \
"float min_brightness = 1.0 - gradient_max_darkness;\n" \
"float gradient_y_pos = min(position.y, gradient_height_perc) / gradient_height_perc;\n" \
"float pixel_brightness = (1.0 - min_brightness) * gradient_y_pos + min_brightness;\n" \
"cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness;\n" \
#define VIGNETTE_VERTEX_SHADER_DECLARATIONS \
"uniform vec2 scale;\n" \
"uniform vec2 offset;\n" \
"varying vec2 position;\n" \
#define VIGNETTE_VERTEX_SHADER_CODE \
"position = cogl_tex_coord0_in.xy * scale + offset;\n" \
#define VIGNETTE_SQRT_2 "1.4142"
#define VIGNETTE_FRAGMENT_SHADER_DECLARATIONS \
"uniform float vignette_sharpness;\n" \
"varying vec2 position;\n" \
"float rand(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453123); }\n" \
#define VIGNETTE_FRAGMENT_SHADER_CODE \
"float t = " VIGNETTE_SQRT_2 " * length(position);\n" \
"t = min(t, 1.0);\n" \
"float pixel_brightness = 1.0 - t * vignette_sharpness;\n" \
"cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness;\n" \
"cogl_color_out.rgb += (rand(position) - 0.5) / 255.0;\n" \
typedef struct _MetaBackgroundLayer MetaBackgroundLayer;
typedef enum
{
PIPELINE_VIGNETTE = (1 << 0),
PIPELINE_BLEND = (1 << 1),
PIPELINE_GRADIENT = (1 << 2),
} PipelineFlags;
struct _MetaBackgroundContent
{
GObject parent;
MetaDisplay *display;
int monitor;
MetaBackground *background;
gboolean gradient;
double gradient_max_darkness;
int gradient_height;
gboolean vignette;
double vignette_brightness;
double vignette_sharpness;
ChangedFlags changed;
CoglPipeline *pipeline;
PipelineFlags pipeline_flags;
cairo_rectangle_int_t texture_area;
gboolean force_bilinear;
cairo_region_t *clip_region;
cairo_region_t *unobscured_region;
};
static void clutter_content_iface_init (ClutterContentInterface *iface);
G_DEFINE_TYPE_WITH_CODE (MetaBackgroundContent,
meta_background_content,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
clutter_content_iface_init));
enum
{
PROP_0,
PROP_META_DISPLAY,
PROP_MONITOR,
PROP_BACKGROUND,
PROP_GRADIENT,
PROP_GRADIENT_HEIGHT,
PROP_GRADIENT_MAX_DARKNESS,
PROP_VIGNETTE,
PROP_VIGNETTE_SHARPNESS,
PROP_VIGNETTE_BRIGHTNESS,
N_PROPS,
};
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
set_clip_region (MetaBackgroundContent *self,
cairo_region_t *clip_region)
{
g_clear_pointer (&self->clip_region, cairo_region_destroy);
if (clip_region)
{
if (cairo_region_is_empty (clip_region))
self->clip_region = cairo_region_reference (clip_region);
else
self->clip_region = cairo_region_copy (clip_region);
}
}
static void
set_unobscured_region (MetaBackgroundContent *self,
cairo_region_t *unobscured_region)
{
g_clear_pointer (&self->unobscured_region, cairo_region_destroy);
if (unobscured_region)
{
if (cairo_region_is_empty (unobscured_region))
self->unobscured_region = cairo_region_reference (unobscured_region);
else
self->unobscured_region = cairo_region_copy (unobscured_region);
}
}
static void
invalidate_pipeline (MetaBackgroundContent *self,
ChangedFlags changed)
{
self->changed |= changed;
}
static void
on_background_changed (MetaBackground *background,
MetaBackgroundContent *self)
{
invalidate_pipeline (self, CHANGED_BACKGROUND);
clutter_content_invalidate (CLUTTER_CONTENT (self));
}
static CoglPipeline *
make_pipeline (PipelineFlags pipeline_flags)
{
static CoglPipeline *templates[8];
CoglPipeline **templatep;
templatep = &templates[pipeline_flags];
if (*templatep == NULL)
{
/* Cogl automatically caches pipelines with no eviction policy,
* so we need to prevent identical pipelines from getting cached
* separately, by reusing the same shader snippets.
*/
*templatep = COGL_PIPELINE (meta_create_texture_pipeline (NULL));
if ((pipeline_flags & PIPELINE_VIGNETTE) != 0)
{
static CoglSnippet *vignette_vertex_snippet;
static CoglSnippet *vignette_fragment_snippet;
if (!vignette_vertex_snippet)
vignette_vertex_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
VIGNETTE_VERTEX_SHADER_DECLARATIONS,
VIGNETTE_VERTEX_SHADER_CODE);
cogl_pipeline_add_snippet (*templatep, vignette_vertex_snippet);
if (!vignette_fragment_snippet)
vignette_fragment_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
VIGNETTE_FRAGMENT_SHADER_DECLARATIONS,
VIGNETTE_FRAGMENT_SHADER_CODE);
cogl_pipeline_add_snippet (*templatep, vignette_fragment_snippet);
}
if ((pipeline_flags & PIPELINE_GRADIENT) != 0)
{
static CoglSnippet *gradient_vertex_snippet;
static CoglSnippet *gradient_fragment_snippet;
if (!gradient_vertex_snippet)
gradient_vertex_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX,
GRADIENT_VERTEX_SHADER_DECLARATIONS,
GRADIENT_VERTEX_SHADER_CODE);
cogl_pipeline_add_snippet (*templatep, gradient_vertex_snippet);
if (!gradient_fragment_snippet)
gradient_fragment_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
GRADIENT_FRAGMENT_SHADER_DECLARATIONS,
GRADIENT_FRAGMENT_SHADER_CODE);
cogl_pipeline_add_snippet (*templatep, gradient_fragment_snippet);
}
if ((pipeline_flags & PIPELINE_BLEND) == 0)
cogl_pipeline_set_blend (*templatep, "RGBA = ADD (SRC_COLOR, 0)", NULL);
}
return cogl_pipeline_copy (*templatep);
}
static void
setup_pipeline (MetaBackgroundContent *self,
ClutterActor *actor,
ClutterPaintContext *paint_context,
cairo_rectangle_int_t *actor_pixel_rect)
{
PipelineFlags pipeline_flags = 0;
guint8 opacity;
float color_component;
CoglFramebuffer *fb;
CoglPipelineFilter filter;
opacity = clutter_actor_get_paint_opacity (actor);
if (opacity < 255)
pipeline_flags |= PIPELINE_BLEND;
if (self->vignette && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
pipeline_flags |= PIPELINE_VIGNETTE;
if (self->gradient && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
pipeline_flags |= PIPELINE_GRADIENT;
if (pipeline_flags != self->pipeline_flags)
g_clear_pointer (&self->pipeline, cogl_object_unref);
if (self->pipeline == NULL)
{
self->pipeline_flags = pipeline_flags;
self->pipeline = make_pipeline (pipeline_flags);
self->changed = CHANGED_ALL;
}
if (self->changed & CHANGED_BACKGROUND)
{
CoglPipelineWrapMode wrap_mode;
CoglTexture *texture = meta_background_get_texture (self->background,
self->monitor,
&self->texture_area,
&wrap_mode);
self->force_bilinear = texture &&
(self->texture_area.width != (int)cogl_texture_get_width (texture) ||
self->texture_area.height != (int)cogl_texture_get_height (texture));
cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
cogl_pipeline_set_layer_wrap_mode (self->pipeline, 0, wrap_mode);
self->changed &= ~CHANGED_BACKGROUND;
}
if (self->changed & CHANGED_VIGNETTE_PARAMETERS)
{
cogl_pipeline_set_uniform_1f (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"vignette_sharpness"),
self->vignette_sharpness);
self->changed &= ~CHANGED_VIGNETTE_PARAMETERS;
}
if (self->changed & CHANGED_GRADIENT_PARAMETERS)
{
MetaRectangle monitor_geometry;
float gradient_height_perc;
meta_display_get_monitor_geometry (self->display,
self->monitor, &monitor_geometry);
gradient_height_perc = MAX (0.0001, self->gradient_height / (float)monitor_geometry.height);
cogl_pipeline_set_uniform_1f (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"gradient_height_perc"),
gradient_height_perc);
cogl_pipeline_set_uniform_1f (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"gradient_max_darkness"),
self->gradient_max_darkness);
self->changed &= ~CHANGED_GRADIENT_PARAMETERS;
}
if (self->vignette)
{
color_component = self->vignette_brightness * opacity / 255.;
if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
{
/* Darken everything to match the average brightness that would
* be there if we were drawing the vignette, which is
* (1 - (pi/12.) * vignette_sharpness) [exercise for the reader :]
*/
color_component *= (1 - 0.74 * self->vignette_sharpness);
}
}
else
{
color_component = opacity / 255.;
}
cogl_pipeline_set_color4f (self->pipeline,
color_component,
color_component,
color_component,
opacity / 255.);
fb = clutter_paint_context_get_framebuffer (paint_context);
if (!self->force_bilinear &&
meta_actor_painting_untransformed (fb,
actor_pixel_rect->width,
actor_pixel_rect->height,
actor_pixel_rect->width,
actor_pixel_rect->height,
NULL, NULL))
filter = COGL_PIPELINE_FILTER_NEAREST;
else
filter = COGL_PIPELINE_FILTER_LINEAR;
cogl_pipeline_set_layer_filters (self->pipeline, 0, filter, filter);
}
static void
set_glsl_parameters (MetaBackgroundContent *self,
cairo_rectangle_int_t *actor_pixel_rect)
{
float scale[2];
float offset[2];
/* Compute a scale and offset for transforming texture coordinates to the
* coordinate system from [-0.5 to 0.5] across the area of the actor
*/
scale[0] = self->texture_area.width / (float)actor_pixel_rect->width;
scale[1] = self->texture_area.height / (float)actor_pixel_rect->height;
offset[0] = self->texture_area.x / (float)actor_pixel_rect->width - 0.5;
offset[1] = self->texture_area.y / (float)actor_pixel_rect->height - 0.5;
cogl_pipeline_set_uniform_float (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"scale"),
2, 1, scale);
cogl_pipeline_set_uniform_float (self->pipeline,
cogl_pipeline_get_uniform_location (self->pipeline,
"offset"),
2, 1, offset);
}
static void
paint_clipped_rectangle (MetaBackgroundContent *self,
ClutterPaintNode *node,
ClutterActorBox *actor_box,
cairo_rectangle_int_t *rect)
{
g_autoptr (ClutterPaintNode) pipeline_node = NULL;
float h_scale, v_scale;
float x1, y1, x2, y2;
float tx1, ty1, tx2, ty2;
h_scale = self->texture_area.width / clutter_actor_box_get_width (actor_box);
v_scale = self->texture_area.height / clutter_actor_box_get_height (actor_box);
x1 = rect->x;
y1 = rect->y;
x2 = rect->x + rect->width;
y2 = rect->y + rect->height;
tx1 = (x1 * h_scale - self->texture_area.x) / (float)self->texture_area.width;
ty1 = (y1 * v_scale - self->texture_area.y) / (float)self->texture_area.height;
tx2 = (x2 * h_scale - self->texture_area.x) / (float)self->texture_area.width;
ty2 = (y2 * v_scale - self->texture_area.y) / (float)self->texture_area.height;
pipeline_node = clutter_pipeline_node_new (self->pipeline);
clutter_paint_node_set_name (pipeline_node, "MetaBackgroundContent (Slice)");
clutter_paint_node_add_texture_rectangle (pipeline_node,
&(ClutterActorBox) {
.x1 = x1,
.y1 = y1,
.x2 = x2,
.y2 = y2,
},
tx1, ty1,
tx2, ty2);
clutter_paint_node_add_child (node, pipeline_node);
}
static void
meta_background_content_paint_content (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *node,
ClutterPaintContext *paint_context)
{
MetaBackgroundContent *self = META_BACKGROUND_CONTENT (content);
ClutterActorBox actor_box;
cairo_rectangle_int_t actor_pixel_rect;
cairo_region_t *region;
int i, n_rects;
if ((self->clip_region && cairo_region_is_empty (self->clip_region)))
return;
clutter_actor_get_content_box (actor, &actor_box);
actor_pixel_rect.x = actor_box.x1;
actor_pixel_rect.y = actor_box.x1;
actor_pixel_rect.width = actor_box.x2 - actor_box.x1;
actor_pixel_rect.height = actor_box.y2 - actor_box.y1;
setup_pipeline (self, actor, paint_context, &actor_pixel_rect);
set_glsl_parameters (self, &actor_pixel_rect);
/* Limit to how many separate rectangles we'll draw; beyond this just
* fall back and draw the whole thing */
#define MAX_RECTS 64
/* Now figure out what to actually paint */
if (self->clip_region)
{
region = cairo_region_copy (self->clip_region);
cairo_region_intersect_rectangle (region, &actor_pixel_rect);
}
else
{
region = cairo_region_create_rectangle (&actor_pixel_rect);
}
if (self->unobscured_region)
cairo_region_intersect (region, self->unobscured_region);
if (cairo_region_is_empty (region))
{
cairo_region_destroy (region);
return;
}
n_rects = cairo_region_num_rectangles (region);
if (n_rects <= MAX_RECTS)
{
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
paint_clipped_rectangle (self, node, &actor_box, &rect);
}
}
else
{
cairo_rectangle_int_t rect;
cairo_region_get_extents (region, &rect);
paint_clipped_rectangle (self, node, &actor_box, &rect);
}
cairo_region_destroy (region);
}
static gboolean
meta_background_content_get_preferred_size (ClutterContent *content,
float *width,
float *height)
{
MetaBackgroundContent *background_content = META_BACKGROUND_CONTENT (content);
MetaRectangle monitor_geometry;
meta_display_get_monitor_geometry (background_content->display,
background_content->monitor,
&monitor_geometry);
if (width)
*width = monitor_geometry.width;
if (height)
*height = monitor_geometry.height;
return TRUE;
}
static void
clutter_content_iface_init (ClutterContentInterface *iface)
{
iface->paint_content = meta_background_content_paint_content;
iface->get_preferred_size = meta_background_content_get_preferred_size;
}
static void
set_monitor (MetaBackgroundContent *self,
int monitor)
{
MetaRectangle old_monitor_geometry;
MetaRectangle new_monitor_geometry;
MetaDisplay *display = self->display;
if(self->monitor == monitor)
return;
meta_display_get_monitor_geometry (display, self->monitor, &old_monitor_geometry);
meta_display_get_monitor_geometry (display, monitor, &new_monitor_geometry);
if(old_monitor_geometry.height != new_monitor_geometry.height)
invalidate_pipeline (self, CHANGED_GRADIENT_PARAMETERS);
self->monitor = monitor;
}
static void
meta_background_content_dispose (GObject *object)
{
MetaBackgroundContent *self = META_BACKGROUND_CONTENT (object);
set_clip_region (self, NULL);
set_unobscured_region (self, NULL);
meta_background_content_set_background (self, NULL);
g_clear_pointer (&self->pipeline, cogl_object_unref);
G_OBJECT_CLASS (meta_background_content_parent_class)->dispose (object);
}
static void
meta_background_content_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaBackgroundContent *self = META_BACKGROUND_CONTENT (object);
switch (prop_id)
{
case PROP_META_DISPLAY:
self->display = g_value_get_object (value);
break;
case PROP_MONITOR:
set_monitor (self, g_value_get_int (value));
break;
case PROP_BACKGROUND:
meta_background_content_set_background (self, g_value_get_object (value));
break;
case PROP_GRADIENT:
meta_background_content_set_gradient (self,
g_value_get_boolean (value),
self->gradient_height,
self->gradient_max_darkness);
break;
case PROP_GRADIENT_HEIGHT:
meta_background_content_set_gradient (self,
self->gradient,
g_value_get_int (value),
self->gradient_max_darkness);
break;
case PROP_GRADIENT_MAX_DARKNESS:
meta_background_content_set_gradient (self,
self->gradient,
self->gradient_height,
g_value_get_double (value));
break;
case PROP_VIGNETTE:
meta_background_content_set_vignette (self,
g_value_get_boolean (value),
self->vignette_brightness,
self->vignette_sharpness);
break;
case PROP_VIGNETTE_SHARPNESS:
meta_background_content_set_vignette (self,
self->vignette,
self->vignette_brightness,
g_value_get_double (value));
break;
case PROP_VIGNETTE_BRIGHTNESS:
meta_background_content_set_vignette (self,
self->vignette,
g_value_get_double (value),
self->vignette_sharpness);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_background_content_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaBackgroundContent *self = META_BACKGROUND_CONTENT (object);
switch (prop_id)
{
case PROP_META_DISPLAY:
g_value_set_object (value, self->display);
break;
case PROP_MONITOR:
g_value_set_int (value, self->monitor);
break;
case PROP_BACKGROUND:
g_value_set_object (value, self->background);
break;
case PROP_GRADIENT:
g_value_set_boolean (value, self->gradient);
break;
case PROP_GRADIENT_HEIGHT:
g_value_set_int (value, self->gradient_height);
break;
case PROP_GRADIENT_MAX_DARKNESS:
g_value_set_double (value, self->gradient_max_darkness);
break;
case PROP_VIGNETTE:
g_value_set_boolean (value, self->vignette);
break;
case PROP_VIGNETTE_BRIGHTNESS:
g_value_set_double (value, self->vignette_brightness);
break;
case PROP_VIGNETTE_SHARPNESS:
g_value_set_double (value, self->vignette_sharpness);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_background_content_class_init (MetaBackgroundContentClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = meta_background_content_dispose;
object_class->set_property = meta_background_content_set_property;
object_class->get_property = meta_background_content_get_property;
properties[PROP_META_DISPLAY] =
g_param_spec_object ("meta-display",
"MetaDisplay",
"MetaDisplay",
META_TYPE_DISPLAY,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
properties[PROP_MONITOR] =
g_param_spec_int ("monitor",
"monitor",
"monitor",
0, G_MAXINT, 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
properties[PROP_BACKGROUND] =
g_param_spec_object ("background",
"Background",
"MetaBackground object holding background parameters",
META_TYPE_BACKGROUND,
G_PARAM_READWRITE);
properties[PROP_GRADIENT] =
g_param_spec_boolean ("gradient",
"Gradient",
"Whether gradient effect is enabled",
FALSE,
G_PARAM_READWRITE);
properties[PROP_GRADIENT_HEIGHT] =
g_param_spec_int ("gradient-height",
"Gradient Height",
"Height of gradient effect",
0, G_MAXINT, 0,
G_PARAM_READWRITE);
properties[PROP_GRADIENT_MAX_DARKNESS] =
g_param_spec_double ("gradient-max-darkness",
"Gradient Max Darkness",
"How dark is the gradient initially",
0.0, 1.0, 0.0,
G_PARAM_READWRITE);
properties[PROP_VIGNETTE] =
g_param_spec_boolean ("vignette",
"Vignette",
"Whether vignette effect is enabled",
FALSE,
G_PARAM_READWRITE);
properties[PROP_VIGNETTE_BRIGHTNESS] =
g_param_spec_double ("brightness",
"Vignette Brightness",
"Brightness of vignette effect",
0.0, 1.0, 1.0,
G_PARAM_READWRITE);
properties[PROP_VIGNETTE_SHARPNESS] =
g_param_spec_double ("vignette-sharpness",
"Vignette Sharpness",
"Sharpness of vignette effect",
0.0, G_MAXDOUBLE, 0.0,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
meta_background_content_init (MetaBackgroundContent *self)
{
self->gradient = FALSE;
self->gradient_height = 0;
self->gradient_max_darkness = 0.0;
self->vignette = FALSE;
self->vignette_brightness = 1.0;
self->vignette_sharpness = 0.0;
}
/**
* meta_background_content_new:
* @monitor: Index of the monitor for which to draw the background
*
* Creates a new actor to draw the background for the given monitor.
*
* Return value: (transfer full): the newly created background actor
*/
ClutterContent *
meta_background_content_new (MetaDisplay *display,
int monitor)
{
return g_object_new (META_TYPE_BACKGROUND_CONTENT,
"meta-display", display,
"monitor", monitor,
NULL);
}
void
meta_background_content_set_background (MetaBackgroundContent *self,
MetaBackground *background)
{
g_return_if_fail (META_IS_BACKGROUND_CONTENT (self));
g_return_if_fail (background == NULL || META_IS_BACKGROUND (background));
if (background == self->background)
return;
if (self->background)
{
g_signal_handlers_disconnect_by_func (self->background,
(gpointer)on_background_changed,
self);
g_clear_object (&self->background);
}
if (background)
{
self->background = g_object_ref (background);
g_signal_connect (self->background, "changed",
G_CALLBACK (on_background_changed), self);
}
invalidate_pipeline (self, CHANGED_BACKGROUND);
clutter_content_invalidate (CLUTTER_CONTENT (self));
}
void
meta_background_content_set_gradient (MetaBackgroundContent *self,
gboolean enabled,
int height,
double max_darkness)
{
gboolean changed = FALSE;
g_return_if_fail (META_IS_BACKGROUND_CONTENT (self));
g_return_if_fail (height >= 0);
g_return_if_fail (max_darkness >= 0. && max_darkness <= 1.);
enabled = enabled != FALSE && height != 0;
if (enabled != self->gradient)
{
self->gradient = enabled;
invalidate_pipeline (self, CHANGED_EFFECTS);
changed = TRUE;
}
if (height != self->gradient_height || max_darkness != self->gradient_max_darkness)
{
self->gradient_height = height;
self->gradient_max_darkness = max_darkness;
invalidate_pipeline (self, CHANGED_GRADIENT_PARAMETERS);
changed = TRUE;
}
if (changed)
clutter_content_invalidate (CLUTTER_CONTENT (self));
}
void
meta_background_content_set_vignette (MetaBackgroundContent *self,
gboolean enabled,
double brightness,
double sharpness)
{
gboolean changed = FALSE;
g_return_if_fail (META_IS_BACKGROUND_CONTENT (self));
g_return_if_fail (brightness >= 0. && brightness <= 1.);
g_return_if_fail (sharpness >= 0.);
enabled = enabled != FALSE;
if (enabled != self->vignette)
{
self->vignette = enabled;
invalidate_pipeline (self, CHANGED_EFFECTS);
changed = TRUE;
}
if (brightness != self->vignette_brightness || sharpness != self->vignette_sharpness)
{
self->vignette_brightness = brightness;
self->vignette_sharpness = sharpness;
invalidate_pipeline (self, CHANGED_VIGNETTE_PARAMETERS);
changed = TRUE;
}
if (changed)
clutter_content_invalidate (CLUTTER_CONTENT (self));
}
cairo_region_t *
meta_background_content_get_clip_region (MetaBackgroundContent *self)
{
return self->clip_region;
}
void
meta_background_content_cull_out (MetaBackgroundContent *self,
cairo_region_t *unobscured_region,
cairo_region_t *clip_region)
{
set_unobscured_region (self, unobscured_region);
set_clip_region (self, clip_region);
}
void
meta_background_content_reset_culling (MetaBackgroundContent *self)
{
set_unobscured_region (self, NULL);
set_clip_region (self, NULL);
}

View File

@@ -127,10 +127,12 @@ meta_compositor_native_pre_paint (MetaCompositor *compositor)
}
MetaCompositorNative *
meta_compositor_native_new (MetaDisplay *display)
meta_compositor_native_new (MetaDisplay *display,
MetaBackend *backend)
{
return g_object_new (META_TYPE_COMPOSITOR_NATIVE,
"display", display,
"backend", backend,
NULL);
}

View File

@@ -27,6 +27,7 @@
G_DECLARE_FINAL_TYPE (MetaCompositorNative, meta_compositor_native,
META, COMPOSITOR_NATIVE, MetaCompositor)
MetaCompositorNative * meta_compositor_native_new (MetaDisplay *display);
MetaCompositorNative * meta_compositor_native_new (MetaDisplay *display,
MetaBackend *backend);
#endif /* META_COMPOSITOR_NATIVE_H */

View File

@@ -37,10 +37,12 @@ meta_compositor_server_unmanage (MetaCompositor *compositor)
}
MetaCompositorServer *
meta_compositor_server_new (MetaDisplay *display)
meta_compositor_server_new (MetaDisplay *display,
MetaBackend *backend)
{
return g_object_new (META_TYPE_COMPOSITOR_SERVER,
"display", display,
"backend", backend,
NULL);
}

View File

@@ -32,6 +32,7 @@ struct _MetaCompositorServerClass
MetaCompositorClass parent_class;
};
MetaCompositorServer * meta_compositor_server_new (MetaDisplay *display);
MetaCompositorServer * meta_compositor_server_new (MetaDisplay *display,
MetaBackend *backend);
#endif /* META_COMPOSITOR_SERVER_H */

View File

@@ -371,10 +371,12 @@ meta_compositor_x11_get_output_xwindow (MetaCompositorX11 *compositor_x11)
}
MetaCompositorX11 *
meta_compositor_x11_new (MetaDisplay *display)
meta_compositor_x11_new (MetaDisplay *display,
MetaBackend *backend)
{
return g_object_new (META_TYPE_COMPOSITOR_X11,
"display", display,
"backend", backend,
NULL);
}

View File

@@ -27,7 +27,8 @@
G_DECLARE_FINAL_TYPE (MetaCompositorX11, meta_compositor_x11,
META, COMPOSITOR_X11, MetaCompositor)
MetaCompositorX11 * meta_compositor_x11_new (MetaDisplay *display);
MetaCompositorX11 * meta_compositor_x11_new (MetaDisplay *display,
MetaBackend *backend);
void meta_compositor_x11_process_xevent (MetaCompositorX11 *compositor_x11,
XEvent *xevent,

View File

@@ -30,6 +30,7 @@
#include "clutter/clutter.h"
#include "meta/meta-backend.h"
#include "meta/meta-background-actor.h"
#include "meta/meta-background-content.h"
#include "meta/meta-background-group.h"
#include "meta/meta-monitor-manager.h"
#include "meta/meta-plugin.h"
@@ -332,6 +333,8 @@ on_monitors_changed (MetaMonitorManager *monitor_manager,
n = meta_display_get_n_monitors (display);
for (i = 0; i < n; i++)
{
MetaBackgroundContent *background_content;
ClutterContent *content;
MetaRectangle rect;
ClutterActor *background_actor;
MetaBackground *background;
@@ -340,6 +343,8 @@ on_monitors_changed (MetaMonitorManager *monitor_manager,
meta_display_get_monitor_geometry (display, i, &rect);
background_actor = meta_background_actor_new (display, i);
content = clutter_actor_get_content (background_actor);
background_content = META_BACKGROUND_CONTENT (content);
clutter_actor_set_position (background_actor, rect.x, rect.y);
clutter_actor_set_size (background_actor, rect.width, rect.height);
@@ -356,13 +361,10 @@ on_monitors_changed (MetaMonitorManager *monitor_manager,
background = meta_background_new (display);
meta_background_set_color (background, &color);
meta_background_actor_set_background (META_BACKGROUND_ACTOR (background_actor), background);
meta_background_content_set_background (background_content, background);
g_object_unref (background);
meta_background_actor_set_vignette (META_BACKGROUND_ACTOR (background_actor),
TRUE,
0.5,
0.5);
meta_background_content_set_vignette (background_content, TRUE, 0.5, 0.5);
clutter_actor_add_child (self->priv->background_group, background_actor);
}

View File

@@ -573,17 +573,17 @@ meta_display_remove_pending_pings_for_window (MetaDisplay *display,
static MetaCompositor *
create_compositor (MetaDisplay *display)
{
#ifdef HAVE_WAYLAND
MetaBackend *backend = meta_get_backend ();
#ifdef HAVE_WAYLAND
#ifdef HAVE_NATIVE_BACKEND
if (META_IS_BACKEND_NATIVE (backend))
return META_COMPOSITOR (meta_compositor_native_new (display));
return META_COMPOSITOR (meta_compositor_native_new (display, backend));
#endif
if (META_IS_BACKEND_X11_NESTED (backend))
return META_COMPOSITOR (meta_compositor_server_new (display));
return META_COMPOSITOR (meta_compositor_server_new (display, backend));
#endif
return META_COMPOSITOR (meta_compositor_x11_new (display));
return META_COMPOSITOR (meta_compositor_x11_new (display, backend));
}
static void

View File

@@ -30,6 +30,7 @@
#include "config.h"
#include "backends/meta-backend-private.h"
#include "backends/meta-keymap-utils.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-monitor-manager-private.h"
#include "backends/x11/meta-backend-x11.h"
@@ -741,7 +742,7 @@ create_us_layout (void)
names.variant = "";
names.options = "";
context = xkb_context_new (XKB_CONTEXT_NO_FLAGS);
context = meta_create_xkb_context ();
keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
xkb_context_unref (context);

View File

@@ -152,6 +152,7 @@ owner_changed_cb (MetaSelection *selection,
display->saved_clipboard);
g_set_object (&display->selection_source, new_owner);
meta_selection_set_owner (selection, selection_type, new_owner);
g_object_unref (new_owner);
}
}
@@ -170,6 +171,7 @@ meta_clipboard_manager_shutdown (MetaDisplay *display)
{
MetaSelection *selection;
g_clear_object (&display->selection_source);
g_clear_pointer (&display->saved_clipboard, g_bytes_unref);
g_clear_pointer (&display->saved_clipboard_mimetype, g_free);
selection = meta_display_get_selection (display);

View File

@@ -365,103 +365,119 @@ move_window_above (GArray *stack,
static gboolean
meta_stack_op_apply (MetaStackTracker *tracker,
MetaStackOp *op,
GArray *stack,
GArray *stack,
ApplyFlags apply_flags)
{
switch (op->any.type)
{
case STACK_OP_ADD:
{
int old_pos;
if (META_STACK_ID_IS_X11 (op->add.window) &&
(apply_flags & NO_RESTACK_X_WINDOWS) != 0)
return FALSE;
int old_pos = find_window (stack, op->add.window);
if (old_pos >= 0)
{
g_warning ("STACK_OP_ADD: window %s already in stack",
get_window_desc (tracker, op->add.window));
return FALSE;
}
old_pos = find_window (stack, op->add.window);
if (old_pos >= 0)
{
meta_topic (META_DEBUG_STACK,
"STACK_OP_ADD: window %s already in stack",
get_window_desc (tracker, op->add.window));
return FALSE;
}
g_array_append_val (stack, op->add.window);
return TRUE;
g_array_append_val (stack, op->add.window);
return TRUE;
}
case STACK_OP_REMOVE:
{
int old_pos;
if (META_STACK_ID_IS_X11 (op->remove.window) &&
(apply_flags & NO_RESTACK_X_WINDOWS) != 0)
return FALSE;
int old_pos = find_window (stack, op->remove.window);
if (old_pos < 0)
{
g_warning ("STACK_OP_REMOVE: window %s not in stack",
get_window_desc (tracker, op->remove.window));
return FALSE;
}
old_pos = find_window (stack, op->remove.window);
if (old_pos < 0)
{
meta_topic (META_DEBUG_STACK,
"STACK_OP_REMOVE: window %s not in stack",
get_window_desc (tracker, op->remove.window));
return FALSE;
}
g_array_remove_index (stack, old_pos);
return TRUE;
g_array_remove_index (stack, old_pos);
return TRUE;
}
case STACK_OP_RAISE_ABOVE:
{
int old_pos = find_window (stack, op->raise_above.window);
int above_pos;
if (old_pos < 0)
{
g_warning ("STACK_OP_RAISE_ABOVE: window %s not in stack",
get_window_desc (tracker, op->raise_above.window));
return FALSE;
}
int old_pos;
int above_pos;
old_pos = find_window (stack, op->raise_above.window);
if (old_pos < 0)
{
meta_topic (META_DEBUG_STACK,
"STACK_OP_RAISE_ABOVE: window %s not in stack",
get_window_desc (tracker, op->raise_above.window));
return FALSE;
}
if (op->raise_above.sibling)
{
above_pos = find_window (stack, op->raise_above.sibling);
if (above_pos < 0)
{
g_warning ("STACK_OP_RAISE_ABOVE: sibling window %s not in stack",
get_window_desc (tracker, op->raise_above.sibling));
return FALSE;
}
}
else
{
above_pos = -1;
}
{
above_pos = find_window (stack, op->raise_above.sibling);
if (above_pos < 0)
{
meta_topic (META_DEBUG_STACK,
"STACK_OP_RAISE_ABOVE: sibling window %s not in stack",
get_window_desc (tracker, op->raise_above.sibling));
return FALSE;
}
}
else
{
above_pos = -1;
}
return move_window_above (stack, op->raise_above.window, old_pos, above_pos,
return move_window_above (stack, op->raise_above.window, old_pos, above_pos,
apply_flags);
}
case STACK_OP_LOWER_BELOW:
{
int old_pos = find_window (stack, op->lower_below.window);
int above_pos;
if (old_pos < 0)
{
g_warning ("STACK_OP_LOWER_BELOW: window %s not in stack",
get_window_desc (tracker, op->lower_below.window));
return FALSE;
}
int old_pos;
int above_pos;
old_pos = find_window (stack, op->raise_above.window);
if (old_pos < 0)
{
meta_topic (META_DEBUG_STACK,
"STACK_OP_LOWER_BELOW: window %s not in stack",
get_window_desc (tracker, op->lower_below.window));
return FALSE;
}
if (op->lower_below.sibling)
{
int below_pos = find_window (stack, op->lower_below.sibling);
if (below_pos < 0)
{
g_warning ("STACK_OP_LOWER_BELOW: sibling window %s not in stack",
get_window_desc (tracker, op->lower_below.sibling));
return FALSE;
}
{
int below_pos;
above_pos = below_pos - 1;
}
else
{
above_pos = stack->len - 1;
}
below_pos = find_window (stack, op->lower_below.sibling);
if (below_pos < 0)
{
meta_topic (META_DEBUG_STACK,
"STACK_OP_LOWER_BELOW: sibling window %s not in stack",
get_window_desc (tracker, op->lower_below.sibling));
return FALSE;
}
return move_window_above (stack, op->lower_below.window, old_pos, above_pos,
above_pos = below_pos - 1;
}
else
{
above_pos = stack->len - 1;
}
return move_window_above (stack, op->lower_below.window, old_pos, above_pos,
apply_flags);
}
}

View File

@@ -195,6 +195,8 @@ mutter_sources = [
'backends/meta-input-mapper-private.h',
'backends/meta-input-settings.c',
'backends/meta-input-settings-private.h',
'backends/meta-keymap-utils.c',
'backends/meta-keymap-utils.h',
'backends/meta-logical-monitor.c',
'backends/meta-logical-monitor.h',
'backends/meta-monitor.c',
@@ -287,6 +289,8 @@ mutter_sources = [
'compositor/compositor-private.h',
'compositor/meta-background-actor.c',
'compositor/meta-background-actor-private.h',
'compositor/meta-background-content.c',
'compositor/meta-background-content-private.h',
'compositor/meta-background.c',
'compositor/meta-background-group.c',
'compositor/meta-background-image.c',
@@ -529,6 +533,12 @@ if have_wayland
'wayland/meta-wayland-inhibit-shortcuts-dialog.c',
'wayland/meta-wayland-inhibit-shortcuts-dialog.h',
'wayland/meta-wayland-inhibit-shortcuts.h',
'wayland/meta-wayland-inputfd-evdev-device.c',
'wayland/meta-wayland-inputfd-evdev-device.h',
'wayland/meta-wayland-inputfd-manager.c',
'wayland/meta-wayland-inputfd-manager.h',
'wayland/meta-wayland-inputfd-seat.c',
'wayland/meta-wayland-inputfd-seat.h',
'wayland/meta-wayland-input-device.c',
'wayland/meta-wayland-input-device.h',
'wayland/meta-wayland-keyboard.c',
@@ -810,6 +820,7 @@ if have_wayland
['gtk-primary-selection', 'private', ],
['gtk-shell', 'private', ],
['gtk-text-input', 'private', ],
['inputfd', 'unstable', 'v1', ],
['keyboard-shortcuts-inhibit', 'unstable', 'v1', ],
['linux-dmabuf', 'unstable', 'v1', ],
['pointer-constraints', 'unstable', 'v1', ],

View File

@@ -11,6 +11,7 @@ mutter_public_headers = [
'meta-backend.h',
'meta-background.h',
'meta-background-actor.h',
'meta-background-content.h',
'meta-background-group.h',
'meta-background-image.h',
'meta-close-dialog.h',

View File

@@ -47,24 +47,4 @@ META_EXPORT
ClutterActor *meta_background_actor_new (MetaDisplay *display,
int monitor);
META_EXPORT
void meta_background_actor_set_background (MetaBackgroundActor *self,
MetaBackground *background);
META_EXPORT
void meta_background_actor_set_gradient (MetaBackgroundActor *self,
gboolean enabled,
int height,
double tone_start);
META_EXPORT
void meta_background_actor_set_monitor (MetaBackgroundActor *self,
int monitor);
META_EXPORT
void meta_background_actor_set_vignette (MetaBackgroundActor *self,
gboolean enabled,
double brightness,
double sharpness);
#endif /* META_BACKGROUND_ACTOR_H */

View File

@@ -0,0 +1,66 @@
/*
* meta-background-content.h: ClutterContent for painting the wallpaper
*
* Copyright 2010 Red Hat, Inc.
* Copyright 2020 Endless Foundation
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef META_BACKGROUND_CONTENT_H
#define META_BACKGROUND_CONTENT_H
#include <gsettings-desktop-schemas/gdesktop-enums.h>
#include "clutter/clutter.h"
#include "meta/meta-background.h"
/**
* MetaBackgroundContent:
*
* This class handles tracking and painting the root window background.
* By integrating with #MetaWindowGroup we can avoid painting parts of
* the background that are obscured by other windows.
*/
#define META_TYPE_BACKGROUND_CONTENT (meta_background_content_get_type ())
META_EXPORT
G_DECLARE_FINAL_TYPE (MetaBackgroundContent,
meta_background_content,
META, BACKGROUND_CONTENT,
GObject)
META_EXPORT
ClutterContent *meta_background_content_new (MetaDisplay *display,
int monitor);
META_EXPORT
void meta_background_content_set_background (MetaBackgroundContent *self,
MetaBackground *background);
META_EXPORT
void meta_background_content_set_gradient (MetaBackgroundContent *self,
gboolean enabled,
int height,
double tone_start);
META_EXPORT
void meta_background_content_set_vignette (MetaBackgroundContent *self,
gboolean enabled,
double brightness,
double sharpness);
#endif /* META_BACKGROUND_CONTENT_H */

View File

@@ -5,8 +5,6 @@
#include <clutter/clutter.h>
typedef struct {
ClutterActor *stage;
guint no_display : 1;
} ClutterTestEnvironment;
@@ -98,18 +96,9 @@ out:
ClutterActor *
clutter_test_get_stage (void)
{
g_assert (test_environ != NULL);
MetaBackend *backend = meta_get_backend ();
if (test_environ->stage == NULL)
{
/* create a stage, and ensure that it goes away at the end */
test_environ->stage = clutter_stage_new ();
clutter_actor_set_name (test_environ->stage, "Test Stage");
g_object_add_weak_pointer (G_OBJECT (test_environ->stage),
(gpointer *) &test_environ->stage);
}
return test_environ->stage;
return meta_backend_get_stage (backend);
}
typedef struct {
@@ -122,11 +111,13 @@ static void
clutter_test_func_wrapper (gconstpointer data_)
{
const ClutterTestData *data = data_;
ClutterActor *stage;
g_test_log_set_fatal_handler (log_func, NULL);
/* ensure that the previous test state has been cleaned up */
g_assert_null (test_environ->stage);
stage = clutter_test_get_stage ();
g_assert_false (clutter_actor_is_mapped (stage));
if (test_environ->no_display)
{
@@ -151,11 +142,8 @@ out:
if (data->test_notify != NULL)
data->test_notify (data->test_data);
if (test_environ->stage != NULL)
{
clutter_actor_destroy (test_environ->stage);
g_assert_null (test_environ->stage);
}
clutter_actor_remove_all_children (stage);
clutter_actor_hide (stage);
}
/**

View File

@@ -132,7 +132,7 @@ check_coords (TestState *state,
gint y_2,
const graphene_point3d_t *verts)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("checking that (%i,%i,%i,%i) \xe2\x89\x88 (%i,%i,%i,%i): %s\n",
x_1, y_1, x_2, y_2,
(int) (verts[0].x),
@@ -200,7 +200,7 @@ test_anchor_point (TestState *state)
/* Change the anchor point with each of the gravities */
for (i = 0; i < G_N_ELEMENTS (gravities); i++)
{
if (g_test_verbose ())
if (!g_test_quiet ())
{
GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY);
GEnumValue *value = g_enum_get_value (gravity_class,
@@ -356,7 +356,7 @@ test_scale_center (TestState *state)
/* Change the anchor point with each of the gravities */
for (i = 0; i < G_N_ELEMENTS (gravities); i++)
{
if (g_test_verbose ())
if (!g_test_quiet ())
{
GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY);
GEnumValue *value = g_enum_get_value (gravity_class,
@@ -472,7 +472,7 @@ test_rotate_center (TestState *state)
char prop_name[] = "rotation-angle- ";
prop_name[sizeof (prop_name) - 2] = i - CLUTTER_X_AXIS + 'x';
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Setting %s to 90 degrees\n", prop_name);
g_object_set (rect, prop_name, 90.0, NULL);
@@ -531,7 +531,7 @@ test_rotate_center (TestState *state)
char prop_name[] = "rotation-angle- ";
prop_name[sizeof (prop_name) - 2] = i - CLUTTER_X_AXIS + 'x';
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Setting %s to 90 degrees with center 10,20,0\n", prop_name);
clutter_actor_set_rotation (rect, i, 90.0, 10, 20, 0);
@@ -605,7 +605,7 @@ test_rotate_center (TestState *state)
/* Try rotating the z with all of the gravities */
for (i = 0; i < G_N_ELEMENTS (gravities); i++)
{
if (g_test_verbose ())
if (!g_test_quiet ())
{
GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY);
GEnumValue *value = g_enum_get_value (gravity_class,

View File

@@ -39,7 +39,7 @@ test_destroy_add (ClutterContainer *container,
{
TestDestroy *self = TEST_DESTROY (container);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Adding '%s' (type:%s)\n",
clutter_actor_get_name (actor),
G_OBJECT_TYPE_NAME (actor));
@@ -54,7 +54,7 @@ test_destroy_remove (ClutterContainer *container,
{
TestDestroy *self = TEST_DESTROY (container);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Removing '%s' (type:%s)\n",
clutter_actor_get_name (actor),
G_OBJECT_TYPE_NAME (actor));
@@ -81,7 +81,7 @@ test_destroy_destroy (ClutterActor *self)
if (test->bg != NULL)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Destroying '%s' (type:%s)\n",
clutter_actor_get_name (test->bg),
G_OBJECT_TYPE_NAME (test->bg));
@@ -92,7 +92,7 @@ test_destroy_destroy (ClutterActor *self)
if (test->label != NULL)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Destroying '%s' (type:%s)\n",
clutter_actor_get_name (test->label),
G_OBJECT_TYPE_NAME (test->label));
@@ -174,7 +174,7 @@ actor_destruction (void)
g_object_add_weak_pointer (G_OBJECT (test), (gpointer *) &test);
g_object_add_weak_pointer (G_OBJECT (child), (gpointer *) &child);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Adding external child...\n");
clutter_actor_set_name (child, "Child");
@@ -184,7 +184,7 @@ actor_destruction (void)
g_signal_connect (child, "notify", G_CALLBACK (on_notify), &property_changed);
g_signal_connect (child, "destroy", G_CALLBACK (on_destroy), &destroy_called);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Calling destroy()...\n");
clutter_actor_destroy (test);

View File

@@ -408,7 +408,7 @@ actor_added (ClutterContainer *container,
int *counter = data;
ClutterActor *old_child;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Adding actor '%s'\n", clutter_actor_get_name (child));
old_child = clutter_actor_get_child_at_index (actor, 0);
@@ -425,7 +425,7 @@ actor_removed (ClutterContainer *container,
{
int *counter = data;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Removing actor '%s'\n", clutter_actor_get_name (child));
*counter += 1;

View File

@@ -15,7 +15,7 @@ actor_initial_state (void)
g_object_ref_sink (actor);
g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("initial state - visible: %s, realized: %s, mapped: %s\n",
CLUTTER_ACTOR_IS_VISIBLE (actor) ? "yes" : "no",
CLUTTER_ACTOR_IS_REALIZED (actor) ? "yes" : "no",
@@ -40,7 +40,7 @@ actor_shown_not_parented (void)
clutter_actor_show (actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("show without a parent - visible: %s, realized: %s, mapped: %s\n",
CLUTTER_ACTOR_IS_VISIBLE (actor) ? "yes" : "no",
CLUTTER_ACTOR_IS_REALIZED (actor) ? "yes" : "no",
@@ -92,7 +92,7 @@ actor_mapped (void)
clutter_actor_add_child (stage, actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("adding to a container should map - "
"visible: %s, realized: %s, mapped: %s\n",
CLUTTER_ACTOR_IS_VISIBLE (actor) ? "yes" : "no",
@@ -105,7 +105,7 @@ actor_mapped (void)
clutter_actor_hide (actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("hiding should unmap - "
"visible: %s, realized: %s, mapped: %s\n",
CLUTTER_ACTOR_IS_VISIBLE (actor) ? "yes" : "no",

View File

@@ -40,7 +40,7 @@ actor_iter_traverse_children (void)
g_assert (CLUTTER_IS_ACTOR (child));
g_assert (clutter_actor_get_parent (child) == actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child));
if (i == 0)
@@ -63,7 +63,7 @@ actor_iter_traverse_children (void)
g_assert (CLUTTER_IS_ACTOR (child));
g_assert (clutter_actor_get_parent (child) == actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child));
if (i == 0)
@@ -115,7 +115,7 @@ actor_iter_traverse_remove (void)
g_assert (CLUTTER_IS_ACTOR (child));
g_assert (clutter_actor_get_parent (child) == actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child));
if (i == 0)
@@ -176,7 +176,7 @@ actor_iter_assignment (void)
g_assert (CLUTTER_IS_ACTOR (child));
g_assert (clutter_actor_get_parent (child) == actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("actor %2d = '%s'\n", i, clutter_actor_get_name (child));
if (i == 0)
@@ -196,7 +196,7 @@ actor_iter_assignment (void)
{
g_assert (clutter_actor_get_parent (child) == actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("actor %2d = '%s'\n", i, clutter_actor_get_name (child));
if (i == n_actors - 1)

View File

@@ -16,7 +16,7 @@ opacity_label (void)
label = clutter_text_new_with_text ("Sans 18px", "Label, 50% opacity");
clutter_text_set_color (CLUTTER_TEXT (label), &label_color);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("label 50%%.get_color()/1\n");
clutter_text_get_color (CLUTTER_TEXT (label), &color_check);
g_assert (color_check.alpha == label_color.alpha);
@@ -24,16 +24,16 @@ opacity_label (void)
clutter_actor_add_child (stage, label);
clutter_actor_set_position (label, 10, 10);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("label 50%%.get_color()/2\n");
clutter_text_get_color (CLUTTER_TEXT (label), &color_check);
g_assert (color_check.alpha == label_color.alpha);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("label 50%%.get_paint_opacity()/1\n");
g_assert (clutter_actor_get_paint_opacity (label) == 255);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("label 50%%.get_paint_opacity()/2\n");
clutter_actor_set_opacity (label, 128);
g_assert (clutter_actor_get_paint_opacity (label) == 128);
@@ -54,19 +54,19 @@ opacity_rectangle (void)
clutter_actor_set_size (rect, 128, 128);
clutter_actor_set_position (rect, 150, 90);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("rect 100%%.get_color()/1\n");
clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check);
g_assert (color_check.alpha == rect_color.alpha);
clutter_actor_add_child (stage, rect);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("rect 100%%.get_color()/2\n");
clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check);
g_assert (color_check.alpha == rect_color.alpha);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("rect 100%%.get_paint_opacity()\n");
g_assert (clutter_actor_get_paint_opacity (rect) == 255);
}
@@ -93,19 +93,19 @@ opacity_paint (void)
label = clutter_text_new_with_text ("Sans 18px", "Label+Group, 25% opacity");
clutter_text_set_color (CLUTTER_TEXT (label), &label_color);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("label 50%% + group 50%%.get_color()/1\n");
clutter_text_get_color (CLUTTER_TEXT (label), &color_check);
g_assert (color_check.alpha == label_color.alpha);
clutter_container_add (CLUTTER_CONTAINER (group1), label, NULL);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("label 50%% + group 50%%.get_color()/2\n");
clutter_text_get_color (CLUTTER_TEXT (label), &color_check);
g_assert (color_check.alpha == label_color.alpha);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("label 50%% + group 50%%.get_paint_opacity() = 128\n");
g_assert (clutter_actor_get_paint_opacity (label) == 128);
@@ -118,19 +118,19 @@ opacity_paint (void)
rect = clutter_rectangle_new_with_color (&rect_color);
clutter_actor_set_size (rect, 128, 128);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("rect 100%% + group 100%% + group 50%%.get_color()/1\n");
clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check);
g_assert (color_check.alpha == rect_color.alpha);
clutter_container_add (CLUTTER_CONTAINER (group2), rect, NULL);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("rect 100%% + group 100%% + group 50%%.get_color()/2\n");
clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check);
g_assert (color_check.alpha == rect_color.alpha);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("rect 100%%.get_paint_opacity()\n");
g_assert (clutter_actor_get_paint_opacity (rect) == 128);
}

View File

@@ -49,7 +49,7 @@ on_timeout (gpointer data)
{
if (test_num == 0)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("No covering actor:\n");
}
if (test_num == 1)
@@ -62,7 +62,7 @@ on_timeout (gpointer data)
clutter_actor_add_child (state->stage, over_actor);
clutter_actor_hide (over_actor);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Invisible covering actor:\n");
}
else if (test_num == 2)
@@ -84,7 +84,7 @@ on_timeout (gpointer data)
*/
clutter_actor_allocate (over_actor, &over_actor_box);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Clipped covering actor:\n");
}
else if (test_num == 3)
@@ -98,7 +98,7 @@ on_timeout (gpointer data)
"blur",
clutter_blur_effect_new ());
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("With blur effect:\n");
}
@@ -121,13 +121,13 @@ on_timeout (gpointer data)
y * state->actor_height
+ state->actor_height / 2);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("% 3i,% 3i / %p -> ",
x, y, state->actors[y * ACTORS_X + x]);
if (actor == NULL)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("NULL: FAIL\n");
}
else if (actor == over_actor)
@@ -137,7 +137,7 @@ on_timeout (gpointer data)
&& y >= 2 && y < ACTORS_Y - 2)
pass = TRUE;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("over_actor: %s\n", pass ? "pass" : "FAIL");
}
else
@@ -148,7 +148,7 @@ on_timeout (gpointer data)
|| y < 2 || y >= ACTORS_Y - 2))
pass = TRUE;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("%p: %s\n", actor, pass ? "pass" : "FAIL");
}
@@ -206,7 +206,7 @@ actor_pick (void)
clutter_main ();
if (g_test_verbose ())
if (!g_test_quiet ())
{
if (!state.pass)
g_test_message ("Failed pass: %s[%d], actor index: %d [%p]\n",

View File

@@ -260,7 +260,7 @@ actor_shader_effect (void)
if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
return;
stage = clutter_stage_new ();
stage = clutter_test_get_stage ();
rect = make_actor (foo_old_shader_effect_get_type ());
clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);

View File

@@ -91,7 +91,7 @@ actor_preferred_size (void)
test = g_object_new (TEST_TYPE_ACTOR, NULL);
self = (TestActor *) test;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Preferred size\n");
clutter_actor_get_preferred_size (test,
@@ -105,7 +105,7 @@ actor_preferred_size (void)
g_assert_cmpfloat (nat_width, ==, min_width);
g_assert_cmpfloat (nat_height, ==, min_height);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Preferred width\n");
self->preferred_width_called = FALSE;
clutter_actor_get_preferred_width (test, 10, &min_width, &nat_width);
@@ -113,7 +113,7 @@ actor_preferred_size (void)
g_assert_cmpfloat (min_width, ==, 10);
g_assert_cmpfloat (nat_width, ==, 100);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Preferred height\n");
self->preferred_height_called = FALSE;
clutter_actor_get_preferred_height (test, 200, &min_height, &nat_height);
@@ -121,7 +121,7 @@ actor_preferred_size (void)
g_assert_cmpfloat (min_height, !=, 10);
g_assert_cmpfloat (nat_height, ==, 100);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Preferred width (cached)\n");
self->preferred_width_called = FALSE;
clutter_actor_get_preferred_width (test, 10, &min_width, &nat_width);
@@ -129,7 +129,7 @@ actor_preferred_size (void)
g_assert_cmpfloat (min_width, ==, 10);
g_assert_cmpfloat (nat_width, ==, 100);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Preferred height (cache eviction)\n");
self->preferred_height_called = FALSE;
clutter_actor_get_preferred_height (test, 10, &min_height, &nat_height);
@@ -152,7 +152,7 @@ actor_fixed_size (void)
rect = clutter_actor_new ();
g_object_ref_sink (rect);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Initial size is 0\n");
g_assert_cmpfloat (clutter_actor_get_width (rect), ==, 0);
@@ -160,7 +160,7 @@ actor_fixed_size (void)
clutter_actor_set_size (rect, 100, 100);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Explicit size set\n");
g_assert_cmpfloat (clutter_actor_get_width (rect), ==, 100);
@@ -173,7 +173,7 @@ actor_fixed_size (void)
"natural-height-set", &nat_height_set,
NULL);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Notification properties\n");
g_assert (min_width_set && nat_width_set);
@@ -183,7 +183,7 @@ actor_fixed_size (void)
&min_width, &min_height,
&nat_width, &nat_height);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Preferred size\n");
g_assert_cmpfloat (min_width, ==, 100);
@@ -193,7 +193,7 @@ actor_fixed_size (void)
clutter_actor_set_size (rect, -1, -1);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Explicit size unset\n");
g_object_get (G_OBJECT (rect),

View File

@@ -107,7 +107,7 @@ check_result (CallbackData *data)
if (g_strcmp0 (expected_text, text) != 0)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("text value differs %s vs %s\n", expected_text, text);
fail = TRUE;
}
@@ -116,7 +116,7 @@ check_result (CallbackData *data)
expected_char = g_utf8_get_char (g_utf8_offset_to_pointer (text, data->offset));
if (expected_char != unichar)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("text af offset differs\n");
fail = TRUE;
}
@@ -125,25 +125,25 @@ check_result (CallbackData *data)
ATK_XY_WINDOW);
if (x != data->extents_x)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("extents x position at index 0 differs (current value=%d)\n", x);
fail = TRUE;
}
if (y != data->extents_y)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("extents y position at index 0 differs (current value=%d)\n", y);
fail = TRUE;
}
if (width != data->extents_width)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("extents width at index 0 differs (current value=%d)\n", width);
fail = TRUE;
}
if (height != data->extents_height)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("extents height at index 0 differs (current value=%d)\n", height);
fail = TRUE;
}
@@ -151,7 +151,7 @@ check_result (CallbackData *data)
pos = atk_text_get_offset_at_point (cally_text, x, y, ATK_XY_WINDOW);
if (pos != data->offset)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("offset at position (%d, %d) differs (current value=%d)\n", x,
y, pos);
fail = TRUE;
@@ -161,20 +161,20 @@ check_result (CallbackData *data)
&start, &end);
if (start != 0)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("run attributes start offset is not 0: %d\n", start);
fail = TRUE;
}
if (end != g_utf8_strlen (text, -1))
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("run attributes end offset is not text length: %d\n", end);
fail = TRUE;
}
attrs = (GSList*) at_set;
fail = compare_lists (attrs, data->run_attributes);
if (fail && g_test_verbose ())
if (fail && !g_test_quiet ())
{
g_print ("run attributes mismatch\n");
dump_attribute_set (attrs);
@@ -183,7 +183,7 @@ check_result (CallbackData *data)
at_set = atk_text_get_default_attributes (cally_text);
attrs = (GSList*) at_set;
fail = compare_lists (attrs, data->default_attributes);
if (fail && g_test_verbose ())
if (fail && !g_test_quiet ())
{
g_print ("default attributes mismatch\n");
dump_attribute_set (attrs);
@@ -194,11 +194,11 @@ check_result (CallbackData *data)
if (fail)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("FAIL\n");
data->test_failed = TRUE;
}
else if (g_test_verbose ())
else if (!g_test_quiet ())
g_print ("pass\n");
return fail;
@@ -319,10 +319,10 @@ cally_text (void)
clutter_actor_destroy (data.stage);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("\nOverall result: ");
if (g_test_verbose ())
if (!g_test_quiet ())
{
if (data.test_failed)
g_print ("FAIL\n");

View File

@@ -18,7 +18,7 @@ color_hls_roundtrip (void)
g_assert_cmpfloat (hue, ==, 0.0);
g_assert (luminance >= 0.0 && luminance <= 1.0);
g_assert_cmpfloat (saturation, ==, 0.0);
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("RGB = { %x, %x, %x }, HLS = { %.2f, %.2f, %.2f }\n",
color.red,
@@ -48,7 +48,7 @@ color_hls_roundtrip (void)
g_assert (hue >= 0.0 && hue < 360.0);
g_assert (luminance >= 0.0 && luminance <= 1.0);
g_assert (saturation >= 0.0 && saturation <= 1.0);
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("RGB = { %x, %x, %x }, HLS = { %.2f, %.2f, %.2f }\n",
color.red,
@@ -92,7 +92,7 @@ color_from_string_valid (void)
ClutterColor color;
g_assert (clutter_color_from_string (&color, "#ff0000ff"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 0xff, 0, 0, 0xff }\n",
color.red,
@@ -106,7 +106,7 @@ color_from_string_valid (void)
g_assert_cmpuint (color.alpha, ==, 0xff);
g_assert (clutter_color_from_string (&color, "#0f0f"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 0, 0xff, 0, 0xff }\n",
color.red,
@@ -120,7 +120,7 @@ color_from_string_valid (void)
g_assert_cmpuint (color.alpha, ==, 0xff);
g_assert (clutter_color_from_string (&color, "#0000ff"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 0, 0, 0xff, 0xff }\n",
color.red,
@@ -134,7 +134,7 @@ color_from_string_valid (void)
g_assert_cmpuint (color.alpha, ==, 0xff);
g_assert (clutter_color_from_string (&color, "#abc"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 0xaa, 0xbb, 0xcc, 0xff }\n",
color.red,
@@ -148,7 +148,7 @@ color_from_string_valid (void)
g_assert_cmpuint (color.alpha, ==, 0xff);
g_assert (clutter_color_from_string (&color, "#123abc"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 0x12, 0x3a, 0xbc, 0xff }\n",
color.red,
@@ -162,7 +162,7 @@ color_from_string_valid (void)
g_assert (color.alpha == 0xff);
g_assert (clutter_color_from_string (&color, "rgb(255, 128, 64)"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 255, 128, 64, 255 }\n",
color.red,
@@ -176,7 +176,7 @@ color_from_string_valid (void)
g_assert_cmpuint (color.alpha, ==, 255);
g_assert (clutter_color_from_string (&color, "rgba ( 30%, 0, 25%, 0.5 ) "));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { %.1f, 0, %.1f, 128 }\n",
color.red,
@@ -192,7 +192,7 @@ color_from_string_valid (void)
g_assert_cmpuint (color.alpha, ==, 127);
g_assert (clutter_color_from_string (&color, "rgb( 50%, -50%, 150% )"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 127, 0, 255, 255 }\n",
color.red,
@@ -206,7 +206,7 @@ color_from_string_valid (void)
g_assert_cmpuint (color.alpha, ==, 255);
g_assert (clutter_color_from_string (&color, "hsl( 0, 100%, 50% )"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 255, 0, 0, 255 }\n",
color.red,
@@ -222,7 +222,7 @@ color_from_string_valid (void)
g_assert (clutter_color_from_string (&color, "hsl( 0, 100%, 50% )"));
g_assert (clutter_color_from_string (&color, "hsla( 0, 100%, 50%, 0.5 )"));
if (g_test_verbose ())
if (!g_test_quiet ())
{
g_print ("color = { %x, %x, %x, %x }, expected = { 255, 0, 0, 127 }\n",
color.red,
@@ -274,7 +274,7 @@ color_operators (void)
g_assert_cmpuint (op2.blue, ==, 0);
g_assert_cmpuint (op2.alpha, ==, 0xff);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Adding %x, %x; expected result: %x\n",
clutter_color_to_pixel (&op1),
clutter_color_to_pixel (&op2),
@@ -283,7 +283,7 @@ color_operators (void)
clutter_color_add (&op1, &op2, &res);
g_assert_cmpuint (clutter_color_to_pixel (&res), ==, 0xffff00ff);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Checking alpha channel on color add\n");
op1.alpha = 0xdd;
@@ -294,7 +294,7 @@ color_operators (void)
clutter_color_from_pixel (&op1, 0xffffffff);
clutter_color_from_pixel (&op2, 0xff00ffff);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Subtracting %x, %x; expected result: %x\n",
clutter_color_to_pixel (&op1),
clutter_color_to_pixel (&op2),
@@ -303,7 +303,7 @@ color_operators (void)
clutter_color_subtract (&op1, &op2, &res);
g_assert_cmpuint (clutter_color_to_pixel (&res), ==, 0x00ff00ff);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Checking alpha channel on color subtract\n");
op1.alpha = 0xdd;

View File

@@ -81,21 +81,21 @@ interval_from_script (void)
"test-script-interval.json",
NULL);
clutter_script_load_from_file (script, test_file, &error);
if (g_test_verbose () && error)
if (!g_test_quiet () && error)
g_printerr ("\tError: %s", error->message);
g_assert_no_error (error);
interval = CLUTTER_INTERVAL (clutter_script_get_object (script, "int-1"));
initial = clutter_interval_peek_initial_value (interval);
if (g_test_verbose ())
if (!g_test_quiet ())
g_test_message ("\tinitial ['%s'] = '%.2f'",
g_type_name (G_VALUE_TYPE (initial)),
g_value_get_float (initial));
g_assert (G_VALUE_HOLDS (initial, G_TYPE_FLOAT));
g_assert_cmpfloat (g_value_get_float (initial), ==, 23.3f);
final = clutter_interval_peek_final_value (interval);
if (g_test_verbose ())
if (!g_test_quiet ())
g_test_message ("\tfinal ['%s'] = '%.2f'",
g_type_name (G_VALUE_TYPE (final)),
g_value_get_float (final));

View File

@@ -33,6 +33,10 @@ clutter_conform_tests_general_tests = [
'color',
'interval',
'script-parser',
'timeline',
'timeline-interpolate',
'timeline-progress',
'timeline-rewind',
'units',
]
@@ -73,6 +77,7 @@ foreach test : clutter_conform_tests
test(test, test_executable,
suite: ['clutter', 'clutter/conform'],
env: test_env
env: test_env,
is_parallel: false,
)
endforeach

View File

@@ -556,7 +556,7 @@ path_test_get_length (CallbackData *data)
if (!(fabs (approx_length - 46340.f) / 46340.f <= 0.15f))
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("M 0 0 L 46340 0 - Expected 46340, got %d instead.", approx_length);
return FALSE;
@@ -567,7 +567,7 @@ path_test_get_length (CallbackData *data)
if (!(fabs (approx_length - 46341.f) / 46341.f <= 0.15f))
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("M 0 0 L 46341 0 - Expected 46341, got %d instead.", approx_length);
return FALSE;
@@ -580,7 +580,7 @@ path_test_get_length (CallbackData *data)
/* Allow 15% margin of error */
if (!(fabs (approx_length - actual_length) / (float) actual_length <= 0.15f))
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Expected %g, got %d instead.\n", actual_length, approx_length);
return FALSE;
@@ -724,12 +724,12 @@ path_base (TestConformSimpleFixture *fixture,
{
gboolean succeeded;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("%s... ", path_tests[i].desc);
succeeded = path_tests[i].func (&data) && compare_nodes (&data);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("%s\n", succeeded ? "ok" : "FAIL");
g_assert (succeeded);

View File

@@ -131,7 +131,7 @@ script_child (void)
test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-child.json", NULL);
clutter_script_load_from_file (script, test_file, &error);
if (g_test_verbose () && error)
if (!g_test_quiet () && error)
g_print ("Error: %s", error->message);
g_assert_no_error (error);
@@ -177,7 +177,7 @@ script_single (void)
test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-single.json", NULL);
clutter_script_load_from_file (script, test_file, &error);
if (g_test_verbose () && error)
if (!g_test_quiet () && error)
g_print ("Error: %s", error->message);
g_assert_no_error (error);
@@ -209,7 +209,7 @@ script_object_property (void)
test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-object-property.json", NULL);
clutter_script_load_from_file (script, test_file, &error);
if (g_test_verbose () && error)
if (!g_test_quiet () && error)
g_print ("Error: %s", error->message);
g_assert_no_error (error);
@@ -235,7 +235,7 @@ script_named_object (void)
test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-named-object.json", NULL);
clutter_script_load_from_file (script, test_file, &error);
if (g_test_verbose () && error)
if (!g_test_quiet () && error)
g_print ("Error: %s", error->message);
g_assert_no_error (error);
@@ -261,7 +261,7 @@ script_margin (void)
test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-margin.json", NULL);
clutter_script_load_from_file (script, test_file, &error);
if (g_test_verbose () && error)
if (!g_test_quiet () && error)
g_print ("Error: %s", error->message);
g_assert_no_error (error);

View File

@@ -68,7 +68,7 @@ check_result (CallbackData *data, const char *note,
PangoRectangle test_extents;
gboolean fail = FALSE;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("%s: ", note);
/* Force a redraw to get the on_paint handler to run */
@@ -79,7 +79,7 @@ check_result (CallbackData *data, const char *note,
pango_layout_get_extents (data->test_layout, NULL, &test_extents);
if (memcmp (&test_extents, &data->label_extents, sizeof (PangoRectangle)))
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("extents are different: expected: %d, %d, %d, %d "
"-> text: %d, %d, %d, %d\n",
test_extents.x / 1024,
@@ -95,18 +95,18 @@ check_result (CallbackData *data, const char *note,
}
else
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("extents are the same, ");
}
if (data->layout_changed)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("layout changed, ");
}
else
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("layout did not change, ");
}
@@ -115,14 +115,14 @@ check_result (CallbackData *data, const char *note,
if (fail)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("FAIL\n");
data->test_failed = TRUE;
}
else
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("pass\n");
}
@@ -283,10 +283,10 @@ text_cache (void)
clutter_actor_destroy (data.stage);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("\nOverall result: ");
if (g_test_verbose ())
if (!g_test_quiet ())
{
if (data.test_failed)
g_print ("FAIL\n");

View File

@@ -214,12 +214,12 @@ text_delete_chars (void)
for (j = 0; j < 4; j++)
clutter_text_insert_unichar (text, t->unichar);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("text: %s\n", clutter_text_get_text (text));
clutter_text_set_cursor_position (text, 2);
clutter_text_delete_chars (text, 1);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("text: %s (cursor at: %d)\n",
clutter_text_get_text (text),
clutter_text_get_cursor_position (text));
@@ -229,7 +229,7 @@ text_delete_chars (void)
clutter_text_set_cursor_position (text, 2);
clutter_text_delete_chars (text, 1);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("text: %s (cursor at: %d)\n",
clutter_text_get_text (text),
clutter_text_get_cursor_position (text));
@@ -503,7 +503,7 @@ text_idempotent_use_markup (void)
int bar_end_index = bar_start_index + strlen ("bar");
/* case 1: text -> use_markup */
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("text: '%s' -> use-markup: TRUE\n", contents);
text = g_object_new (CLUTTER_TYPE_TEXT,
@@ -511,7 +511,7 @@ text_idempotent_use_markup (void)
NULL);
g_object_ref_sink (text);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Contents: '%s' (expected: '%s')\n",
clutter_text_get_text (text),
display);
@@ -526,14 +526,14 @@ text_idempotent_use_markup (void)
clutter_actor_destroy (CLUTTER_ACTOR (text));
/* case 2: use_markup -> text */
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("use-markup: TRUE -> text: '%s'\n", contents);
text = g_object_new (CLUTTER_TYPE_TEXT,
"use-markup", TRUE, "text", contents,
NULL);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Contents: '%s' (expected: '%s')\n",
clutter_text_get_text (text),
display);

View File

@@ -116,24 +116,24 @@ validate_result (TestState *state)
{
int ypos = 0;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Testing onscreen clone...\n");
validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0);
ypos++;
#if 0 /* this doesn't work */
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Testing offscreen clone...\n");
validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0);
#endif
ypos++;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Testing onscreen clone with rectangular clip...\n");
validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, ~1);
ypos++;
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("Testing onscreen clone with path clip...\n");
validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 1);
ypos++;

View File

@@ -1,8 +1,9 @@
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
#include <stdlib.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "test-conform-common.h"
#include "tests/clutter-test-utils.h"
/* We ask for 1 frame per millisecond.
* Whenever this rate can't be achieved then the timeline
@@ -12,8 +13,13 @@
#define TEST_TIMELINE_DURATION 5000
/* We are at the mercy of the system scheduler so this
* may not be a very reliable tolerance. */
#define TEST_ERROR_TOLERANCE 20
* may not be a very reliable tolerance.
*
* It's set as very tolerable (1 ms shorter than the frame interval) as
* otherwise CI, which are very prone to not get CPU time scheduled, tend to
* often fail.
*/
#define TEST_ERROR_TOLERANCE ((TEST_TIMELINE_FPS / 4) - 1)
typedef struct _TestState
{
@@ -118,26 +124,23 @@ completed_cb (ClutterTimeline *timeline,
if (state->completion_count == 2)
{
if (state->passed)
{
g_test_message ("Passed\n");
clutter_main_quit ();
}
clutter_main_quit ();
else
{
g_test_message ("Failed\n");
exit (EXIT_FAILURE);
}
g_assert_not_reached ();
}
}
void
static void
timeline_interpolation (void)
{
ClutterActor *stage;
TestState state;
stage = clutter_test_get_stage ();
state.timeline =
clutter_timeline_new (TEST_TIMELINE_DURATION);
clutter_timeline_set_loop (state.timeline, TRUE);
clutter_timeline_set_repeat_count (state.timeline, -1);
g_signal_connect (G_OBJECT(state.timeline),
"new-frame",
G_CALLBACK(new_frame_cb),
@@ -152,6 +155,8 @@ timeline_interpolation (void)
state.passed = TRUE;
state.expected_frame = 0;
clutter_actor_show (stage);
state.start_time = g_get_real_time ();
clutter_timeline_start (state.timeline);
@@ -159,3 +164,7 @@ timeline_interpolation (void)
g_object_unref (state.timeline);
}
CLUTTER_TEST_SUITE (
CLUTTER_TEST_UNIT ("/timeline/interpolate", timeline_interpolation)
)

View File

@@ -1,16 +1,17 @@
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
#include <glib.h>
#include <clutter/clutter.h>
#include "test-conform-common.h"
void
timeline_progress_step (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
gconstpointer dummy G_GNUC_UNUSED)
#include "tests/clutter-test-utils.h"
static void
timeline_progress_step (void)
{
ClutterTimeline *timeline;
timeline = clutter_timeline_new (1000);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("mode: step(3, end)\n");
clutter_timeline_rewind (timeline);
@@ -45,7 +46,7 @@ timeline_progress_step (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
clutter_timeline_advance (timeline, 1000);
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("mode: step-start\n");
clutter_timeline_rewind (timeline);
@@ -64,7 +65,7 @@ timeline_progress_step (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
clutter_timeline_advance (timeline, 1000);
g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0);
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("mode: step-end\n");
clutter_timeline_rewind (timeline);
@@ -86,9 +87,8 @@ timeline_progress_step (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
g_object_unref (timeline);
}
void
timeline_progress_mode (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
gconstpointer dummy G_GNUC_UNUSED)
static void
timeline_progress_mode (void)
{
ClutterTimeline *timeline;
@@ -108,3 +108,8 @@ timeline_progress_mode (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
g_object_unref (timeline);
}
CLUTTER_TEST_SUITE (
CLUTTER_TEST_UNIT ("/timeline/progress/step", timeline_progress_step);
CLUTTER_TEST_UNIT ("/timeline/progress/mode", timeline_progress_mode)
)

View File

@@ -1,8 +1,9 @@
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
#include <stdlib.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "test-conform-common.h"
#include "tests/clutter-test-utils.h"
#define TEST_TIMELINE_DURATION 500
#define TEST_WATCHDOG_KICK_IN_SECONDS 10
@@ -66,11 +67,14 @@ new_frame_cb (ClutterTimeline *timeline,
}
}
void
static void
timeline_rewind (void)
{
ClutterActor *stage;
TestState state;
stage = clutter_test_get_stage ();
state.timeline =
clutter_timeline_new (TEST_TIMELINE_DURATION);
g_signal_connect (G_OBJECT(state.timeline),
@@ -84,9 +88,15 @@ timeline_rewind (void)
&state);
state.rewind_count = 0;
clutter_actor_show (stage);
clutter_timeline_start (state.timeline);
clutter_main();
g_object_unref (state.timeline);
}
CLUTTER_TEST_SUITE (
CLUTTER_TEST_UNIT ("/timeline/rewind", timeline_rewind)
)

View File

@@ -1,11 +1,12 @@
#define CLUTTER_DISABLE_DEPRECATION_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <clutter/clutter.h>
#include "test-conform-common.h"
#include "tests/clutter-test-utils.h"
/* This test runs three timelines at 30 fps with 10 frames. Some of
/* This test runs three timelines at 6 fps with 10 frames. Some of
the timelines have markers. Once the timelines are run it then
checks that all of the frames were hit, all of the markers were hit
and that the completed signal was fired. The timelines are then run
@@ -15,7 +16,7 @@
for. */
#define FRAME_COUNT 10
#define FPS 30
#define FPS 6
typedef struct _TimelineData TimelineData;
@@ -45,7 +46,7 @@ static void
timeline_complete_cb (ClutterTimeline *timeline,
TimelineData *data)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("%i: Completed\n", data->timeline_num);
data->completed_count++;
@@ -61,7 +62,7 @@ timeline_new_frame_cb (ClutterTimeline *timeline,
int frame_no = ((msec * FRAME_COUNT + (FRAME_COUNT * 1000 / FPS) / 2)
/ (FRAME_COUNT * 1000 / FPS));
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("%i: Doing frame %d, delta = %i\n",
data->timeline_num, frame_no,
clutter_timeline_get_delta (timeline));
@@ -77,7 +78,7 @@ timeline_marker_reached_cb (ClutterTimeline *timeline,
guint frame_num,
TimelineData *data)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("%i: Marker '%s' (%d) reached, delta = %i\n",
data->timeline_num, marker_name, frame_num,
clutter_timeline_get_delta (timeline));
@@ -117,7 +118,7 @@ check_timeline (ClutterTimeline *timeline,
marker_reached_count[i]++;
else
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("FAIL: unknown marker '%s' hit for timeline %i\n",
(char *) node->data, data->timeline_num);
succeeded = FALSE;
@@ -127,7 +128,7 @@ check_timeline (ClutterTimeline *timeline,
for (i = 0; i < n_markers; i++)
if (marker_reached_count[i] != 1)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("FAIL: marker '%s' hit %i times for timeline %i\n",
markers[i], marker_reached_count[i], data->timeline_num);
succeeded = FALSE;
@@ -141,7 +142,7 @@ check_timeline (ClutterTimeline *timeline,
if (missed_frame_count)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("FAIL: missed %i frame%s for timeline %i\n",
missed_frame_count, missed_frame_count == 1 ? "" : "s",
data->timeline_num);
@@ -151,7 +152,7 @@ check_timeline (ClutterTimeline *timeline,
if (data->completed_count != 1)
{
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("FAIL: timeline %i completed %i times\n",
data->timeline_num, data->completed_count);
succeeded = FALSE;
@@ -180,10 +181,10 @@ delay_cb (gpointer data)
return TRUE;
}
void
timeline_base (TestConformSimpleFixture *fixture,
gconstpointer data)
static void
timeline_base (void)
{
ClutterActor *stage;
ClutterTimeline *timeline_1;
TimelineData data_1;
ClutterTimeline *timeline_2;
@@ -194,9 +195,7 @@ timeline_base (TestConformSimpleFixture *fixture,
gsize n_markers;
guint delay_tag;
/* NB: We have to ensure a stage is instantiated else the master
* clock wont run... */
ClutterActor *stage = clutter_stage_new ();
stage = clutter_test_get_stage ();
timeline_data_init (&data_1, 1);
timeline_1 = clutter_timeline_new (FRAME_COUNT * 1000 / FPS);
@@ -216,7 +215,7 @@ timeline_base (TestConformSimpleFixture *fixture,
g_strfreev (markers);
timeline_data_init (&data_2, 2);
timeline_2 = clutter_timeline_clone (timeline_1);
timeline_2 = clutter_timeline_new (FRAME_COUNT * 1000 / FPS);
clutter_timeline_add_marker_at_time (timeline_2, "bar", 2 * 1000 / FPS);
markers = clutter_timeline_list_markers (timeline_2, -1, &n_markers);
g_assert (markers != NULL);
@@ -225,7 +224,7 @@ timeline_base (TestConformSimpleFixture *fixture,
g_strfreev (markers);
timeline_data_init (&data_3, 3);
timeline_3 = clutter_timeline_clone (timeline_1);
timeline_3 = clutter_timeline_new (FRAME_COUNT * 1000 / FPS);
clutter_timeline_set_direction (timeline_3, CLUTTER_TIMELINE_BACKWARD);
clutter_timeline_add_marker_at_time (timeline_3, "start-marker",
FRAME_COUNT * 1000 / FPS);
@@ -267,7 +266,9 @@ timeline_base (TestConformSimpleFixture *fixture,
"completed", G_CALLBACK (timeline_complete_cb),
&data_3);
if (g_test_verbose ())
clutter_actor_show (stage);
if (!g_test_quiet ())
g_print ("Without delay...\n");
clutter_timeline_start (timeline_1);
@@ -282,7 +283,7 @@ timeline_base (TestConformSimpleFixture *fixture,
g_assert (check_timeline (timeline_2, &data_2, TRUE));
g_assert (check_timeline (timeline_3, &data_3, TRUE));
if (g_test_verbose ())
if (!g_test_quiet ())
g_print ("With delay...\n");
timeline_data_destroy (&data_1);
@@ -314,13 +315,10 @@ timeline_base (TestConformSimpleFixture *fixture,
timeline_data_destroy (&data_3);
g_clear_handle_id (&delay_tag, g_source_remove);
clutter_actor_destroy (stage);
}
void
timeline_markers_from_script (TestConformSimpleFixture *fixture,
gconstpointer data)
static void
timeline_markers_from_script (void)
{
ClutterScript *script = clutter_script_new ();
ClutterTimeline *timeline;
@@ -329,9 +327,12 @@ timeline_markers_from_script (TestConformSimpleFixture *fixture,
gchar **markers;
gsize n_markers;
test_file = clutter_test_get_data_file ("test-script-timeline-markers.json");
test_file = g_test_build_filename (G_TEST_DIST,
"scripts",
"test-script-timeline-markers.json",
NULL);
clutter_script_load_from_file (script, test_file, &error);
if (g_test_verbose () && error != NULL)
if (!g_test_quiet () && error != NULL)
g_print ("Error: %s", error->message);
g_assert_no_error (error);
@@ -351,11 +352,16 @@ timeline_markers_from_script (TestConformSimpleFixture *fixture,
markers = clutter_timeline_list_markers (timeline, 500, &n_markers);
g_assert_cmpint (n_markers, ==, 2);
g_assert (markers != NULL);
g_assert_cmpstr (markers[0], ==, "marker3");
g_assert_cmpstr (markers[1], ==, "marker1");
g_assert (g_strv_contains ((const char * const *) markers, "marker1"));
g_assert (g_strv_contains ((const char * const *) markers, "marker3"));
g_strfreev (markers);
g_object_unref (script);
g_free (test_file);
}
CLUTTER_TEST_SUITE (
CLUTTER_TEST_UNIT ("/timeline/base", timeline_base);
CLUTTER_TEST_UNIT ("/timeline/markers-from-script", timeline_markers_from_script)
)

View File

@@ -410,7 +410,7 @@ test_cogl_tex_polygon_main (int argc, char *argv[])
/* Timeline for animation */
timeline = clutter_timeline_new (6000);
clutter_timeline_set_loop (timeline, TRUE);
clutter_timeline_set_repeat_count (timeline, -1);
g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), coglbox);
clutter_timeline_start (timeline);

View File

@@ -216,7 +216,7 @@ test_cogl_tex_tile_main (int argc, char *argv[])
/* Timeline for animation */
timeline = clutter_timeline_new (6000); /* 6 second duration */
clutter_timeline_set_loop (timeline, TRUE);
clutter_timeline_set_repeat_count (timeline, -1);
g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), coglbox);
clutter_timeline_start (timeline);

View File

@@ -123,6 +123,27 @@ headless_start_test = executable('mutter-headless-start-test',
install_dir: mutter_installed_tests_libexecdir,
)
stage_view_tests = executable('mutter-stage-view-tests',
sources: [
'meta-backend-test.c',
'meta-backend-test.h',
'meta-gpu-test.c',
'meta-gpu-test.h',
'meta-monitor-manager-test.c',
'meta-monitor-manager-test.h',
'monitor-test-utils.c',
'monitor-test-utils.h',
'stage-view-tests.c',
'test-utils.c',
'test-utils.h',
],
include_directories: tests_includepath,
c_args: tests_c_args,
dependencies: [tests_deps],
install: have_installed_tests,
install_dir: mutter_installed_tests_libexecdir,
)
stacking_tests = [
'basic-x11',
'basic-wayland',
@@ -173,3 +194,10 @@ test('headless-start', headless_start_test,
is_parallel: false,
timeout: 60,
)
test('stage-view', stage_view_tests,
suite: ['core', 'mutter/unit'],
env: test_env,
is_parallel: false,
timeout: 60,
)

View File

@@ -0,0 +1,504 @@
/*
* Copyright (C) 2020 Jonas Dreßler
*
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "compositor/meta-plugin-manager.h"
#include "core/main-private.h"
#include "meta/main.h"
#include "tests/meta-backend-test.h"
#include "tests/monitor-test-utils.h"
#include "tests/test-utils.h"
#define FRAME_WARNING "Frame has assigned frame counter but no frame drawn time"
static gboolean
run_tests (gpointer data)
{
MetaBackend *backend = meta_get_backend ();
MetaSettings *settings = meta_backend_get_settings (backend);
gboolean ret;
g_test_log_set_fatal_handler (NULL, NULL);
meta_settings_override_experimental_features (settings);
meta_settings_enable_experimental_feature (
settings,
META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER);
ret = g_test_run ();
meta_quit (ret != 0);
return G_SOURCE_REMOVE;
}
static gboolean
ignore_frame_counter_warning (const gchar *log_domain,
GLogLevelFlags log_level,
const gchar *message,
gpointer user_data)
{
if ((log_level & G_LOG_LEVEL_WARNING) &&
g_strcmp0 (log_domain, "mutter") == 0 &&
g_str_has_suffix (message, FRAME_WARNING))
return FALSE;
return TRUE;
}
static MonitorTestCaseSetup initial_test_case_setup = {
.modes = {
{
.width = 1024,
.height = 768,
.refresh_rate = 60.0
}
},
.n_modes = 1,
.outputs = {
{
.crtc = 0,
.modes = { 0 },
.n_modes = 1,
.preferred_mode = 0,
.possible_crtcs = { 0 },
.n_possible_crtcs = 1,
.width_mm = 222,
.height_mm = 125
},
{
.crtc = 1,
.modes = { 0 },
.n_modes = 1,
.preferred_mode = 0,
.possible_crtcs = { 1 },
.n_possible_crtcs = 1,
.width_mm = 220,
.height_mm = 124
}
},
.n_outputs = 2,
.crtcs = {
{
.current_mode = 0
},
{
.current_mode = 0
}
},
.n_crtcs = 2
};
static void
meta_test_stage_views_exist (void)
{
MetaBackend *backend = meta_get_backend ();
ClutterActor *stage;
GList *stage_views;
stage = meta_backend_get_stage (backend);
g_assert_cmpint (clutter_actor_get_width (stage), ==, 1024 * 2);
g_assert_cmpint (clutter_actor_get_height (stage), ==, 768);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
g_assert_cmpint (g_list_length (stage_views), ==, 2);
}
static void
on_after_paint (ClutterStage *stage,
gboolean *was_painted)
{
*was_painted = TRUE;
}
static void
wait_for_paint (ClutterActor *stage)
{
gboolean was_painted = FALSE;
gulong was_painted_id;
was_painted_id = g_signal_connect (CLUTTER_STAGE (stage),
"after-paint",
G_CALLBACK (on_after_paint),
&was_painted);
while (!was_painted)
g_main_context_iteration (NULL, FALSE);
g_signal_handler_disconnect (stage, was_painted_id);
}
static void
on_stage_views_changed (ClutterActor *actor,
gboolean *stage_views_changed)
{
*stage_views_changed = TRUE;
}
static void
is_on_stage_views (ClutterActor *actor,
unsigned int n_views,
...)
{
va_list valist;
int i = 0;
GList *stage_views = clutter_actor_peek_stage_views (actor);
va_start (valist, n_views);
for (i = 0; i < n_views; i++)
{
ClutterStageView *view = va_arg (valist, ClutterStageView*);
g_assert_nonnull (g_list_find (stage_views, view));
}
va_end (valist);
g_assert (g_list_length (stage_views) == n_views);
}
static void
meta_test_actor_stage_views (void)
{
MetaBackend *backend = meta_get_backend ();
ClutterActor *stage, *container, *test_actor;
GList *stage_views;
gboolean stage_views_changed_container = FALSE;
gboolean stage_views_changed_test_actor = FALSE;
gboolean *stage_views_changed_container_ptr =
&stage_views_changed_container;
gboolean *stage_views_changed_test_actor_ptr =
&stage_views_changed_test_actor;
stage = meta_backend_get_stage (backend);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
container = clutter_actor_new ();
clutter_actor_set_size (container, 100, 100);
clutter_actor_add_child (stage, container);
test_actor = clutter_actor_new ();
clutter_actor_set_size (test_actor, 50, 50);
clutter_actor_add_child (container, test_actor);
g_signal_connect (container, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_container_ptr);
g_signal_connect (test_actor, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_test_actor_ptr);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (container, 1, stage_views->data);
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted for the initial change */
g_assert (stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_container = FALSE;
stage_views_changed_test_actor = FALSE;
/* Move the container to the second stage view */
clutter_actor_set_x (container, 1040);
wait_for_paint (stage);
is_on_stage_views (container, 1, stage_views->next->data);
is_on_stage_views (test_actor, 1, stage_views->next->data);
/* The signal was emitted again */
g_assert (stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_container = FALSE;
stage_views_changed_test_actor = FALSE;
/* Move the container so it's on both stage views while the test_actor
* is only on the first one.
*/
clutter_actor_set_x (container, 940);
wait_for_paint (stage);
is_on_stage_views (container, 2, stage_views->data, stage_views->next->data);
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted again */
g_assert (stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
g_signal_handlers_disconnect_by_func (container, on_stage_views_changed,
stage_views_changed_container_ptr);
g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed,
stage_views_changed_test_actor_ptr);
clutter_actor_destroy (container);
}
static void
meta_test_actor_stage_views_reparent (void)
{
MetaBackend *backend = meta_get_backend ();
ClutterActor *stage, *container, *test_actor;
GList *stage_views;
gboolean stage_views_changed_container = FALSE;
gboolean stage_views_changed_test_actor = FALSE;
gboolean *stage_views_changed_container_ptr =
&stage_views_changed_container;
gboolean *stage_views_changed_test_actor_ptr =
&stage_views_changed_test_actor;
stage = meta_backend_get_stage (backend);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
container = clutter_actor_new ();
clutter_actor_set_size (container, 100, 100);
clutter_actor_set_x (container, 1020);
clutter_actor_add_child (stage, container);
test_actor = clutter_actor_new ();
clutter_actor_set_size (test_actor, 20, 20);
clutter_actor_add_child (container, test_actor);
g_signal_connect (container, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_container_ptr);
g_signal_connect (test_actor, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_test_actor_ptr);
clutter_actor_show (stage);
wait_for_paint (stage);
is_on_stage_views (container, 2, stage_views->data, stage_views->next->data);
is_on_stage_views (test_actor, 2, stage_views->data, stage_views->next->data);
/* The signal was emitted for both actors */
g_assert (stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_container = FALSE;
stage_views_changed_test_actor = FALSE;
/* Remove the test_actor from the scene-graph */
g_object_ref (test_actor);
clutter_actor_remove_child (container, test_actor);
/* While the test_actor is not on stage, it must be on no stage views */
is_on_stage_views (test_actor, 0);
/* When the test_actor left the stage, the signal was emitted */
g_assert (!stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_test_actor = FALSE;
/* Add the test_actor again as a child of the stage */
clutter_actor_add_child (stage, test_actor);
g_object_unref (test_actor);
wait_for_paint (stage);
/* The container is still on both stage views... */
is_on_stage_views (container, 2, stage_views->data, stage_views->next->data);
/* ...while the test_actor is only on the first one now */
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted for the test_actor again */
g_assert (!stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_test_actor = FALSE;
/* Move the container out of the stage... */
clutter_actor_set_y (container, 2000);
g_object_ref (test_actor);
clutter_actor_remove_child (stage, test_actor);
/* When the test_actor left the stage, the signal was emitted */
g_assert (!stage_views_changed_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_test_actor = FALSE;
/* ...and reparent the test_actor to the container again */
clutter_actor_add_child (container, test_actor);
g_object_unref (test_actor);
wait_for_paint (stage);
/* Now both actors are on no stage views */
is_on_stage_views (container, 0);
is_on_stage_views (test_actor, 0);
/* The signal was emitted only for the container, the test_actor already
* has no stage-views.
*/
g_assert (stage_views_changed_container);
g_assert (!stage_views_changed_test_actor);
g_signal_handlers_disconnect_by_func (container, on_stage_views_changed,
stage_views_changed_container_ptr);
g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed,
stage_views_changed_test_actor_ptr);
clutter_actor_destroy (container);
}
static void
meta_test_actor_stage_views_hide_parent (void)
{
MetaBackend *backend = meta_get_backend ();
ClutterActor *stage, *outer_container, *inner_container, *test_actor;
GList *stage_views;
gboolean stage_views_changed_outer_container = FALSE;
gboolean stage_views_changed_inner_container = FALSE;
gboolean stage_views_changed_test_actor = FALSE;
gboolean *stage_views_changed_outer_container_ptr =
&stage_views_changed_outer_container;
gboolean *stage_views_changed_inner_container_ptr =
&stage_views_changed_inner_container;
gboolean *stage_views_changed_test_actor_ptr =
&stage_views_changed_test_actor;
stage = meta_backend_get_stage (backend);
stage_views = clutter_stage_peek_stage_views (CLUTTER_STAGE (stage));
outer_container = clutter_actor_new ();
clutter_actor_add_child (stage, outer_container);
inner_container = clutter_actor_new ();
clutter_actor_add_child (outer_container, inner_container);
test_actor = clutter_actor_new ();
clutter_actor_set_size (test_actor, 20, 20);
clutter_actor_add_child (inner_container, test_actor);
g_signal_connect (outer_container, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_outer_container_ptr);
g_signal_connect (inner_container, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_inner_container_ptr);
g_signal_connect (test_actor, "stage-views-changed",
G_CALLBACK (on_stage_views_changed),
stage_views_changed_test_actor_ptr);
clutter_actor_show (stage);
wait_for_paint (stage);
/* The containers and the test_actor are on all on the first view */
is_on_stage_views (outer_container, 1, stage_views->data);
is_on_stage_views (inner_container, 1, stage_views->data);
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted for all three */
g_assert (stage_views_changed_outer_container);
g_assert (stage_views_changed_inner_container);
g_assert (stage_views_changed_test_actor);
stage_views_changed_outer_container = FALSE;
stage_views_changed_inner_container = FALSE;
stage_views_changed_test_actor = FALSE;
/* Hide the inner_container */
clutter_actor_hide (inner_container);
/* Move the outer_container so it's still on the first view */
clutter_actor_set_x (outer_container, 1023);
wait_for_paint (stage);
/* The outer_container is still expanded so it should be on both views */
is_on_stage_views (outer_container, 2,
stage_views->data, stage_views->next->data);
/* The inner_container and test_actor aren't updated because they're hidden */
is_on_stage_views (inner_container, 1, stage_views->data);
is_on_stage_views (test_actor, 1, stage_views->data);
/* The signal was emitted for the outer_container */
g_assert (stage_views_changed_outer_container);
g_assert (!stage_views_changed_inner_container);
g_assert (!stage_views_changed_test_actor);
stage_views_changed_outer_container = FALSE;
/* Show the inner_container again */
clutter_actor_show (inner_container);
wait_for_paint (stage);
/* All actors are on both views now */
is_on_stage_views (outer_container, 2,
stage_views->data, stage_views->next->data);
is_on_stage_views (inner_container, 2,
stage_views->data, stage_views->next->data);
is_on_stage_views (test_actor, 2,
stage_views->data, stage_views->next->data);
/* The signal was emitted for the inner_container and test_actor */
g_assert (!stage_views_changed_outer_container);
g_assert (stage_views_changed_inner_container);
g_assert (stage_views_changed_test_actor);
g_signal_handlers_disconnect_by_func (outer_container, on_stage_views_changed,
stage_views_changed_outer_container_ptr);
g_signal_handlers_disconnect_by_func (inner_container, on_stage_views_changed,
stage_views_changed_inner_container_ptr);
g_signal_handlers_disconnect_by_func (test_actor, on_stage_views_changed,
stage_views_changed_test_actor_ptr);
clutter_actor_destroy (outer_container);
}
static void
init_tests (int argc, char **argv)
{
MetaMonitorTestSetup *test_setup;
test_setup = create_monitor_test_setup (&initial_test_case_setup,
MONITOR_TEST_FLAG_NO_STORED);
meta_monitor_manager_test_init_test_setup (test_setup);
g_test_add_func ("/stage-view/stage-views-exist",
meta_test_stage_views_exist);
g_test_add_func ("/stage-views/actor-stage-views",
meta_test_actor_stage_views);
g_test_add_func ("/stage-views/actor-stage-views-reparent",
meta_test_actor_stage_views_reparent);
g_test_add_func ("/stage-views/actor-stage-views-hide-parent",
meta_test_actor_stage_views_hide_parent);
}
int
main (int argc, char *argv[])
{
test_init (&argc, &argv);
init_tests (argc, argv);
meta_plugin_manager_load (test_get_plugin_name ());
meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND,
META_TYPE_BACKEND_TEST);
meta_init ();
meta_register_with_session ();
g_test_log_set_fatal_handler (ignore_frame_counter_warning, NULL);
g_idle_add (run_tests, NULL);
return meta_run ();
}

View File

@@ -858,6 +858,8 @@ int
main(int argc, char **argv)
{
GOptionContext *context = g_option_context_new (NULL);
GdkScreen *screen;
GtkCssProvider *provider;
GError *error = NULL;
g_option_context_add_main_entries (context, options, NULL);
@@ -876,31 +878,25 @@ main(int argc, char **argv)
gtk_init (NULL, NULL);
if (!wayland)
screen = gdk_screen_get_default ();
provider = gtk_css_provider_new ();
static const char *no_decoration_css =
"decoration {"
" border-radius: 0 0 0 0;"
" border-width: 0;"
" box-shadow: 0 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0 rgba(0, 0, 0, 0);"
" margin: 0px;"
"}";
if (!gtk_css_provider_load_from_data (provider,
no_decoration_css,
strlen (no_decoration_css),
&error))
{
GdkScreen *screen;
GtkCssProvider *provider;
screen = gdk_screen_get_default ();
provider = gtk_css_provider_new ();
static const char *no_decoration_css =
"decoration {"
" border-radius: 0 0 0 0;"
" border-width: 0;"
" box-shadow: 0 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0 rgba(0, 0, 0, 0);"
" margin: 0px;"
"}";
if (!gtk_css_provider_load_from_data (provider,
no_decoration_css,
strlen (no_decoration_css),
&error))
{
g_printerr ("%s", error->message);
return 1;
}
gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_printerr ("%s", error->message);
return 1;
}
gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
windows = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);

View File

@@ -572,6 +572,9 @@ test_case_do (TestCase *test,
if (!test_client_do (client, error, argv[0], window_id, NULL))
return FALSE;
if (!test_case_wait (test, error))
return FALSE;
MetaWindow *window = test_client_find_window (client, window_id, error);
if (!window)
return FALSE;

View File

@@ -94,7 +94,7 @@ static struct wl_resource *
create_and_send_dnd_offer (MetaWaylandDataSource *source,
struct wl_resource *target)
{
MetaWaylandDataOffer *offer = g_slice_new0 (MetaWaylandDataOffer);
MetaWaylandDataOffer *offer;
struct wl_array *mime_types;
struct wl_resource *resource;
char **p;

View File

@@ -51,6 +51,7 @@ transfer_cb (MetaSelection *selection,
}
g_output_stream_close (stream, NULL, NULL);
g_object_unref (stream);
}
static void

View File

@@ -51,6 +51,7 @@ transfer_cb (MetaSelection *selection,
}
g_output_stream_close (stream, NULL, NULL);
g_object_unref (stream);
}
static void

View File

@@ -77,6 +77,7 @@ transfer_cb (MetaSelection *selection,
}
g_output_stream_close (stream, NULL, NULL);
g_object_unref (stream);
}
static void

View File

@@ -0,0 +1,287 @@
/*
* Wayland Support
*
* Copyright (C) 2018 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#include "config.h"
#include <linux/input.h>
#include <sys/ioctl.h>
#include "backends/native/meta-backend-native.h"
#include "compositor/meta-surface-actor-wayland.h"
#include "inputfd-unstable-v1-server-protocol.h"
#include "wayland/meta-wayland-inputfd-evdev-device.h"
static void device_open_fd (MetaWaylandInputFdEvdevDevice *device);
static void
unbind_resource (struct wl_resource *resource)
{
wl_list_remove (wl_resource_get_link (resource));
}
static void
move_resources (struct wl_list *destination,
struct wl_list *source)
{
wl_list_insert_list (destination, source);
wl_list_init (source);
}
static void
move_resources_for_client (struct wl_list *destination,
struct wl_list *source,
struct wl_client *client)
{
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe (resource, tmp, source)
{
if (wl_resource_get_client (resource) == client)
{
wl_list_remove (wl_resource_get_link (resource));
wl_list_insert (destination, wl_resource_get_link (resource));
}
}
}
static void
evdev_device_handle_focus_surface_destroy (struct wl_listener *listener,
void *data)
{
MetaWaylandInputFdEvdevDevice *device = wl_container_of (listener, device,
focus_surface_listener);
meta_wayland_inputfd_evdev_device_set_focus (device, NULL);
}
static gboolean
check_device_qualifies (GUdevDevice *device)
{
const gchar *path = g_udev_device_get_device_file (device);
if (!path || !strstr (path, "/event"))
return FALSE;
if (!g_udev_device_get_property_as_boolean (device, "ID_INPUT_JOYSTICK"))
return FALSE;
return TRUE;
}
MetaWaylandInputFdEvdevDevice *
meta_wayland_inputfd_evdev_device_new (MetaWaylandInputFdSeat *seat,
GUdevDevice *device)
{
MetaWaylandInputFdEvdevDevice *evdev_device;
GUdevDevice *parent;
if (!check_device_qualifies (device))
return NULL;
parent = g_udev_device_get_parent (device);
evdev_device = g_slice_new0 (MetaWaylandInputFdEvdevDevice);
evdev_device->udev_device = g_object_ref (device);
wl_list_init (&evdev_device->resource_list);
wl_list_init (&evdev_device->focus_resource_list);
evdev_device->focus_surface_listener.notify =
evdev_device_handle_focus_surface_destroy;
evdev_device->fd = -1;
evdev_device->name =
g_udev_device_get_sysfs_attr (parent, "name");
evdev_device->vid =
strtol (g_udev_device_get_property (device, "ID_VENDOR_ID"),
NULL, 16);
evdev_device->pid =
strtol (g_udev_device_get_property (device, "ID_MODEL_ID"),
NULL, 16);
g_object_unref (parent);
return evdev_device;
}
void
meta_wayland_inputfd_evdev_device_free (MetaWaylandInputFdEvdevDevice *evdev_device)
{
struct wl_resource *resource, *next;
meta_wayland_inputfd_evdev_device_set_focus (evdev_device, NULL);
wl_resource_for_each_safe (resource, next, &evdev_device->resource_list)
{
zwp_inputfd_device_evdev_v1_send_removed (resource);
wl_list_remove (wl_resource_get_link (resource));
wl_list_init (wl_resource_get_link (resource));
}
g_object_unref (evdev_device->udev_device);
g_slice_free (MetaWaylandInputFdEvdevDevice, evdev_device);
}
static void
inputfd_device_evdev_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static const struct zwp_inputfd_device_evdev_v1_interface inputfd_device_evdev_interface = {
inputfd_device_evdev_destroy,
};
struct wl_resource *
meta_wayland_inputfd_evdev_device_create_new_resource (MetaWaylandInputFdEvdevDevice *device,
struct wl_client *client,
struct wl_resource *seat_resource,
uint32_t id)
{
struct wl_resource *resource;
resource = wl_resource_create (client, &zwp_inputfd_device_evdev_v1_interface,
wl_resource_get_version (seat_resource),
id);
wl_resource_set_implementation (resource, &inputfd_device_evdev_interface,
device, unbind_resource);
wl_resource_set_user_data (resource, device);
wl_list_insert (&device->resource_list, wl_resource_get_link (resource));
return resource;
}
void
meta_wayland_inputfd_evdev_device_notify (MetaWaylandInputFdEvdevDevice *device,
struct wl_resource *resource)
{
zwp_inputfd_device_evdev_v1_send_name (resource, device->name);
zwp_inputfd_device_evdev_v1_send_usb_id (resource, device->vid, device->pid);
zwp_inputfd_device_evdev_v1_send_done (resource);
}
static void
device_open_fd (MetaWaylandInputFdEvdevDevice *device)
{
MetaBackend *backend;
MetaLauncher *launcher;
const char *path;
g_assert (device->fd == -1);
path = g_udev_device_get_device_file (device->udev_device);
backend = meta_get_backend ();
if (!META_IS_BACKEND_NATIVE (backend))
return;
launcher = meta_backend_native_get_launcher (META_BACKEND_NATIVE (backend));
device->fd = meta_launcher_open_restricted (launcher, path, NULL);
}
static void
device_close_fd (MetaWaylandInputFdEvdevDevice *device)
{
MetaBackend *backend;
MetaLauncher *launcher;
ioctl(device->fd, EVIOCREVOKE, NULL);
backend = meta_get_backend ();
if (!META_IS_BACKEND_NATIVE (backend))
return;
launcher = meta_backend_native_get_launcher (META_BACKEND_NATIVE (backend));
meta_launcher_close_restricted (launcher, device->fd);
device->fd = -1;
}
static void
meta_wayland_inputfd_evdev_device_broadcast_focus_in (MetaWaylandInputFdEvdevDevice *device,
MetaWaylandSurface *surface,
uint32_t serial)
{
struct wl_resource *resource;
wl_resource_for_each (resource, &device->focus_resource_list)
{
zwp_inputfd_device_evdev_v1_send_focus_in (resource,
serial,
device->fd,
surface->resource);
}
}
static void
meta_wayland_inputfd_evdev_device_broadcast_focus_out (MetaWaylandInputFdEvdevDevice *device)
{
struct wl_resource *resource;
wl_resource_for_each (resource, &device->focus_resource_list)
{
zwp_inputfd_device_evdev_v1_send_focus_out (resource);
}
}
void
meta_wayland_inputfd_evdev_device_set_focus (MetaWaylandInputFdEvdevDevice *device,
MetaWaylandSurface *surface)
{
if (device->focus_surface == surface)
return;
if (device->focus_surface != NULL)
{
struct wl_list *focus_resources = &device->focus_resource_list;
if (!wl_list_empty (focus_resources))
{
meta_wayland_inputfd_evdev_device_broadcast_focus_out (device);
move_resources (&device->resource_list, &device->focus_resource_list);
}
wl_list_remove (&device->focus_surface_listener.link);
device->focus_surface = NULL;
device_close_fd (device);
}
if (surface != NULL)
device_open_fd (device);
if (surface != NULL && device->fd >= 0)
{
struct wl_client *client;
device->focus_surface = surface;
wl_resource_add_destroy_listener (device->focus_surface->resource,
&device->focus_surface_listener);
client = wl_resource_get_client (device->focus_surface->resource);
move_resources_for_client (&device->focus_resource_list,
&device->resource_list, client);
if (!wl_list_empty (&device->focus_resource_list))
{
struct wl_display *display = wl_client_get_display (client);
uint32_t serial = wl_display_next_serial (display);
meta_wayland_inputfd_evdev_device_broadcast_focus_in (device,
surface,
serial);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Wayland Support
*
* Copyright (C) 2018 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#ifndef META_WAYLAND_INPUTFD_EVDEV_DEVICE_H
#define META_WAYLAND_INPUTFD_EVDEV_DEVICE_H
#include <glib.h>
#include <gudev/gudev.h>
#include <wayland-server.h>
#include "meta-wayland-types.h"
struct _MetaWaylandInputFdEvdevDevice
{
GUdevDevice *udev_device;
struct wl_list resource_list;
struct wl_list focus_resource_list;
MetaWaylandSurface *focus_surface;
struct wl_listener focus_surface_listener;
const gchar *name;
uint32_t vid;
uint32_t pid;
int fd;
};
MetaWaylandInputFdEvdevDevice *
meta_wayland_inputfd_evdev_device_new (MetaWaylandInputFdSeat *seat,
GUdevDevice *device);
void meta_wayland_inputfd_evdev_device_free (MetaWaylandInputFdEvdevDevice *evdev_device);
struct wl_resource *
meta_wayland_inputfd_evdev_device_create_new_resource (MetaWaylandInputFdEvdevDevice *device,
struct wl_client *client,
struct wl_resource *seat_resource,
uint32_t id);
void meta_wayland_inputfd_evdev_device_set_focus (MetaWaylandInputFdEvdevDevice *device,
MetaWaylandSurface *surface);
void meta_wayland_inputfd_evdev_device_notify (MetaWaylandInputFdEvdevDevice *device,
struct wl_resource *resource);
#endif /* META_WAYLAND_INPUTFD_EVDEV_DEVICE_H */

View File

@@ -0,0 +1,128 @@
/*
* Wayland Support
*
* Copyright (C) 2018 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#include "config.h"
#include "inputfd-unstable-v1-server-protocol.h"
#include "wayland/meta-wayland-inputfd-manager.h"
#include "wayland/meta-wayland-inputfd-seat.h"
#include "wayland/meta-wayland-private.h"
static void
unbind_resource (struct wl_resource *resource)
{
wl_list_remove (wl_resource_get_link (resource));
}
static void
inputfd_manager_get_seat_evdev (struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *seat_resource)
{
MetaWaylandInputFdManager *manager = wl_resource_get_user_data (resource);
MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
MetaWaylandInputFdSeat *inputfd_seat;
inputfd_seat = meta_wayland_inputfd_manager_ensure_seat (manager, seat);
meta_wayland_inputfd_seat_create_new_evdev_resource (inputfd_seat, client,
resource, id);
}
static void
inputfd_manager_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static const struct zwp_inputfd_manager_v1_interface inputfd_manager_interface = {
inputfd_manager_get_seat_evdev,
inputfd_manager_destroy
};
static void
bind_inputfd_manager (struct wl_client *client,
void *data,
uint32_t version,
uint32_t id)
{
MetaWaylandCompositor *compositor = data;
MetaWaylandInputFdManager *manager = compositor->inputfd_manager;
struct wl_resource *resource;
resource = wl_resource_create (client, &zwp_inputfd_manager_v1_interface,
MIN (version, 1), id);
wl_resource_set_implementation (resource, &inputfd_manager_interface,
manager, unbind_resource);
wl_resource_set_user_data (resource, manager);
wl_list_insert (&manager->resource_list,
wl_resource_get_link (resource));
}
static MetaWaylandInputFdManager *
meta_wayland_inputfd_manager_new (MetaWaylandCompositor *compositor)
{
MetaWaylandInputFdManager *manager;
manager = g_slice_new0 (MetaWaylandInputFdManager);
manager->compositor = compositor;
manager->wl_display = compositor->wayland_display;
manager->seats = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) meta_wayland_inputfd_seat_free);
wl_list_init (&manager->resource_list);
wl_global_create (manager->wl_display,
&zwp_inputfd_manager_v1_interface,
META_ZWP_INPUTFD_V1_VERSION,
compositor, bind_inputfd_manager);
return manager;
}
void
meta_wayland_inputfd_manager_init (MetaWaylandCompositor *compositor)
{
compositor->inputfd_manager = meta_wayland_inputfd_manager_new (compositor);
}
void
meta_wayland_inputfd_manager_free (MetaWaylandInputFdManager *manager)
{
g_hash_table_destroy (manager->seats);
g_slice_free (MetaWaylandInputFdManager, manager);
}
MetaWaylandInputFdSeat *
meta_wayland_inputfd_manager_ensure_seat (MetaWaylandInputFdManager *manager,
MetaWaylandSeat *seat)
{
MetaWaylandInputFdSeat *inputfd_seat;
inputfd_seat = g_hash_table_lookup (manager->seats, seat);
if (!inputfd_seat)
{
inputfd_seat = meta_wayland_inputfd_seat_new (manager, seat);
g_hash_table_insert (manager->seats, seat, inputfd_seat);
}
return inputfd_seat;
}

View File

@@ -0,0 +1,47 @@
/*
* Wayland Support
*
* Copyright (C) 2018 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#ifndef META_WAYLAND_INPUTFD_MANAGER_H
#define META_WAYLAND_INPUTFD_MANAGER_H
#include <wayland-server.h>
#include <glib.h>
#include "meta-wayland-types.h"
struct _MetaWaylandInputFdManager
{
MetaWaylandCompositor *compositor;
struct wl_display *wl_display;
struct wl_list resource_list;
GHashTable *seats;
};
void meta_wayland_inputfd_manager_init (MetaWaylandCompositor *compositor);
void meta_wayland_inputfd_manager_free (MetaWaylandInputFdManager *manager);
MetaWaylandInputFdSeat *
meta_wayland_inputfd_manager_ensure_seat (MetaWaylandInputFdManager *manager,
MetaWaylandSeat *seat);
#endif /* META_WAYLAND_INPUTFD_MANAGER_H */

View File

@@ -0,0 +1,201 @@
/*
* Wayland Support
*
* Copyright (C) 2018 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#include "config.h"
#include "inputfd-unstable-v1-server-protocol.h"
#include "wayland/meta-wayland-inputfd-seat.h"
#include "wayland/meta-wayland-inputfd-evdev-device.h"
static void
unbind_resource (struct wl_resource *resource)
{
wl_list_remove (wl_resource_get_link (resource));
}
static void
notify_evdev_device_added (MetaWaylandInputFdSeat *seat,
MetaWaylandInputFdEvdevDevice *evdev_device,
struct wl_resource *seat_resource)
{
struct wl_resource *resource;
struct wl_client *client;
client = wl_resource_get_client (seat_resource);
resource = meta_wayland_inputfd_evdev_device_create_new_resource (evdev_device,
client,
seat_resource,
0);
zwp_inputfd_seat_evdev_v1_send_device_added (seat_resource, resource);
}
static void
broadcast_evdev_device_added (MetaWaylandInputFdSeat *seat,
MetaWaylandInputFdEvdevDevice *evdev_device)
{
struct wl_resource *seat_resource;
wl_resource_for_each (seat_resource, &seat->evdev_seat_resources)
{
notify_evdev_device_added (seat, evdev_device, seat_resource);
}
}
static void
check_add_device (MetaWaylandInputFdSeat *inputfd_seat,
GUdevDevice *device)
{
MetaWaylandInputFdEvdevDevice *evdev_device;
evdev_device = meta_wayland_inputfd_evdev_device_new (inputfd_seat,
device);
if (evdev_device)
{
g_hash_table_insert (inputfd_seat->evdev_devices,
(gpointer) g_udev_device_get_sysfs_path (device),
evdev_device);
broadcast_evdev_device_added (inputfd_seat, evdev_device);
return;
}
}
static void
remove_device (MetaWaylandInputFdSeat *inputfd_seat,
GUdevDevice *device)
{
g_hash_table_remove (inputfd_seat->evdev_devices,
g_udev_device_get_sysfs_path (device));
}
static void
udev_event_cb (GUdevClient *client,
char *action,
GUdevDevice *device,
MetaWaylandInputFdSeat *seat)
{
if (g_strcmp0 (action, "add") == 0)
check_add_device (seat, device);
else if (g_strcmp0 (action, "remove") == 0)
remove_device (seat, device);
}
MetaWaylandInputFdSeat *
meta_wayland_inputfd_seat_new (MetaWaylandInputFdManager *manager,
MetaWaylandSeat *seat)
{
const char * const subsystems[] = { "input", NULL };
MetaWaylandInputFdSeat *inputfd_seat;
GList *devices;
inputfd_seat = g_slice_new0 (MetaWaylandInputFdSeat);
wl_list_init (&inputfd_seat->evdev_seat_resources);
inputfd_seat->evdev_devices =
g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
(GDestroyNotify) meta_wayland_inputfd_evdev_device_free);
inputfd_seat->udev_client = g_udev_client_new (subsystems);
g_signal_connect (inputfd_seat->udev_client, "uevent",
G_CALLBACK (udev_event_cb), inputfd_seat);
devices = g_udev_client_query_by_subsystem (inputfd_seat->udev_client,
subsystems[0]);
while (devices)
{
GUdevDevice *device = devices->data;
check_add_device (inputfd_seat, device);
g_object_unref (device);
devices = g_list_delete_link (devices, devices);
}
return inputfd_seat;
}
void
meta_wayland_inputfd_seat_free (MetaWaylandInputFdSeat *inputfd_seat)
{
g_hash_table_destroy (inputfd_seat->evdev_devices);
g_signal_handlers_disconnect_by_func (inputfd_seat->udev_client,
udev_event_cb, inputfd_seat);
g_object_unref (inputfd_seat->udev_client);
g_slice_free (MetaWaylandInputFdSeat, inputfd_seat);
}
static void
notify_evdev_devices (MetaWaylandInputFdSeat *seat,
struct wl_resource *seat_resource)
{
MetaWaylandInputFdEvdevDevice *device;
GHashTableIter iter;
g_hash_table_iter_init (&iter, seat->evdev_devices);
while (g_hash_table_iter_next (&iter, NULL, (void **) &device))
{
notify_evdev_device_added (seat, device, seat_resource);
}
}
static void
inputfd_seat_evdev_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static const struct zwp_inputfd_seat_evdev_v1_interface inputfd_seat_evdev_interface = {
inputfd_seat_evdev_destroy
};
struct wl_resource *
meta_wayland_inputfd_seat_create_new_evdev_resource (MetaWaylandInputFdSeat *seat,
struct wl_client *client,
struct wl_resource *manager_resource,
uint32_t id)
{
struct wl_resource *resource;
resource = wl_resource_create (client, &zwp_inputfd_seat_evdev_v1_interface,
wl_resource_get_version (manager_resource),
id);
wl_resource_set_implementation (resource, &inputfd_seat_evdev_interface,
seat, unbind_resource);
wl_resource_set_user_data (resource, seat);
wl_list_insert (&seat->evdev_seat_resources, wl_resource_get_link (resource));
notify_evdev_devices (seat, resource);
return resource;
}
void
meta_wayland_inputfd_seat_set_focus (MetaWaylandInputFdSeat *seat,
MetaWaylandSurface *surface)
{
MetaWaylandInputFdEvdevDevice *device;
GHashTableIter iter;
g_hash_table_iter_init (&iter, seat->evdev_devices);
while (g_hash_table_iter_next (&iter, NULL, (void **) &device))
meta_wayland_inputfd_evdev_device_set_focus (device, surface);
}

View File

@@ -0,0 +1,51 @@
/*
* Wayland Support
*
* Copyright (C) 2018 Red Hat
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#ifndef META_WAYLAND_INPUTFD_SEAT_H
#define META_WAYLAND_INPUTFD_SEAT_H
#include <glib.h>
#include <gudev/gudev.h>
#include <wayland-server.h>
#include "meta-wayland-types.h"
struct _MetaWaylandInputFdSeat
{
GUdevClient *udev_client;
struct wl_list evdev_seat_resources;
GHashTable *evdev_devices;
};
MetaWaylandInputFdSeat *
meta_wayland_inputfd_seat_new (MetaWaylandInputFdManager *manager,
MetaWaylandSeat *seat);
void meta_wayland_inputfd_seat_free (MetaWaylandInputFdSeat *inputfd_seat);
struct wl_resource *
meta_wayland_inputfd_seat_create_new_evdev_resource (MetaWaylandInputFdSeat *seat,
struct wl_client *client,
struct wl_resource *resource,
uint32_t id);
void meta_wayland_inputfd_seat_set_focus (MetaWaylandInputFdSeat *seat,
MetaWaylandSurface *surface);
#endif /* META_WAYLAND_INPUTFD_SEAT_H */

View File

@@ -26,6 +26,7 @@
#include "clutter/clutter.h"
#include "core/window-private.h"
#include "meta/meta-cursor-tracker.h"
#include "wayland/meta-wayland-inputfd-manager.h"
#include "wayland/meta-wayland-pointer-gestures.h"
#include "wayland/meta-wayland-seat.h"
#include "wayland/meta-wayland-surface.h"
@@ -85,6 +86,7 @@ struct _MetaWaylandCompositor
MetaWaylandSeat *seat;
MetaWaylandTabletManager *tablet_manager;
MetaWaylandInputFdManager *inputfd_manager;
GHashTable *scheduled_surface_associations;
};

View File

@@ -25,6 +25,7 @@
#include "wayland/meta-wayland-data-device.h"
#include "wayland/meta-wayland-data-device-primary-legacy.h"
#include "wayland/meta-wayland-inputfd-seat.h"
#include "wayland/meta-wayland-private.h"
#include "wayland/meta-wayland-tablet-seat.h"
#include "wayland/meta-wayland-versions.h"
@@ -176,7 +177,16 @@ meta_wayland_seat_set_capabilities (MetaWaylandSeat *seat,
meta_display_sync_wayland_input_focus (display);
}
else if (CAPABILITY_DISABLED (prev_flags, flags, WL_SEAT_CAPABILITY_KEYBOARD))
meta_wayland_keyboard_disable (seat->keyboard);
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWaylandInputFdSeat *inputfd_seat;
meta_wayland_keyboard_disable (seat->keyboard);
inputfd_seat = meta_wayland_inputfd_manager_ensure_seat (compositor->inputfd_manager,
seat);
meta_wayland_inputfd_seat_set_focus (inputfd_seat, NULL);
}
if (CAPABILITY_ENABLED (prev_flags, flags, WL_SEAT_CAPABILITY_TOUCH))
meta_wayland_touch_enable (seat->touch);
@@ -428,6 +438,7 @@ meta_wayland_seat_set_input_focus (MetaWaylandSeat *seat,
MetaWaylandSurface *surface)
{
MetaWaylandTabletSeat *tablet_seat;
MetaWaylandInputFdSeat *inputfd_seat;
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
if (meta_wayland_seat_has_keyboard (seat))
@@ -443,6 +454,10 @@ meta_wayland_seat_set_input_focus (MetaWaylandSeat *seat,
meta_wayland_text_input_set_focus (seat->text_input, surface);
meta_wayland_gtk_text_input_set_focus (seat->gtk_text_input, surface);
inputfd_seat = meta_wayland_inputfd_manager_ensure_seat (compositor->inputfd_manager,
seat);
meta_wayland_inputfd_seat_set_focus (inputfd_seat, surface);
}
gboolean

View File

@@ -931,7 +931,7 @@ meta_wayland_tablet_tool_update (MetaWaylandTabletTool *tool,
break;
case CLUTTER_PROXIMITY_IN:
if (!tool->cursor_renderer)
tool->cursor_renderer = meta_cursor_renderer_new ();
tool->cursor_renderer = meta_cursor_renderer_new (meta_get_backend ());
tool->current_tablet =
meta_wayland_tablet_seat_lookup_tablet (tool->seat,
clutter_event_get_source_device (event));

View File

@@ -61,4 +61,8 @@ typedef struct _MetaWaylandWindowConfiguration MetaWaylandWindowConfiguration;
typedef struct _MetaWaylandPointerClient MetaWaylandPointerClient;
typedef struct _MetaWaylandInputFdManager MetaWaylandInputFdManager;
typedef struct _MetaWaylandInputFdSeat MetaWaylandInputFdSeat;
typedef struct _MetaWaylandInputFdEvdevDevice MetaWaylandInputFdEvdevDevice;
#endif

View File

@@ -55,5 +55,6 @@
#define META_GTK_TEXT_INPUT_VERSION 1
#define META_ZWP_TEXT_INPUT_V3_VERSION 1
#define META_WP_VIEWPORTER_VERSION 1
#define META_ZWP_INPUTFD_V1_VERSION 1
#endif

View File

@@ -438,6 +438,7 @@ meta_wayland_compositor_setup (MetaWaylandCompositor *wayland_compositor)
meta_wayland_surface_inhibit_shortcuts_dialog_init ();
meta_wayland_text_input_init (compositor);
meta_wayland_gtk_text_input_init (compositor);
meta_wayland_inputfd_manager_init (compositor);
/* Xwayland specific protocol, needs to be filtered out for all other clients */
if (meta_xwayland_grab_keyboard_init (compositor))

View File

@@ -367,6 +367,7 @@ transfer_cb (MetaSelection *selection,
}
g_output_stream_close (stream, NULL, NULL);
g_object_unref (stream);
}
static void