diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c
index 2f5097f2d..006dce0f5 100644
--- a/clutter/clutter/cogl/clutter-stage-cogl.c
+++ b/clutter/clutter/cogl/clutter-stage-cogl.c
@@ -595,7 +595,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION);
has_buffer_age =
- COGL_ONSCREEN (onscreen) &&
+ cogl_is_onscreen (onscreen) &&
cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE);
redraw_clip = clutter_stage_view_take_redraw_clip (view);
diff --git a/debian/libmuffin0.symbols b/debian/libmuffin0.symbols
index a12ce98ef..3a58387d2 100644
--- a/debian/libmuffin0.symbols
+++ b/debian/libmuffin0.symbols
@@ -2054,29 +2054,8 @@ libmuffin.so.0 libmuffin0 #MINVER#
meta_backend_set_keymap@Base 5.3.0
meta_backend_set_numlock@Base 5.3.0
meta_backend_x11_nested_get_type@Base 5.3.0
- meta_background_actor_get_type@Base 5.3.0
- meta_background_actor_new@Base 5.3.0
- meta_background_actor_set_background@Base 5.3.0
- meta_background_actor_set_gradient@Base 5.3.0
- meta_background_actor_set_monitor@Base 5.3.0
- meta_background_actor_set_vignette@Base 5.3.0
- meta_background_get_type@Base 5.3.0
- meta_background_group_get_type@Base 5.3.0
- meta_background_group_new@Base 5.3.0
- meta_background_image_cache_get_default@Base 5.3.0
- meta_background_image_cache_get_type@Base 5.3.0
- meta_background_image_cache_load@Base 5.3.0
- meta_background_image_cache_purge@Base 5.3.0
- meta_background_image_get_success@Base 5.3.0
- meta_background_image_get_texture@Base 5.3.0
- meta_background_image_get_type@Base 5.3.0
- meta_background_image_is_loaded@Base 5.3.0
- meta_background_new@Base 5.3.0
- meta_background_refresh_all@Base 5.3.0
- meta_background_set_blend@Base 5.3.0
- meta_background_set_color@Base 5.3.0
- meta_background_set_file@Base 5.3.0
- meta_background_set_gradient@Base 5.3.0
+ meta_create_background_for_monitor@Base 6.6.0
+ meta_get_background_actors_for_display@Base 6.6.0
meta_barrier_destroy@Base 5.3.0
meta_barrier_direction_get_type@Base 5.3.0
meta_barrier_event_get_type@Base 5.3.0
@@ -2593,6 +2572,8 @@ libmuffin.so.0 libmuffin0 #MINVER#
meta_virtual_modifier_get_type@Base 5.3.0
meta_warning@Base 5.3.0
meta_wayland_actor_surface_get_actor@Base 6.0.0
+ meta_wayland_background_actor_get_type@Base 6.6.0
+ meta_wayland_background_actor_new_for_monitor@Base 6.6.0
meta_wayland_compositor_get_default@Base 6.0.0
meta_wayland_get_wayland_display_name@Base 6.0.0
meta_wayland_get_xwayland_display_name@Base 6.0.0
@@ -2756,6 +2737,7 @@ libmuffin.so.0 libmuffin0 #MINVER#
meta_workspace_set_builtin_struts@Base 5.3.0
meta_x11_background_actor_get_type@Base 5.3.0
meta_x11_background_actor_new_for_display@Base 5.3.0
+ meta_x11_background_actor_new_for_monitor@Base 6.6.0
meta_x11_background_get_type@Base 5.3.0
meta_x11_background_transition_get_type@Base 5.3.0
meta_x11_display_clear_stage_input_region@Base 5.3.0
diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c
index 8bcc5273a..f472153b3 100644
--- a/src/compositor/compositor.c
+++ b/src/compositor/compositor.c
@@ -74,8 +74,7 @@
#include "meta/compositor-muffin.h"
#include "meta/main.h"
#include "meta/meta-backend.h"
-#include "meta/meta-background-actor.h"
-#include "meta/meta-background-group.h"
+#include "meta/meta-monitor-manager.h"
#include "meta/meta-shadow-factory.h"
#include "meta/meta-x11-errors.h"
#include "meta/meta-x11-background-actor.h"
@@ -85,6 +84,9 @@
#ifdef HAVE_WAYLAND
#include "compositor/meta-window-actor-wayland.h"
+#include "meta/meta-wayland-background-actor.h"
+#include "wayland/meta-wayland-layer-shell.h"
+#include "wayland/meta-wayland-outputs.h"
#include "wayland/meta-wayland-private.h"
#endif
@@ -127,7 +129,7 @@ typedef struct _MetaCompositorPrivate
ClutterActor *feedback_group;
ClutterActor *bottom_window_group;
- ClutterActor *background_actor;
+ GList *background_actors;
ClutterActor *desklet_container;
GList *windows;
@@ -579,6 +581,123 @@ meta_compositor_redirect_x11_windows (MetaCompositor *compositor)
redirect_windows (display->x11_display);
}
+static void
+rebuild_x11_background_actors (MetaCompositor *compositor)
+{
+ MetaCompositorPrivate *priv =
+ meta_compositor_get_instance_private (compositor);
+ MetaDisplay *display = priv->display;
+ int i, n;
+
+ g_list_free_full (priv->background_actors, (GDestroyNotify) clutter_actor_destroy);
+ priv->background_actors = NULL;
+
+ n = meta_display_get_n_monitors (display);
+ for (i = 0; i < n; i++)
+ {
+ ClutterActor *actor;
+ MetaRectangle rect;
+
+ actor = meta_x11_background_actor_new_for_monitor (display, i);
+ if (actor == NULL)
+ continue;
+
+ meta_display_get_monitor_geometry (display, i, &rect);
+ clutter_actor_set_position (actor, rect.x, rect.y);
+ clutter_actor_set_size (actor, rect.width, rect.height);
+
+ clutter_actor_add_child (priv->window_group, actor);
+ priv->background_actors = g_list_append (priv->background_actors, actor);
+ }
+}
+
+static void
+on_monitors_changed (MetaMonitorManager *monitor_manager,
+ MetaCompositor *compositor)
+{
+ if (!meta_is_wayland_compositor ())
+ rebuild_x11_background_actors (compositor);
+}
+
+#ifdef HAVE_WAYLAND
+static int
+get_monitor_index_for_layer_surface (MetaWaylandLayerSurface *layer_surface)
+{
+ MetaWaylandCompositor *wl_compositor =
+ meta_wayland_compositor_get_default ();
+ MetaWaylandOutput *surface_output =
+ meta_wayland_layer_surface_get_output (layer_surface);
+ MetaDisplay *display = meta_get_display ();
+ int i, n;
+
+ if (!surface_output)
+ return -1;
+
+ n = meta_display_get_n_monitors (display);
+ for (i = 0; i < n; i++)
+ {
+ MetaWaylandOutput *output =
+ meta_wayland_compositor_get_output_for_monitor (wl_compositor, i);
+ if (output == surface_output)
+ return i;
+ }
+
+ return -1;
+}
+
+static void
+on_layer_surface_mapped (MetaWaylandLayerShell *layer_shell,
+ MetaWaylandLayerSurface *layer_surface,
+ MetaCompositor *compositor)
+{
+ MetaCompositorPrivate *priv =
+ meta_compositor_get_instance_private (compositor);
+ MetaSurfaceActor *surface_actor;
+ int monitor_index;
+
+ if (meta_wayland_layer_surface_get_layer (layer_surface) !=
+ META_LAYER_SHELL_LAYER_BACKGROUND)
+ return;
+
+ surface_actor = meta_wayland_actor_surface_get_actor (
+ META_WAYLAND_ACTOR_SURFACE (layer_surface));
+ if (!surface_actor)
+ return;
+
+ monitor_index = get_monitor_index_for_layer_surface (layer_surface);
+
+ if (monitor_index < 0)
+ {
+ priv->background_actors =
+ g_list_append (priv->background_actors, surface_actor);
+ return;
+ }
+
+ priv->background_actors =
+ g_list_insert (priv->background_actors, surface_actor, monitor_index);
+}
+
+static void
+on_layer_surface_unmapped (MetaWaylandLayerShell *layer_shell,
+ MetaWaylandLayerSurface *layer_surface,
+ MetaCompositor *compositor)
+{
+ MetaCompositorPrivate *priv =
+ meta_compositor_get_instance_private (compositor);
+ MetaSurfaceActor *surface_actor;
+
+ if (meta_wayland_layer_surface_get_layer (layer_surface) !=
+ META_LAYER_SHELL_LAYER_BACKGROUND)
+ return;
+
+ surface_actor = meta_wayland_actor_surface_get_actor (
+ META_WAYLAND_ACTOR_SURFACE (layer_surface));
+
+ priv->background_actors =
+ g_list_remove (priv->background_actors, surface_actor);
+}
+#endif
+
gboolean
meta_compositor_do_manage (MetaCompositor *compositor,
GError **error)
@@ -615,14 +734,11 @@ meta_compositor_do_manage (MetaCompositor *compositor,
priv->feedback_group = meta_window_group_new (display);
if (!meta_is_wayland_compositor ())
- {
- priv->background_actor = meta_x11_background_actor_new_for_display (display);
- clutter_actor_add_child (priv->window_group, priv->background_actor);
- }
+ rebuild_x11_background_actors (compositor);
clutter_actor_add_child (priv->window_group, priv->bottom_window_group);
- // This needs to remain stacked just above the background actor in the window group.
+ // This needs to remain stacked just above the background actors in the window group.
// So sync_actor_stacking() has to be able to reference it. The deskletManager
// will take this and finish setting it up.
priv->desklet_container = clutter_actor_new ();
@@ -634,6 +750,27 @@ meta_compositor_do_manage (MetaCompositor *compositor,
if (!META_COMPOSITOR_GET_CLASS (compositor)->manage (compositor, error))
return FALSE;
+ g_signal_connect (meta_monitor_manager_get (), "monitors-changed",
+ G_CALLBACK (on_monitors_changed), compositor);
+
+#ifdef HAVE_WAYLAND
+ if (meta_is_wayland_compositor ())
+ {
+ MetaWaylandCompositor *wl_compositor =
+ meta_wayland_compositor_get_default ();
+ MetaWaylandLayerShell *layer_shell =
+ meta_wayland_compositor_get_layer_shell (wl_compositor);
+
+ if (layer_shell)
+ {
+ g_signal_connect (layer_shell, "layer-surface-mapped",
+ G_CALLBACK (on_layer_surface_mapped), compositor);
+ g_signal_connect (layer_shell, "layer-surface-unmapped",
+ G_CALLBACK (on_layer_surface_unmapped), compositor);
+ }
+ }
+#endif
+
priv->plugin_mgr = meta_plugin_manager_new (compositor);
clutter_actor_show (priv->stage);
@@ -963,9 +1100,7 @@ sync_actor_stacking (MetaCompositor *compositor)
{
ClutterActor *actor = old->data;
- if (META_IS_BACKGROUND_GROUP (actor) ||
- META_IS_BACKGROUND_ACTOR (actor) ||
- META_IS_X11_BACKGROUND_ACTOR (actor))
+ if (META_IS_X11_BACKGROUND_ACTOR (actor))
{
backgrounds = g_list_prepend (backgrounds, actor);
@@ -1018,31 +1153,6 @@ sync_actor_stacking (MetaCompositor *compositor)
// Then the bottom window group (which META_WINDOW_DESKTOP windows like nemo-desktop's get placed in).
clutter_actor_set_child_below_sibling (priv->window_group, priv->bottom_window_group, NULL);
- if (meta_is_wayland_compositor ())
- {
- children = clutter_actor_get_children (priv->bottom_window_group);
- for (tmp = children; tmp != NULL; tmp = tmp->next)
- {
- MetaWindowActor *child = tmp->data;
- MetaWindow *mw = meta_window_actor_get_meta_window (child);
-
- if (mw != NULL)
- {
- // CsdBackground manager sets _NET_WM_STATE_BELOW (gtk_window_set_keep_below)
- // This sets its stack layer to META_LAYER_BOTTOM, so we can keep these below
- // the nemo-desktop, etc..
- MetaStackLayer layer = meta_window_get_default_layer (mw);
-
- if (layer == META_LAYER_BOTTOM)
- {
- clutter_actor_set_child_below_sibling (priv->bottom_window_group, CLUTTER_ACTOR (child), NULL);
- }
- }
- }
-
- g_list_free (children);
- }
-
// and finally backgrounds..
/* we prepended the backgrounds above so the last actor in the list
@@ -1438,7 +1548,12 @@ meta_compositor_dispose (GObject *object)
g_clear_signal_handler (&priv->top_window_actor_destroy_id,
priv->top_window_actor);
- g_clear_pointer (&priv->background_actor, clutter_actor_destroy);
+ if (!meta_is_wayland_compositor ())
+ {
+ for (GList *l = priv->background_actors; l; l = l->next)
+ clutter_actor_destroy (l->data);
+ }
+ g_clear_pointer (&priv->background_actors, g_list_free);
g_clear_pointer (&priv->bottom_window_group, clutter_actor_destroy);
g_clear_pointer (&priv->desklet_container, clutter_actor_destroy);
g_clear_pointer (&priv->window_group, clutter_actor_destroy);
@@ -1783,11 +1898,10 @@ meta_compositor_get_laters (MetaCompositor *compositor)
* meta_get_x11_background_actor_for_display:
* @display: a #MetaDisplay
*
- * Gets the actor that draws the root window background under the windows.
- * The root window background automatically tracks the image or color set
- * by the environment.
+ * Deprecated. Returns the first per-monitor X11 background actor, or NULL.
+ * Use meta_get_background_actors_for_display() instead.
*
- * Returns: (transfer none): The background actor corresponding to @display
+ * Returns: (transfer none) (nullable): A background actor, or NULL
*/
ClutterActor *
meta_get_x11_background_actor_for_display (MetaDisplay *display)
@@ -1795,12 +1909,10 @@ meta_get_x11_background_actor_for_display (MetaDisplay *display)
MetaCompositorPrivate *priv =
meta_compositor_get_instance_private (display->compositor);
- if (meta_is_wayland_compositor ())
- {
- return NULL;
- }
+ if (meta_is_wayland_compositor () || priv->background_actors == NULL)
+ return NULL;
- return priv->background_actor;
+ return priv->background_actors->data;
}
/**
@@ -1853,3 +1965,50 @@ meta_update_desklet_stacking (MetaCompositor *compositor)
meta_stack_tracker_queue_sync_stack (priv->display->stack_tracker);
}
+
+/**
+ * meta_create_background_for_monitor:
+ * @display: a #MetaDisplay
+ * @monitor: the monitor index
+ *
+ * Creates a new standalone actor that displays the desktop background for the
+ * given monitor. On X11, this returns a per-monitor root pixmap actor. On
+ * Wayland, this returns a monitor-sized clone of the layer-shell background
+ * surface.
+ *
+ * These are independent copies, not the live compositor background actors.
+ * Use meta_get_background_actors_for_display() to get the actual actors
+ * in the scene graph.
+ *
+ * Returns: (transfer full): The background actor for @monitor
+ */
+ClutterActor *
+meta_create_background_for_monitor (MetaDisplay *display,
+ int monitor)
+{
+#ifdef HAVE_WAYLAND
+ if (meta_is_wayland_compositor ())
+ return meta_wayland_background_actor_new_for_monitor (display, monitor);
+#endif
+
+ return g_object_ref_sink (meta_x11_background_actor_new_for_monitor (display, monitor));
+}
+
+/**
+ * meta_get_background_actors_for_display:
+ * @display: a #MetaDisplay
+ *
+ * Returns a list of the live per-monitor background actors in the compositor's
+ * scene graph. These actors can have effects applied to them directly.
+ *
+ * Returns: (transfer none) (element-type Clutter.Actor): The list of
+ * background actors
+ */
+GList *
+meta_get_background_actors_for_display (MetaDisplay *display)
+{
+ MetaCompositorPrivate *priv =
+ meta_compositor_get_instance_private (display->compositor);
+
+ return priv->background_actors;
+}
diff --git a/src/compositor/meta-background-actor-private.h b/src/compositor/meta-background-actor-private.h
deleted file mode 100644
index bdf8c4744..000000000
--- a/src/compositor/meta-background-actor-private.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-#ifndef META_BACKGROUND_ACTOR_PRIVATE_H
-#define META_BACKGROUND_ACTOR_PRIVATE_H
-
-#include "meta/meta-background-actor.h"
-
-cairo_region_t *meta_background_actor_get_clip_region (MetaBackgroundActor *self);
-
-#endif /* META_BACKGROUND_ACTOR_PRIVATE_H */
diff --git a/src/compositor/meta-background-actor.c b/src/compositor/meta-background-actor.c
deleted file mode 100644
index 628fb3ff3..000000000
--- a/src/compositor/meta-background-actor.c
+++ /dev/null
@@ -1,1014 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * Copyright 2009 Sander Dijkhuis
- * Copyright 2014 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 .
- *
- * 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 "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;
-
- 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 cullable_iface_init (MetaCullableInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR,
- G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
-
-static void
-set_clip_region (MetaBackgroundActor *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 (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 min_filter, mag_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))
- {
- min_filter = COGL_PIPELINE_FILTER_NEAREST;
- mag_filter = COGL_PIPELINE_FILTER_NEAREST;
- }
- else
- {
- min_filter = COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST;
- mag_filter = COGL_PIPELINE_FILTER_LINEAR;
- }
-
- cogl_pipeline_set_layer_filters (self->pipeline, 0, min_filter, mag_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);
- 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);
-}
-
-static void
-meta_background_actor_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
-
- switch (prop_id)
- {
- case PROP_META_DISPLAY:
- self->display = g_value_get_object (value);
- 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);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-meta_background_actor_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- MetaBackgroundActor *self = META_BACKGROUND_ACTOR (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_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",
- META_TYPE_DISPLAY,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-
- g_object_class_install_property (object_class,
- PROP_META_DISPLAY,
- param_spec);
-
- param_spec = g_param_spec_int ("monitor",
- "monitor",
- "monitor",
- 0, G_MAXINT, 0,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-
- 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->vignette = FALSE;
- self->vignette_brightness = 1.0;
- self->vignette_sharpness = 0.0;
-}
-
-/**
- * meta_background_actor_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: the newly created background actor
- */
-ClutterActor *
-meta_background_actor_new (MetaDisplay *display,
- int monitor)
-{
- MetaBackgroundActor *self;
-
- self = g_object_new (META_TYPE_BACKGROUND_ACTOR,
- "meta-display", display,
- "monitor", monitor,
- NULL);
-
- return CLUTTER_ACTOR (self);
-}
-
-static void
-meta_background_actor_cull_out (MetaCullable *cullable,
- cairo_region_t *unobscured_region,
- cairo_region_t *clip_region)
-{
- MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable);
-
- set_unobscured_region (self, unobscured_region);
- set_clip_region (self, clip_region);
-}
-
-static void
-meta_background_actor_reset_culling (MetaCullable *cullable)
-{
- MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable);
-
- set_unobscured_region (self, NULL);
- set_clip_region (self, NULL);
-}
-
-static void
-cullable_iface_init (MetaCullableInterface *iface)
-{
- iface->cull_out = meta_background_actor_cull_out;
- iface->reset_culling = meta_background_actor_reset_culling;
-}
-
-/**
- * meta_background_actor_get_clip_region:
- * @self: a #MetaBackgroundActor
- *
- * Return value (transfer none): a #cairo_region_t that represents the part of
- * the background not obscured by other #MetaBackgroundActor or
- * #MetaWindowActor objects.
- */
-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));
-}
diff --git a/src/compositor/meta-background-group.c b/src/compositor/meta-background-group.c
deleted file mode 100644
index e30b8af4c..000000000
--- a/src/compositor/meta-background-group.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-/**
- * SECTION:meta-background-group
- * @title: MetaBackgroundGroup
- * @short_description: Container for background actors
- *
- * This class is a subclass of ClutterActor with special handling for
- * MetaBackgroundActor/MetaBackgroundGroup when painting children.
- * It makes sure to only draw the parts of the backgrounds not
- * occluded by opaque windows.
- *
- * See #MetaWindowGroup for more information behind the motivation,
- * and details on implementation.
- */
-
-#include "config.h"
-
-#include "compositor/meta-cullable.h"
-#include "meta/meta-background-group.h"
-
-static void cullable_iface_init (MetaCullableInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (MetaBackgroundGroup, meta_background_group, CLUTTER_TYPE_ACTOR,
- G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
-
-static void
-meta_background_group_class_init (MetaBackgroundGroupClass *klass)
-{
-}
-
-static void
-meta_background_group_cull_out (MetaCullable *cullable,
- cairo_region_t *unobscured_region,
- cairo_region_t *clip_region)
-{
- meta_cullable_cull_out_children (cullable, unobscured_region, clip_region);
-}
-
-static void
-meta_background_group_reset_culling (MetaCullable *cullable)
-{
- meta_cullable_reset_culling_children (cullable);
-}
-
-static void
-cullable_iface_init (MetaCullableInterface *iface)
-{
- iface->cull_out = meta_background_group_cull_out;
- iface->reset_culling = meta_background_group_reset_culling;
-}
-
-static void
-meta_background_group_init (MetaBackgroundGroup *self)
-{
-}
-
-ClutterActor *
-meta_background_group_new (void)
-{
- MetaBackgroundGroup *background_group;
-
- background_group = g_object_new (META_TYPE_BACKGROUND_GROUP, NULL);
-
- return CLUTTER_ACTOR (background_group);
-}
diff --git a/src/compositor/meta-background-image.c b/src/compositor/meta-background-image.c
deleted file mode 100644
index a29e74380..000000000
--- a/src/compositor/meta-background-image.c
+++ /dev/null
@@ -1,370 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * Copyright 2014 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 .
- */
-
-/**
- * SECTION:meta-background-image
- * @title: MetaBackgroundImage
- * @short_description: objects holding images loaded from files, used for backgrounds
- */
-
-#include "config.h"
-
-#include "meta/meta-background-image.h"
-
-#include
-#include
-
-#include "clutter/clutter.h"
-#include "compositor/cogl-utils.h"
-
-enum
-{
- LOADED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-/**
- * MetaBackgroundImageCache:
- *
- * #MetaBackgroundImageCache caches loading of textures for backgrounds; there's actually
- * nothing background specific about it, other than it is tuned to work well for
- * large images as typically are used for backgrounds.
- */
-struct _MetaBackgroundImageCache
-{
- GObject parent_instance;
-
- GHashTable *images;
-};
-
-/**
- * MetaBackgroundImage:
- *
- * #MetaBackgroundImage is an object that represents a loaded or loading background image.
- */
-struct _MetaBackgroundImage
-{
- GObject parent_instance;
- GFile *file;
- MetaBackgroundImageCache *cache;
- gboolean in_cache;
- gboolean loaded;
- CoglTexture *texture;
-};
-
-G_DEFINE_TYPE (MetaBackgroundImageCache, meta_background_image_cache, G_TYPE_OBJECT);
-
-static void
-meta_background_image_cache_init (MetaBackgroundImageCache *cache)
-{
- cache->images = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal);
-}
-
-static void
-meta_background_image_cache_finalize (GObject *object)
-{
- MetaBackgroundImageCache *cache = META_BACKGROUND_IMAGE_CACHE (object);
- GHashTableIter iter;
- gpointer key, value;
-
- g_hash_table_iter_init (&iter, cache->images);
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- MetaBackgroundImage *image = value;
- image->in_cache = FALSE;
- }
-
- g_hash_table_destroy (cache->images);
-
- G_OBJECT_CLASS (meta_background_image_cache_parent_class)->finalize (object);
-}
-
-static void
-meta_background_image_cache_class_init (MetaBackgroundImageCacheClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->finalize = meta_background_image_cache_finalize;
-}
-
-/**
- * meta_background_image_cache_get_default:
- *
- * Return value: (transfer none): the global singleton background cache
- */
-MetaBackgroundImageCache *
-meta_background_image_cache_get_default (void)
-{
- static MetaBackgroundImageCache *cache;
-
- if (cache == NULL)
- cache = g_object_new (META_TYPE_BACKGROUND_IMAGE_CACHE, NULL);
-
- return cache;
-}
-
-static void
-load_file (GTask *task,
- MetaBackgroundImage *image,
- gpointer task_data,
- GCancellable *cancellable)
-{
- GError *error = NULL;
- GdkPixbuf *pixbuf;
- GFileInputStream *stream;
-
- stream = g_file_read (image->file, NULL, &error);
- if (stream == NULL)
- {
- g_task_return_error (task, error);
- return;
- }
-
- pixbuf = gdk_pixbuf_new_from_stream (G_INPUT_STREAM (stream), NULL, &error);
- g_object_unref (stream);
-
- if (pixbuf == NULL)
- {
- g_task_return_error (task, error);
- return;
- }
-
- g_task_return_pointer (task, pixbuf, (GDestroyNotify) g_object_unref);
-}
-
-static void
-file_loaded (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- MetaBackgroundImage *image = META_BACKGROUND_IMAGE (source_object);
- GError *error = NULL;
- GError *catch_error = NULL;
- GTask *task;
- CoglTexture *texture;
- GdkPixbuf *pixbuf, *rotated;
- int width, height, row_stride;
- guchar *pixels;
- gboolean has_alpha;
-
- task = G_TASK (result);
- pixbuf = g_task_propagate_pointer (task, &error);
-
- if (pixbuf == NULL)
- {
- char *uri = g_file_get_uri (image->file);
- g_warning ("Failed to load background '%s': %s",
- uri, error->message);
- g_clear_error (&error);
- g_free (uri);
- goto out;
- }
-
- rotated = gdk_pixbuf_apply_embedded_orientation (pixbuf);
- if (rotated != NULL)
- {
- g_object_unref (pixbuf);
- pixbuf = rotated;
- }
-
- width = gdk_pixbuf_get_width (pixbuf);
- height = gdk_pixbuf_get_height (pixbuf);
- row_stride = gdk_pixbuf_get_rowstride (pixbuf);
- pixels = gdk_pixbuf_get_pixels (pixbuf);
- has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
-
- texture = meta_create_texture (width, height,
- has_alpha ? COGL_TEXTURE_COMPONENTS_RGBA : COGL_TEXTURE_COMPONENTS_RGB,
- META_TEXTURE_ALLOW_SLICING);
-
- if (!cogl_texture_set_data (texture,
- has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
- row_stride,
- pixels, 0,
- &catch_error))
- {
- g_warning ("Failed to create texture for background");
- g_error_free (catch_error);
- cogl_object_unref (texture);
- }
-
- image->texture = texture;
-
-out:
- if (pixbuf != NULL)
- g_object_unref (pixbuf);
-
- image->loaded = TRUE;
- g_signal_emit (image, signals[LOADED], 0);
-}
-
-/**
- * meta_background_image_cache_load:
- * @cache: a #MetaBackgroundImageCache
- * @file: #GFile to load
- *
- * Loads an image to use as a background, or returns a reference to an
- * image that is already in the process of loading or loaded. In either
- * case, what is returned is a #MetaBackgroundImage which can be derefenced
- * to get a #CoglTexture. If meta_background_image_is_loaded() returns %TRUE,
- * the background is loaded, otherwise the MetaBackgroundImage::loaded
- * signal will be emitted exactly once. The 'loaded' state means that the
- * loading process finished, whether it succeeded or failed.
- *
- * Return value: (transfer full): a #MetaBackgroundImage to dereference to get the loaded texture
- */
-MetaBackgroundImage *
-meta_background_image_cache_load (MetaBackgroundImageCache *cache,
- GFile *file)
-{
- MetaBackgroundImage *image;
- GTask *task;
-
- g_return_val_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache), NULL);
- g_return_val_if_fail (file != NULL, NULL);
-
- image = g_hash_table_lookup (cache->images, file);
- if (image != NULL)
- return g_object_ref (image);
-
- image = g_object_new (META_TYPE_BACKGROUND_IMAGE, NULL);
- image->cache = cache;
- image->in_cache = TRUE;
- image->file = g_object_ref (file);
- g_hash_table_insert (cache->images, image->file, image);
-
- task = g_task_new (image, NULL, file_loaded, NULL);
-
- g_task_run_in_thread (task, (GTaskThreadFunc) load_file);
- g_object_unref (task);
-
- return image;
-}
-
-/**
- * meta_background_image_cache_purge:
- * @cache: a #MetaBackgroundImageCache
- * @file: file to remove from the cache
- *
- * Remove an entry from the cache; this would be used if monitoring
- * showed that the file changed.
- */
-void
-meta_background_image_cache_purge (MetaBackgroundImageCache *cache,
- GFile *file)
-{
- MetaBackgroundImage *image;
-
- g_return_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache));
- g_return_if_fail (file != NULL);
-
- image = g_hash_table_lookup (cache->images, file);
- if (image == NULL)
- return;
-
- g_hash_table_remove (cache->images, image->file);
- image->in_cache = FALSE;
-}
-
-G_DEFINE_TYPE (MetaBackgroundImage, meta_background_image, G_TYPE_OBJECT);
-
-static void
-meta_background_image_init (MetaBackgroundImage *image)
-{
-}
-
-static void
-meta_background_image_finalize (GObject *object)
-{
- MetaBackgroundImage *image = META_BACKGROUND_IMAGE (object);
-
- if (image->in_cache)
- g_hash_table_remove (image->cache->images, image->file);
-
- if (image->texture)
- cogl_object_unref (image->texture);
- if (image->file)
- g_object_unref (image->file);
-
- G_OBJECT_CLASS (meta_background_image_parent_class)->finalize (object);
-}
-
-static void
-meta_background_image_class_init (MetaBackgroundImageClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->finalize = meta_background_image_finalize;
-
- signals[LOADED] =
- g_signal_new ("loaded",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-}
-
-/**
- * meta_background_image_is_loaded:
- * @image: a #MetaBackgroundImage
- *
- * Return value: %TRUE if loading has already completed, %FALSE otherwise
- */
-gboolean
-meta_background_image_is_loaded (MetaBackgroundImage *image)
-{
- g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), FALSE);
-
- return image->loaded;
-}
-
-/**
- * meta_background_image_get_success:
- * @image: a #MetaBackgroundImage
- *
- * This function is a convenience function for checking for success,
- * without having to call meta_background_image_get_texture() and
- * handle the return of a Cogl type.
- *
- * Return value: %TRUE if loading completed successfully, otherwise %FALSE
- */
-gboolean
-meta_background_image_get_success (MetaBackgroundImage *image)
-{
- g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), FALSE);
-
- return image->texture != NULL;
-}
-
-/**
- * meta_background_image_get_texture:
- * @image: a #MetaBackgroundImage
- *
- * Return value: (transfer none): a #CoglTexture if loading succeeded; if
- * loading failed or has not yet finished, %NULL.
- */
-CoglTexture *
-meta_background_image_get_texture (MetaBackgroundImage *image)
-{
- g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), NULL);
-
- return image->texture;
-}
diff --git a/src/compositor/meta-background-private.h b/src/compositor/meta-background-private.h
deleted file mode 100644
index 871487da4..000000000
--- a/src/compositor/meta-background-private.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-#ifndef META_BACKGROUND_PRIVATE_H
-#define META_BACKGROUND_PRIVATE_H
-
-#include "cogl/cogl.h"
-#include "meta/meta-background.h"
-
-CoglTexture *meta_background_get_texture (MetaBackground *self,
- int monitor_index,
- cairo_rectangle_int_t *texture_area,
- CoglPipelineWrapMode *wrap_mode);
-
-#endif /* META_BACKGROUND_PRIVATE_H */
diff --git a/src/compositor/meta-background.c b/src/compositor/meta-background.c
deleted file mode 100644
index e02d40a61..000000000
--- a/src/compositor/meta-background.c
+++ /dev/null
@@ -1,1038 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-/*
- * Copyright 2013 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 .
- */
-
-#include "config.h"
-
-#include "compositor/meta-background-private.h"
-
-#include
-
-#include "backends/meta-backend-private.h"
-#include "compositor/cogl-utils.h"
-#include "meta/display.h"
-#include "meta/meta-background-image.h"
-#include "meta/meta-background.h"
-#include "meta/meta-monitor-manager.h"
-#include "meta/util.h"
-
-enum
-{
- CHANGED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct _MetaBackgroundMonitor MetaBackgroundMonitor;
-
-struct _MetaBackgroundMonitor
-{
- gboolean dirty;
- CoglTexture *texture;
- CoglFramebuffer *fbo;
-};
-
-struct _MetaBackground
-{
- GObject parent;
-
- MetaDisplay *display;
- MetaBackgroundMonitor *monitors;
- int n_monitors;
-
- CDesktopBackgroundStyle style;
- CDesktopBackgroundShading shading_direction;
- ClutterColor color;
- ClutterColor second_color;
-
- GFile *file1;
- MetaBackgroundImage *background_image1;
- GFile *file2;
- MetaBackgroundImage *background_image2;
-
- CoglTexture *color_texture;
- CoglTexture *wallpaper_texture;
-
- float blend_factor;
-
- guint wallpaper_allocation_failed : 1;
-};
-
-enum
-{
- PROP_META_DISPLAY = 1,
- PROP_MONITOR,
-};
-
-G_DEFINE_TYPE (MetaBackground, meta_background, G_TYPE_OBJECT)
-
-static gboolean texture_has_alpha (CoglTexture *texture);
-
-static GSList *all_backgrounds = NULL;
-
-static void
-free_fbos (MetaBackground *self)
-{
- int i;
-
- for (i = 0; i < self->n_monitors; i++)
- {
- MetaBackgroundMonitor *monitor = &self->monitors[i];
- if (monitor->fbo)
- {
- cogl_object_unref (monitor->fbo);
- monitor->fbo = NULL;
- }
- if (monitor->texture)
- {
- cogl_object_unref (monitor->texture);
- monitor->texture = NULL;
- }
- }
-}
-
-static void
-free_color_texture (MetaBackground *self)
-{
- if (self->color_texture != NULL)
- {
- cogl_object_unref (self->color_texture);
- self->color_texture = NULL;
- }
-}
-
-static void
-free_wallpaper_texture (MetaBackground *self)
-{
- if (self->wallpaper_texture != NULL)
- {
- cogl_object_unref (self->wallpaper_texture);
- self->wallpaper_texture = NULL;
- }
-
- self->wallpaper_allocation_failed = FALSE;
-}
-
-static void
-invalidate_monitor_backgrounds (MetaBackground *self)
-{
- free_fbos (self);
- g_free (self->monitors);
- self->monitors = NULL;
- self->n_monitors = 0;
-
- if (self->display)
- {
- int i;
-
- self->n_monitors = meta_display_get_n_monitors (self->display);
- self->monitors = g_new0 (MetaBackgroundMonitor, self->n_monitors);
-
- for (i = 0; i < self->n_monitors; i++)
- self->monitors[i].dirty = TRUE;
- }
-}
-
-static void
-on_monitors_changed (MetaBackground *self)
-{
- invalidate_monitor_backgrounds (self);
-}
-
-static void
-set_display (MetaBackground *self,
- MetaDisplay *display)
-{
- g_set_object (&self->display, display);
-
- invalidate_monitor_backgrounds (self);
-}
-
-static void
-meta_background_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- switch (prop_id)
- {
- case PROP_META_DISPLAY:
- set_display (META_BACKGROUND (object), g_value_get_object (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-meta_background_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- MetaBackground *self = META_BACKGROUND (object);
-
- switch (prop_id)
- {
- case PROP_META_DISPLAY:
- g_value_set_object (value, self->display);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static gboolean
-need_prerender (MetaBackground *self)
-{
- CoglTexture *texture1 = self->background_image1 ? meta_background_image_get_texture (self->background_image1) : NULL;
- CoglTexture *texture2 = self->background_image2 ? meta_background_image_get_texture (self->background_image2) : NULL;
-
- if (texture1 == NULL && texture2 == NULL)
- return FALSE;
-
- if (texture2 == NULL && self->style == C_DESKTOP_BACKGROUND_STYLE_WALLPAPER)
- return FALSE;
-
- return TRUE;
-}
-
-static void
-mark_changed (MetaBackground *self)
-{
- int i;
-
- if (!need_prerender (self))
- free_fbos (self);
-
- for (i = 0; i < self->n_monitors; i++)
- self->monitors[i].dirty = TRUE;
-
- g_signal_emit (self, signals[CHANGED], 0);
-}
-
-static void
-on_background_loaded (MetaBackgroundImage *image,
- MetaBackground *self)
-{
- mark_changed (self);
-}
-
-static gboolean
-file_equal0 (GFile *file1,
- GFile *file2)
-{
- if (file1 == file2)
- return TRUE;
-
- if ((file1 == NULL) || (file2 == NULL))
- return FALSE;
-
- return g_file_equal (file1, file2);
-}
-
-static void
-set_file (MetaBackground *self,
- GFile **filep,
- MetaBackgroundImage **imagep,
- GFile *file,
- gboolean force_reload)
-{
- if (force_reload || !file_equal0 (*filep, file))
- {
- if (*imagep)
- {
- g_signal_handlers_disconnect_by_func (*imagep,
- (gpointer)on_background_loaded,
- self);
- g_object_unref (*imagep);
- *imagep = NULL;
- }
-
- g_set_object (filep, file);
-
- if (file)
- {
- MetaBackgroundImageCache *cache = meta_background_image_cache_get_default ();
-
- *imagep = meta_background_image_cache_load (cache, file);
- g_signal_connect (*imagep, "loaded",
- G_CALLBACK (on_background_loaded), self);
- }
- }
-}
-
-static void
-on_gl_video_memory_purged (MetaBackground *self)
-{
- MetaBackgroundImageCache *cache = meta_background_image_cache_get_default ();
-
- /* The GPU memory that just got invalidated is the texture inside
- * self->background_image1,2 and/or its mipmaps. However, to save memory the
- * original pixbuf isn't kept in RAM so we can't do a simple re-upload. The
- * only copy of the image was the one in texture memory that got invalidated.
- * So we need to do a full reload from disk.
- */
- if (self->file1)
- {
- meta_background_image_cache_purge (cache, self->file1);
- set_file (self, &self->file1, &self->background_image1, self->file1, TRUE);
- }
-
- if (self->file2)
- {
- meta_background_image_cache_purge (cache, self->file2);
- set_file (self, &self->file2, &self->background_image2, self->file2, TRUE);
- }
-
- mark_changed (self);
-}
-
-static void
-meta_background_dispose (GObject *object)
-{
- MetaBackground *self = META_BACKGROUND (object);
-
- free_color_texture (self);
- free_wallpaper_texture (self);
-
- set_file (self, &self->file1, &self->background_image1, NULL, FALSE);
- set_file (self, &self->file2, &self->background_image2, NULL, FALSE);
-
- set_display (self, NULL);
-
- G_OBJECT_CLASS (meta_background_parent_class)->dispose (object);
-}
-
-static void
-meta_background_finalize (GObject *object)
-{
- all_backgrounds = g_slist_remove (all_backgrounds, object);
-
- G_OBJECT_CLASS (meta_background_parent_class)->finalize (object);
-}
-
-static void
-meta_background_constructed (GObject *object)
-{
- MetaBackground *self = META_BACKGROUND (object);
- MetaMonitorManager *monitor_manager = meta_monitor_manager_get ();
-
- G_OBJECT_CLASS (meta_background_parent_class)->constructed (object);
-
- g_signal_connect_object (self->display, "gl-video-memory-purged",
- G_CALLBACK (on_gl_video_memory_purged), object, G_CONNECT_SWAPPED);
-
- g_signal_connect_object (monitor_manager, "monitors-changed",
- G_CALLBACK (on_monitors_changed), self,
- G_CONNECT_SWAPPED);
-}
-
-static void
-meta_background_class_init (MetaBackgroundClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GParamSpec *param_spec;
-
- object_class->dispose = meta_background_dispose;
- object_class->finalize = meta_background_finalize;
- object_class->constructed = meta_background_constructed;
- object_class->set_property = meta_background_set_property;
- object_class->get_property = meta_background_get_property;
-
- signals[CHANGED] =
- g_signal_new ("changed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-
- param_spec = g_param_spec_object ("meta-display",
- "MetaDisplay",
- "MetaDisplay",
- META_TYPE_DISPLAY,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
-
- g_object_class_install_property (object_class,
- PROP_META_DISPLAY,
- param_spec);
-
-}
-
-static void
-meta_background_init (MetaBackground *self)
-{
- all_backgrounds = g_slist_prepend (all_backgrounds, self);
-}
-
-static void
-set_texture_area_from_monitor_area (cairo_rectangle_int_t *monitor_area,
- cairo_rectangle_int_t *texture_area)
-{
- texture_area->x = 0;
- texture_area->y = 0;
- texture_area->width = monitor_area->width;
- texture_area->height = monitor_area->height;
-}
-
-static void
-get_texture_area (MetaBackground *self,
- cairo_rectangle_int_t *monitor_rect,
- float monitor_scale,
- CoglTexture *texture,
- cairo_rectangle_int_t *texture_area)
-{
- cairo_rectangle_int_t image_area;
- int screen_width, screen_height;
- float texture_width, texture_height;
- float monitor_x_scale, monitor_y_scale;
-
- texture_width = cogl_texture_get_width (texture);
- texture_height = cogl_texture_get_height (texture);
-
- switch (self->style)
- {
- case C_DESKTOP_BACKGROUND_STYLE_STRETCHED:
- default:
- /* paint region is whole actor, and the texture
- * is scaled disproportionately to fit the actor
- */
- set_texture_area_from_monitor_area (monitor_rect, texture_area);
- break;
- case C_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
- meta_display_get_size (self->display, &screen_width, &screen_height);
-
- /* Start off by centering a tile in the middle of the
- * total screen area taking care of the monitor scaling.
- */
- image_area.x = (screen_width - texture_width) / 2.0;
- image_area.y = (screen_height - texture_height) / 2.0;
- image_area.width = texture_width;
- image_area.height = texture_height;
-
- /* Translate into the coordinate system of the particular monitor */
- image_area.x -= monitor_rect->x;
- image_area.y -= monitor_rect->y;
-
- *texture_area = image_area;
- break;
- case C_DESKTOP_BACKGROUND_STYLE_CENTERED:
- /* paint region is the original image size centered in the actor,
- * and the texture is scaled to the original image size */
- image_area.width = texture_width;
- image_area.height = texture_height;
- image_area.x = monitor_rect->width / 2 - image_area.width / 2;
- image_area.y = monitor_rect->height / 2 - image_area.height / 2;
-
- *texture_area = image_area;
- break;
- case C_DESKTOP_BACKGROUND_STYLE_SCALED:
- case C_DESKTOP_BACKGROUND_STYLE_ZOOM:
- /* paint region is the actor size in one dimension, and centered and
- * scaled by proportional amount in the other dimension.
- *
- * SCALED forces the centered dimension to fit on screen.
- * ZOOM forces the centered dimension to grow off screen
- */
- monitor_x_scale = monitor_rect->width / texture_width;
- monitor_y_scale = monitor_rect->height / texture_height;
-
- if ((self->style == C_DESKTOP_BACKGROUND_STYLE_SCALED &&
- (monitor_x_scale < monitor_y_scale)) ||
- (self->style == C_DESKTOP_BACKGROUND_STYLE_ZOOM &&
- (monitor_x_scale > monitor_y_scale)))
- {
- /* Fill image to exactly fit actor horizontally */
- image_area.width = monitor_rect->width;
- image_area.height = texture_height * monitor_x_scale;
-
- /* Position image centered vertically in actor */
- image_area.x = 0;
- image_area.y = monitor_rect->height / 2 - image_area.height / 2;
- }
- else
- {
- /* Scale image to exactly fit actor vertically */
- image_area.width = texture_width * monitor_y_scale;
- image_area.height = monitor_rect->height;
-
- /* Position image centered horizontally in actor */
- image_area.x = monitor_rect->width / 2 - image_area.width / 2;
- image_area.y = 0;
- }
-
- *texture_area = image_area;
- break;
-
- case C_DESKTOP_BACKGROUND_STYLE_SPANNED:
- {
- /* paint region is the union of all monitors, with the origin
- * of the region set to align with monitor associated with the background.
- */
- meta_display_get_size (self->display, &screen_width, &screen_height);
-
- /* unclipped texture area is whole screen, scaled depending on monitor */
- image_area.width = screen_width * monitor_scale;
- image_area.height = screen_height * monitor_scale;
-
- /* But make (0,0) line up with the appropriate monitor */
- image_area.x = -monitor_rect->x;
- image_area.y = -monitor_rect->y;
-
- *texture_area = image_area;
- break;
- }
- }
-}
-
-static gboolean
-draw_texture (MetaBackground *self,
- CoglFramebuffer *framebuffer,
- CoglPipeline *pipeline,
- CoglTexture *texture,
- cairo_rectangle_int_t *monitor_area,
- float monitor_scale)
-{
- cairo_rectangle_int_t texture_area;
- gboolean bare_region_visible;
-
- get_texture_area (self, monitor_area, monitor_scale, texture, &texture_area);
-
- switch (self->style)
- {
- case C_DESKTOP_BACKGROUND_STYLE_STRETCHED:
- case C_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
- case C_DESKTOP_BACKGROUND_STYLE_ZOOM:
- case C_DESKTOP_BACKGROUND_STYLE_SPANNED:
- /* Draw the entire monitor */
- cogl_framebuffer_draw_textured_rectangle (framebuffer,
- pipeline,
- 0,
- 0,
- monitor_area->width,
- monitor_area->height,
- - texture_area.x / (float)texture_area.width,
- - texture_area.y / (float)texture_area.height,
- (monitor_area->width - texture_area.x) / (float)texture_area.width,
- (monitor_area->height - texture_area.y) / (float)texture_area.height);
-
- bare_region_visible = texture_has_alpha (texture);
-
- /* Draw just the texture */
- break;
- case C_DESKTOP_BACKGROUND_STYLE_CENTERED:
- case C_DESKTOP_BACKGROUND_STYLE_SCALED:
- cogl_framebuffer_draw_textured_rectangle (framebuffer,
- pipeline,
- texture_area.x, texture_area.y,
- texture_area.x + texture_area.width,
- texture_area.y + texture_area.height,
- 0, 0, 1.0, 1.0);
- bare_region_visible = texture_has_alpha (texture) || memcmp (&texture_area, monitor_area, sizeof (cairo_rectangle_int_t)) != 0;
- break;
- case C_DESKTOP_BACKGROUND_STYLE_NONE:
- bare_region_visible = TRUE;
- break;
- default:
- g_return_val_if_reached(FALSE);
- }
-
- return bare_region_visible;
-}
-
-static void
-ensure_color_texture (MetaBackground *self)
-{
- if (self->color_texture == NULL)
- {
- ClutterBackend *backend = clutter_get_default_backend ();
- CoglContext *ctx = clutter_backend_get_cogl_context (backend);
- GError *error = NULL;
- uint8_t pixels[6];
- int width, height;
-
- if (self->shading_direction == C_DESKTOP_BACKGROUND_SHADING_SOLID)
- {
- width = 1;
- height = 1;
-
- pixels[0] = self->color.red;
- pixels[1] = self->color.green;
- pixels[2] = self->color.blue;
- }
- else
- {
- switch (self->shading_direction)
- {
- case C_DESKTOP_BACKGROUND_SHADING_VERTICAL:
- width = 1;
- height = 2;
- break;
- case C_DESKTOP_BACKGROUND_SHADING_HORIZONTAL:
- width = 2;
- height = 1;
- break;
- default:
- g_return_if_reached ();
- }
-
- pixels[0] = self->color.red;
- pixels[1] = self->color.green;
- pixels[2] = self->color.blue;
- pixels[3] = self->second_color.red;
- pixels[4] = self->second_color.green;
- pixels[5] = self->second_color.blue;
- }
-
- self->color_texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height,
- COGL_PIXEL_FORMAT_RGB_888,
- width * 3,
- pixels,
- &error));
-
- if (error != NULL)
- {
- meta_warning ("Failed to allocate color texture: %s\n", error->message);
- g_error_free (error);
- }
- }
-}
-
-typedef enum
-{
- PIPELINE_REPLACE,
- PIPELINE_ADD,
- PIPELINE_OVER_REVERSE,
-} PipelineType;
-
-static CoglPipeline *
-create_pipeline (PipelineType type)
-{
- const char * const blend_strings[3] = {
- [PIPELINE_REPLACE] = "RGBA = ADD (SRC_COLOR, 0)",
- [PIPELINE_ADD] = "RGBA = ADD (SRC_COLOR, DST_COLOR)",
- [PIPELINE_OVER_REVERSE] = "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)",
- };
- static CoglPipeline *templates[3];
-
- if (templates[type] == NULL)
- {
- templates[type] = meta_create_texture_pipeline (NULL);
- cogl_pipeline_set_blend (templates[type], blend_strings[type], NULL);
- }
-
- cogl_pipeline_set_layer_filters (templates[type],
- 0,
- COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR,
- COGL_PIPELINE_FILTER_LINEAR);
-
- return cogl_pipeline_copy (templates[type]);
-}
-
-static gboolean
-texture_has_alpha (CoglTexture *texture)
-{
- if (!texture)
- return FALSE;
-
- switch (cogl_texture_get_components (texture))
- {
- case COGL_TEXTURE_COMPONENTS_A:
- case COGL_TEXTURE_COMPONENTS_RGBA:
- return TRUE;
- case COGL_TEXTURE_COMPONENTS_RG:
- case COGL_TEXTURE_COMPONENTS_RGB:
- case COGL_TEXTURE_COMPONENTS_DEPTH:
- return FALSE;
- default:
- g_assert_not_reached ();
- return FALSE;
- }
-}
-
-static gboolean
-ensure_wallpaper_texture (MetaBackground *self,
- CoglTexture *texture)
-{
- if (self->wallpaper_texture == NULL && !self->wallpaper_allocation_failed)
- {
- int width = cogl_texture_get_width (texture);
- int height = cogl_texture_get_height (texture);
- CoglOffscreen *offscreen;
- CoglFramebuffer *fbo;
- GError *catch_error = NULL;
- CoglPipeline *pipeline;
-
- self->wallpaper_texture = meta_create_texture (width, height,
- COGL_TEXTURE_COMPONENTS_RGBA,
- META_TEXTURE_FLAGS_NONE);
- offscreen = cogl_offscreen_new_with_texture (self->wallpaper_texture);
- fbo = COGL_FRAMEBUFFER (offscreen);
-
- if (!cogl_framebuffer_allocate (fbo, &catch_error))
- {
- /* This probably means that the size of the wallpapered texture is larger
- * than the maximum texture size; we treat it as permanent until the
- * background is changed again.
- */
- g_error_free (catch_error);
-
- cogl_object_unref (self->wallpaper_texture);
- self->wallpaper_texture = NULL;
- cogl_object_unref (fbo);
-
- self->wallpaper_allocation_failed = TRUE;
- return FALSE;
- }
-
- cogl_framebuffer_orthographic (fbo, 0, 0,
- width, height, -1., 1.);
-
- pipeline = create_pipeline (PIPELINE_REPLACE);
- cogl_pipeline_set_layer_texture (pipeline, 0, texture);
- cogl_framebuffer_draw_textured_rectangle (fbo, pipeline, 0, 0, width, height,
- 0., 0., 1., 1.);
- cogl_object_unref (pipeline);
-
- if (texture_has_alpha (texture))
- {
- ensure_color_texture (self);
-
- pipeline = create_pipeline (PIPELINE_OVER_REVERSE);
- cogl_pipeline_set_layer_texture (pipeline, 0, self->color_texture);
- cogl_framebuffer_draw_rectangle (fbo, pipeline, 0, 0, width, height);
- cogl_object_unref (pipeline);
- }
-
- cogl_object_unref (fbo);
- }
-
- return self->wallpaper_texture != NULL;
-}
-
-static CoglPipelineWrapMode
-get_wrap_mode (CDesktopBackgroundStyle style)
-{
- switch (style)
- {
- case C_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
- return COGL_PIPELINE_WRAP_MODE_REPEAT;
- case C_DESKTOP_BACKGROUND_STYLE_NONE:
- case C_DESKTOP_BACKGROUND_STYLE_STRETCHED:
- case C_DESKTOP_BACKGROUND_STYLE_CENTERED:
- case C_DESKTOP_BACKGROUND_STYLE_SCALED:
- case C_DESKTOP_BACKGROUND_STYLE_ZOOM:
- case C_DESKTOP_BACKGROUND_STYLE_SPANNED:
- default:
- return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
- }
-}
-
-static int
-get_best_mipmap_level (CoglTexture *texture,
- int visible_width,
- int visible_height)
-{
- int mipmap_width = cogl_texture_get_width (texture);
- int mipmap_height = cogl_texture_get_height (texture);
- int halves = 0;
-
- while (mipmap_width >= visible_width && mipmap_height >= visible_height)
- {
- halves++;
- mipmap_width /= 2;
- mipmap_height /= 2;
- }
-
- return MAX (0, halves - 1);
-}
-
-CoglTexture *
-meta_background_get_texture (MetaBackground *self,
- int monitor_index,
- cairo_rectangle_int_t *texture_area,
- CoglPipelineWrapMode *wrap_mode)
-{
- MetaBackgroundMonitor *monitor;
- MetaRectangle geometry;
- cairo_rectangle_int_t monitor_area;
- CoglTexture *texture1, *texture2;
- float monitor_scale;
-
- g_return_val_if_fail (META_IS_BACKGROUND (self), NULL);
- g_return_val_if_fail (monitor_index >= 0 && monitor_index < self->n_monitors, NULL);
-
- monitor = &self->monitors[monitor_index];
-
- meta_display_get_monitor_geometry (self->display, monitor_index, &geometry);
- monitor_scale = meta_display_get_monitor_scale (self->display, monitor_index);
- monitor_area.x = geometry.x;
- monitor_area.y = geometry.y;
- monitor_area.width = geometry.width;
- monitor_area.height = geometry.height;
-
- texture1 = self->background_image1 ? meta_background_image_get_texture (self->background_image1) : NULL;
- texture2 = self->background_image2 ? meta_background_image_get_texture (self->background_image2) : NULL;
-
- if (texture1 == NULL && texture2 == NULL)
- {
- ensure_color_texture (self);
- if (texture_area)
- set_texture_area_from_monitor_area (&monitor_area, texture_area);
- if (wrap_mode)
- *wrap_mode = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
- return self->color_texture;
- }
-
- if (texture2 == NULL && self->style == C_DESKTOP_BACKGROUND_STYLE_WALLPAPER &&
- self->shading_direction == C_DESKTOP_BACKGROUND_SHADING_SOLID &&
- ensure_wallpaper_texture (self, texture1))
- {
- if (texture_area)
- get_texture_area (self, &monitor_area, monitor_scale,
- self->wallpaper_texture, texture_area);
- if (wrap_mode)
- *wrap_mode = COGL_PIPELINE_WRAP_MODE_REPEAT;
- return self->wallpaper_texture;
- }
-
- if (monitor->dirty)
- {
- GError *catch_error = NULL;
- gboolean bare_region_visible = FALSE;
- int texture_width, texture_height;
-
- if (meta_is_stage_views_scaled ())
- {
- texture_width = monitor_area.width * monitor_scale;
- texture_height = monitor_area.height * monitor_scale;
- }
- else
- {
- texture_width = monitor_area.width;
- texture_height = monitor_area.height;
- }
-
- if (monitor->texture == NULL)
- {
- CoglOffscreen *offscreen;
-
- monitor->texture = meta_create_texture (texture_width,
- texture_height,
- COGL_TEXTURE_COMPONENTS_RGBA,
- META_TEXTURE_FLAGS_NONE);
- offscreen = cogl_offscreen_new_with_texture (monitor->texture);
- monitor->fbo = COGL_FRAMEBUFFER (offscreen);
- }
-
- if (self->style != C_DESKTOP_BACKGROUND_STYLE_WALLPAPER)
- {
- monitor_area.x *= monitor_scale;
- monitor_area.y *= monitor_scale;
- monitor_area.width *= monitor_scale;
- monitor_area.height *= monitor_scale;
- }
-
- if (!cogl_framebuffer_allocate (monitor->fbo, &catch_error))
- {
- /* Texture or framebuffer allocation failed; it's unclear why this happened;
- * we'll try again the next time this is called. (MetaBackgroundActor
- * caches the result, so user might be left without a background.)
- */
- cogl_object_unref (monitor->texture);
- monitor->texture = NULL;
- cogl_object_unref (monitor->fbo);
- monitor->fbo = NULL;
-
- g_error_free (catch_error);
- return NULL;
- }
-
- cogl_framebuffer_orthographic (monitor->fbo, 0, 0,
- monitor_area.width, monitor_area.height, -1., 1.);
-
- if (texture2 != NULL && self->blend_factor != 0.0)
- {
- CoglPipeline *pipeline = create_pipeline (PIPELINE_REPLACE);
- int mipmap_level;
-
- mipmap_level = get_best_mipmap_level (texture2,
- texture_width,
- texture_height);
-
- cogl_pipeline_set_color4f (pipeline,
- self->blend_factor, self->blend_factor, self->blend_factor, self->blend_factor);
- cogl_pipeline_set_layer_texture (pipeline, 0, texture2);
- cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (self->style));
- cogl_pipeline_set_layer_max_mipmap_level (pipeline, 0, mipmap_level);
-
- bare_region_visible = draw_texture (self,
- monitor->fbo, pipeline,
- texture2, &monitor_area,
- monitor_scale);
-
- cogl_object_unref (pipeline);
- }
- else
- {
- cogl_framebuffer_clear4f (monitor->fbo,
- COGL_BUFFER_BIT_COLOR,
- 0.0, 0.0, 0.0, 0.0);
- }
-
- if (texture1 != NULL && self->blend_factor != 1.0)
- {
- CoglPipeline *pipeline = create_pipeline (PIPELINE_ADD);
- int mipmap_level;
-
- mipmap_level = get_best_mipmap_level (texture1,
- texture_width,
- texture_height);
-
- cogl_pipeline_set_color4f (pipeline,
- (1 - self->blend_factor),
- (1 - self->blend_factor),
- (1 - self->blend_factor),
- (1 - self->blend_factor));;
- cogl_pipeline_set_layer_texture (pipeline, 0, texture1);
- cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (self->style));
- cogl_pipeline_set_layer_max_mipmap_level (pipeline, 0, mipmap_level);
-
- bare_region_visible = bare_region_visible || draw_texture (self,
- monitor->fbo, pipeline,
- texture1, &monitor_area,
- monitor_scale);
-
- cogl_object_unref (pipeline);
- }
-
- if (bare_region_visible)
- {
- CoglPipeline *pipeline = create_pipeline (PIPELINE_OVER_REVERSE);
-
- ensure_color_texture (self);
- cogl_pipeline_set_layer_texture (pipeline, 0, self->color_texture);
- cogl_framebuffer_draw_rectangle (monitor->fbo,
- pipeline,
- 0, 0,
- monitor_area.width, monitor_area.height);
- cogl_object_unref (pipeline);
- }
-
- monitor->dirty = FALSE;
- }
-
- if (texture_area)
- set_texture_area_from_monitor_area (&geometry, texture_area);
-
- if (wrap_mode)
- *wrap_mode = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
- return monitor->texture;
-}
-
-MetaBackground *
-meta_background_new (MetaDisplay *display)
-{
- return g_object_new (META_TYPE_BACKGROUND,
- "meta-display", display,
- NULL);
-}
-
-void
-meta_background_set_color (MetaBackground *self,
- ClutterColor *color)
-{
- ClutterColor dummy = { 0 };
-
- g_return_if_fail (META_IS_BACKGROUND (self));
- g_return_if_fail (color != NULL);
-
- meta_background_set_gradient (self,
- C_DESKTOP_BACKGROUND_SHADING_SOLID,
- color, &dummy);
-}
-
-void
-meta_background_set_gradient (MetaBackground *self,
- CDesktopBackgroundShading shading_direction,
- ClutterColor *color,
- ClutterColor *second_color)
-{
- g_return_if_fail (META_IS_BACKGROUND (self));
- g_return_if_fail (color != NULL);
- g_return_if_fail (second_color != NULL);
-
- self->shading_direction = shading_direction;
- self->color = *color;
- self->second_color = *second_color;
-
- free_color_texture (self);
- free_wallpaper_texture (self);
- mark_changed (self);
-}
-
-/**
- * meta_background_set_file:
- * @self: a #MetaBackground
- * @file: (nullable): a #GFile representing the background file
- * @style: the background style to apply
- *
- * Set the background to @file
- */
-void
-meta_background_set_file (MetaBackground *self,
- GFile *file,
- CDesktopBackgroundStyle style)
-{
- g_return_if_fail (META_IS_BACKGROUND (self));
-
- meta_background_set_blend (self, file, NULL, 0.0, style);
-}
-
-void
-meta_background_set_blend (MetaBackground *self,
- GFile *file1,
- GFile *file2,
- double blend_factor,
- CDesktopBackgroundStyle style)
-{
- g_return_if_fail (META_IS_BACKGROUND (self));
- g_return_if_fail (blend_factor >= 0.0 && blend_factor <= 1.0);
-
- set_file (self, &self->file1, &self->background_image1, file1, FALSE);
- set_file (self, &self->file2, &self->background_image2, file2, FALSE);
-
- self->blend_factor = blend_factor;
- self->style = style;
-
- free_wallpaper_texture (self);
- mark_changed (self);
-}
-
-void
-meta_background_refresh_all (void)
-{
- GSList *l;
-
- for (l = all_backgrounds; l; l = l->next)
- mark_changed (l->data);
-}
diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c
index 6cc3a6795..5ee141ad1 100644
--- a/src/compositor/meta-surface-actor.c
+++ b/src/compositor/meta-surface-actor.c
@@ -94,7 +94,7 @@ get_scaled_region (MetaSurfaceActor *surface_actor,
float x, y;
window_actor = meta_window_actor_from_actor (CLUTTER_ACTOR (surface_actor));
- geometry_scale = meta_window_actor_get_geometry_scale (window_actor);
+ geometry_scale = window_actor ? meta_window_actor_get_geometry_scale (window_actor) : 1;
clutter_actor_get_position (CLUTTER_ACTOR (surface_actor), &x, &y);
cairo_region_translate (region, x, y);
@@ -331,7 +331,7 @@ meta_surface_actor_is_untransformed (MetaCullable *cullable)
clutter_actor_get_abs_allocation_vertices (actor, verts);
window_actor = meta_window_actor_from_actor (actor);
- geometry_scale = meta_window_actor_get_geometry_scale (window_actor);
+ geometry_scale = window_actor ? meta_window_actor_get_geometry_scale (window_actor) : 1;
return meta_actor_vertices_are_untransformed (verts,
width * geometry_scale,
diff --git a/src/compositor/plugins/default.c b/src/compositor/plugins/default.c
index a8f005b9c..cfb3f9a37 100644
--- a/src/compositor/plugins/default.c
+++ b/src/compositor/plugins/default.c
@@ -28,9 +28,8 @@
#include
#include "clutter/clutter.h"
+#include "meta/compositor-muffin.h"
#include "meta/meta-backend.h"
-#include "meta/meta-background-actor.h"
-#include "meta/meta-background-group.h"
#include "meta/meta-monitor-manager.h"
#include "meta/meta-plugin.h"
#include "meta/util.h"
@@ -323,9 +322,7 @@ on_monitors_changed (MetaMonitorManager *monitor_manager,
{
MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin);
MetaDisplay *display = meta_plugin_get_display (plugin);
-
int i, n;
- GRand *rand = g_rand_new_with_seed (123456);
clutter_actor_destroy_all_children (self->priv->background_group);
@@ -334,40 +331,15 @@ on_monitors_changed (MetaMonitorManager *monitor_manager,
{
MetaRectangle rect;
ClutterActor *background_actor;
- MetaBackground *background;
- ClutterColor color;
-
- meta_display_get_monitor_geometry (display, i, &rect);
- background_actor = meta_background_actor_new (display, i);
+ background_actor = meta_create_background_for_monitor (display, i);
+ meta_display_get_monitor_geometry (display, i, &rect);
clutter_actor_set_position (background_actor, rect.x, rect.y);
clutter_actor_set_size (background_actor, rect.width, rect.height);
- /* Don't use rand() here, mesa calls srand() internally when
- parsing the driconf XML, but it's nice if the colors are
- reproducible.
- */
- clutter_color_init (&color,
- g_rand_int_range (rand, 0, 255),
- g_rand_int_range (rand, 0, 255),
- g_rand_int_range (rand, 0, 255),
- 255);
-
- background = meta_background_new (display);
- meta_background_set_color (background, &color);
- meta_background_actor_set_background (META_BACKGROUND_ACTOR (background_actor), background);
- g_object_unref (background);
-
- meta_background_actor_set_vignette (META_BACKGROUND_ACTOR (background_actor),
- TRUE,
- 0.5,
- 0.5);
-
clutter_actor_add_child (self->priv->background_group, background_actor);
}
-
- g_rand_free (rand);
}
static void
@@ -438,7 +410,7 @@ start (MetaPlugin *plugin)
MetaDisplay *display = meta_plugin_get_display (plugin);
MetaMonitorManager *monitor_manager = meta_monitor_manager_get ();
- self->priv->background_group = meta_background_group_new ();
+ self->priv->background_group = clutter_actor_new ();
clutter_actor_insert_child_below (meta_get_window_group_for_display (display),
self->priv->background_group, NULL);
diff --git a/src/core/constraints.c b/src/core/constraints.c
index 420d878fe..6a9dbc2b5 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -853,7 +853,7 @@ constrain_custom_rule (MetaWindow *window,
return TRUE;
parent = meta_window_get_transient_for (window);
- if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED)
+ if (parent && window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED)
{
placement_rule->parent_rect.x = parent->rect.x;
placement_rule->parent_rect.y = parent->rect.y;
@@ -880,8 +880,8 @@ constrain_custom_rule (MetaWindow *window,
case META_PLACEMENT_STATE_CONSTRAINED_FINISHED:
case META_PLACEMENT_STATE_INVALIDATED:
temporary_rect = (MetaRectangle) {
- .x = parent->rect.x + window->placement.current.rel_x,
- .y = parent->rect.y + window->placement.current.rel_y,
+ .x = (parent ? parent->rect.x : parent_x) + window->placement.current.rel_x,
+ .y = (parent ? parent->rect.y : parent_y) + window->placement.current.rel_y,
.width = info->current.width,
.height = info->current.height,
};
diff --git a/src/core/window.c b/src/core/window.c
index 4cfc428cf..75d9761fe 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -4518,6 +4518,25 @@ meta_window_move_resize_internal (MetaWindow *window,
constrained_rect = unconstrained_rect;
temporary_rect = window->rect;
+
+ /* If the window doesn't have a monitor yet but has a placement rule
+ * (e.g., for popups from layer-shell surfaces), try to determine the
+ * monitor from the placement rule's parent rect so constraints can be applied. */
+ if (!window->monitor && window->placement.rule)
+ {
+ MetaBackend *backend = meta_get_backend ();
+ MetaMonitorManager *monitor_manager =
+ meta_backend_get_monitor_manager (backend);
+ MetaLogicalMonitor *logical_monitor;
+ MetaRectangle *parent_rect = &window->placement.rule->parent_rect;
+
+ logical_monitor =
+ meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager,
+ parent_rect);
+ if (logical_monitor)
+ window->monitor = logical_monitor;
+ }
+
if (flags & (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION) &&
!(flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE) &&
window->monitor)
diff --git a/src/core/workspace-private.h b/src/core/workspace-private.h
index a58b2347d..606b6e8c7 100644
--- a/src/core/workspace-private.h
+++ b/src/core/workspace-private.h
@@ -62,6 +62,7 @@ struct _MetaWorkspace
GList *screen_edges;
GList *monitor_edges;
GSList *builtin_struts;
+ GSList *layer_shell_struts;
GSList *all_struts;
guint work_areas_invalid : 1;
@@ -88,6 +89,14 @@ void meta_workspace_get_work_area_for_logical_monitor (MetaWorkspace *works
void meta_workspace_invalidate_work_area (MetaWorkspace *workspace);
+void meta_workspace_set_layer_shell_struts (MetaWorkspace *workspace,
+ GSList *struts);
+
+void meta_workspace_get_work_area_for_logical_monitor_excluding_layer_shell (
+ MetaWorkspace *workspace,
+ MetaLogicalMonitor *logical_monitor,
+ MetaRectangle *area);
+
GList* meta_workspace_get_onscreen_region (MetaWorkspace *workspace);
GList * meta_workspace_get_onmonitor_region (MetaWorkspace *workspace,
MetaLogicalMonitor *logical_monitor);
diff --git a/src/core/workspace.c b/src/core/workspace.c
index ffea1ab8f..371ccc3de 100644
--- a/src/core/workspace.c
+++ b/src/core/workspace.c
@@ -302,6 +302,22 @@ workspace_free_builtin_struts (MetaWorkspace *workspace)
workspace->builtin_struts = NULL;
}
+/**
+ * workspace_free_layer_shell_struts:
+ * @workspace: The workspace.
+ *
+ * Frees the struts list set with meta_workspace_set_layer_shell_struts
+ */
+static void
+workspace_free_layer_shell_struts (MetaWorkspace *workspace)
+{
+ if (workspace->layer_shell_struts == NULL)
+ return;
+
+ g_slist_free_full (workspace->layer_shell_struts, g_free);
+ workspace->layer_shell_struts = NULL;
+}
+
/* Ensure that the workspace is empty by making sure that
* all of our windows are on-all-workspaces. */
static void
@@ -333,6 +349,7 @@ meta_workspace_remove (MetaWorkspace *workspace)
g_list_free (workspace->list_containing_self);
workspace_free_builtin_struts (workspace);
+ workspace_free_layer_shell_struts (workspace);
/* screen.c:update_num_workspaces(), which calls us, removes windows from
* workspaces first, which can cause the workareas on the workspace to be
@@ -899,6 +916,16 @@ ensure_work_areas_validated (MetaWorkspace *workspace)
workspace->all_struts = copy_strut_list (workspace->builtin_struts);
+ /* Add layer-shell struts */
+ {
+ GSList *s_iter;
+ for (s_iter = workspace->layer_shell_struts; s_iter != NULL; s_iter = s_iter->next)
+ {
+ workspace->all_struts = g_slist_prepend (workspace->all_struts,
+ copy_strut (s_iter->data));
+ }
+ }
+
windows = meta_workspace_list_windows (workspace);
for (tmp = windows; tmp != NULL; tmp = tmp->next)
{
@@ -1161,6 +1188,28 @@ meta_workspace_set_builtin_struts (MetaWorkspace *workspace,
meta_workspace_invalidate_work_area (workspace);
}
+/**
+ * meta_workspace_set_layer_shell_struts:
+ * @workspace: a #MetaWorkspace
+ * @struts: (element-type Meta.Strut) (transfer none): list of #MetaStrut
+ *
+ * Sets a list of struts from layer-shell surfaces that will be used
+ * in addition to the builtin struts and window struts when computing
+ * the work area of the workspace.
+ */
+void
+meta_workspace_set_layer_shell_struts (MetaWorkspace *workspace,
+ GSList *struts)
+{
+ if (strut_lists_equal (struts, workspace->layer_shell_struts))
+ return;
+
+ workspace_free_layer_shell_struts (workspace);
+ workspace->layer_shell_struts = copy_strut_list (struts);
+
+ meta_workspace_invalidate_work_area (workspace);
+}
+
void
meta_workspace_get_work_area_for_logical_monitor (MetaWorkspace *workspace,
MetaLogicalMonitor *logical_monitor,
@@ -1171,6 +1220,56 @@ meta_workspace_get_work_area_for_logical_monitor (MetaWorkspace *workspace,
area);
}
+/**
+ * meta_workspace_get_work_area_for_logical_monitor_excluding_layer_shell:
+ * @workspace: a #MetaWorkspace
+ * @logical_monitor: a #MetaLogicalMonitor
+ * @area: (out): location to store the work area
+ *
+ * Computes work area for @logical_monitor using only builtin struts,
+ * excluding layer-shell struts. This is used for positioning layer-shell
+ * surfaces so they don't get pushed by their own struts.
+ */
+void
+meta_workspace_get_work_area_for_logical_monitor_excluding_layer_shell (
+ MetaWorkspace *workspace,
+ MetaLogicalMonitor *logical_monitor,
+ MetaRectangle *area)
+{
+ GList *tmp;
+ GList *monitor_region;
+ GSList *struts_for_calculation = NULL;
+ GSList *s_iter;
+
+ g_return_if_fail (logical_monitor != NULL);
+ g_return_if_fail (area != NULL);
+
+ /* Start with builtin struts only */
+ for (s_iter = workspace->builtin_struts; s_iter != NULL; s_iter = s_iter->next)
+ {
+ MetaStrut *strut = s_iter->data;
+ MetaStrut *copy = g_new0 (MetaStrut, 1);
+ *copy = *strut;
+ struts_for_calculation = g_slist_prepend (struts_for_calculation, copy);
+ }
+
+ /* Calculate region for this monitor using only builtin struts */
+ monitor_region =
+ meta_rectangle_get_minimal_spanning_set_for_region (&logical_monitor->rect,
+ struts_for_calculation);
+
+ /* Find work area from the region */
+ *area = logical_monitor->rect;
+ for (tmp = monitor_region; tmp != NULL; tmp = tmp->next)
+ {
+ MetaRectangle *rect = tmp->data;
+ meta_rectangle_intersect (area, rect, area);
+ }
+
+ g_list_free_full (monitor_region, g_free);
+ g_slist_free_full (struts_for_calculation, g_free);
+}
+
/**
* meta_workspace_get_work_area_for_monitor:
* @workspace: a #MetaWorkspace
diff --git a/src/meson.build b/src/meson.build
index 2ea93bd97..50c59f9d4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -284,12 +284,6 @@ muffin_sources = [
'compositor/cogl-utils.h',
'compositor/compositor.c',
'compositor/compositor-private.h',
- 'compositor/meta-background-actor.c',
- 'compositor/meta-background-actor-private.h',
- 'compositor/meta-background.c',
- 'compositor/meta-background-group.c',
- 'compositor/meta-background-image.c',
- 'compositor/meta-background-private.h',
'compositor/meta-compositor-server.c',
'compositor/meta-compositor-server.h',
'compositor/meta-compositor-x11.c',
@@ -521,6 +515,9 @@ if have_wayland
'wayland/meta-wayland-dnd-surface.h',
'wayland/meta-wayland-gtk-shell.c',
'wayland/meta-wayland-gtk-shell.h',
+ 'wayland/meta-wayland-background-actor.c',
+ 'wayland/meta-wayland-layer-shell.c',
+ 'wayland/meta-wayland-layer-shell.h',
'wayland/meta-wayland.h',
'wayland/meta-wayland-idle-inhibit.c',
'wayland/meta-wayland-idle-inhibit.h',
@@ -811,6 +808,7 @@ if have_wayland
wayland_protocols = [
['cursor-shape-v1', 'private', ],
['gtk-shell', 'private', ],
+ ['wlr-layer-shell-unstable-v1', 'private', ],
['idle-inhibit', 'unstable', 'v1', ],
['keyboard-shortcuts-inhibit', 'unstable', 'v1', ],
['linux-dmabuf', 'unstable', 'v1', ],
diff --git a/src/meta/compositor-muffin.h b/src/meta/compositor-muffin.h
index a4555a74d..2ef860aa3 100644
--- a/src/meta/compositor-muffin.h
+++ b/src/meta/compositor-muffin.h
@@ -69,4 +69,11 @@ ClutterActor *meta_get_desklet_container_for_display (MetaDisplay *display);
META_EXPORT
gulong meta_get_stage_xwindow (MetaDisplay *display);
+META_EXPORT
+ClutterActor *meta_create_background_for_monitor (MetaDisplay *display,
+ int monitor);
+
+META_EXPORT
+GList *meta_get_background_actors_for_display (MetaDisplay *display);
+
#endif
diff --git a/src/meta/meson.build b/src/meta/meson.build
index d895e88dc..c89223b44 100644
--- a/src/meta/meson.build
+++ b/src/meta/meson.build
@@ -9,10 +9,6 @@ muffin_public_headers = [
'keybindings.h',
'main.h',
'meta-backend.h',
- 'meta-background.h',
- 'meta-background-actor.h',
- 'meta-background-group.h',
- 'meta-background-image.h',
'meta-close-dialog.h',
'meta-cursor-tracker.h',
'meta-dnd.h',
@@ -52,6 +48,12 @@ if have_x11
]
endif
+if have_wayland
+ muffin_public_headers += [
+ 'meta-wayland-background-actor.h',
+ ]
+endif
+
install_headers(muffin_public_headers,
subdir: muffin_includesubdir
)
diff --git a/src/meta/meta-background-actor.h b/src/meta/meta-background-actor.h
deleted file mode 100644
index 93a05a8de..000000000
--- a/src/meta/meta-background-actor.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * meta-background-actor.h: Actor for painting the root window background
- *
- * Copyright 2010 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 .
- */
-
-#ifndef META_BACKGROUND_ACTOR_H
-#define META_BACKGROUND_ACTOR_H
-
-#include
-
-#include "clutter/clutter.h"
-#include "meta/meta-background.h"
-
-/**
- * MetaBackgroundActor:
- *
- * 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_ACTOR (meta_background_actor_get_type ())
-
-META_EXPORT
-G_DECLARE_FINAL_TYPE (MetaBackgroundActor,
- meta_background_actor,
- META, BACKGROUND_ACTOR,
- ClutterActor)
-
-
-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 */
diff --git a/src/meta/meta-background-group.h b/src/meta/meta-background-group.h
deleted file mode 100644
index b43fcb94a..000000000
--- a/src/meta/meta-background-group.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-#ifndef META_BACKGROUND_GROUP_H
-#define META_BACKGROUND_GROUP_H
-
-#include "clutter/clutter.h"
-
-#include
-
-#define META_TYPE_BACKGROUND_GROUP (meta_background_group_get_type ())
-
-META_EXPORT
-G_DECLARE_DERIVABLE_TYPE (MetaBackgroundGroup,
- meta_background_group,
- META, BACKGROUND_GROUP,
- ClutterActor)
-
-struct _MetaBackgroundGroupClass
-{
- ClutterActorClass parent_class;
-};
-
-META_EXPORT
-ClutterActor *meta_background_group_new (void);
-
-#endif /* META_BACKGROUND_GROUP_H */
diff --git a/src/meta/meta-background-image.h b/src/meta/meta-background-image.h
deleted file mode 100644
index 137a6ff8e..000000000
--- a/src/meta/meta-background-image.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * MetaBackgroundImageCache:
- *
- * Simple cache for background textures loaded from files
- *
- * Copyright 2014 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 .
- */
-
-#ifndef __META_BACKGROUND_IMAGE_H__
-#define __META_BACKGROUND_IMAGE_H__
-
-#include
-#include
-
-#include "cogl/cogl.h"
-#include "meta/display.h"
-
-#define META_TYPE_BACKGROUND_IMAGE (meta_background_image_get_type ())
-
-META_EXPORT
-G_DECLARE_FINAL_TYPE (MetaBackgroundImage,
- meta_background_image,
- META, BACKGROUND_IMAGE,
- GObject)
-
-META_EXPORT
-gboolean meta_background_image_is_loaded (MetaBackgroundImage *image);
-
-META_EXPORT
-gboolean meta_background_image_get_success (MetaBackgroundImage *image);
-
-META_EXPORT
-CoglTexture *meta_background_image_get_texture (MetaBackgroundImage *image);
-
-
-#define META_TYPE_BACKGROUND_IMAGE_CACHE (meta_background_image_cache_get_type ())
-
-META_EXPORT
-G_DECLARE_FINAL_TYPE (MetaBackgroundImageCache,
- meta_background_image_cache,
- META, BACKGROUND_IMAGE_CACHE,
- GObject)
-
-META_EXPORT
-MetaBackgroundImageCache *meta_background_image_cache_get_default (void);
-
-META_EXPORT
-MetaBackgroundImage *meta_background_image_cache_load (MetaBackgroundImageCache *cache,
- GFile *file);
-
-META_EXPORT
-void meta_background_image_cache_purge (MetaBackgroundImageCache *cache,
- GFile *file);
-
-#endif /* __META_BACKGROUND_IMAGE_H__ */
diff --git a/src/meta/meta-background.h b/src/meta/meta-background.h
deleted file mode 100644
index fcec8b20d..000000000
--- a/src/meta/meta-background.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * meta-background-actor.h: for painting the root window background
- *
- * Copyright 2010 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 .
- */
-
-#ifndef META_BACKGROUND_H
-#define META_BACKGROUND_H
-
-#include
-
-#include "clutter/clutter.h"
-#include "meta/display.h"
-
-/**
- * MetaBackground:
- *
- * 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 (meta_background_get_type ())
-
-META_EXPORT
-G_DECLARE_FINAL_TYPE (MetaBackground,
- meta_background,
- META, BACKGROUND,
- GObject)
-
-
-META_EXPORT
-void meta_background_refresh_all (void);
-
-META_EXPORT
-MetaBackground *meta_background_new (MetaDisplay *display);
-
-META_EXPORT
-void meta_background_set_color (MetaBackground *self,
- ClutterColor *color);
-
-META_EXPORT
-void meta_background_set_gradient (MetaBackground *self,
- CDesktopBackgroundShading shading_direction,
- ClutterColor *color,
- ClutterColor *second_color);
-
-META_EXPORT
-void meta_background_set_file (MetaBackground *self,
- GFile *file,
- CDesktopBackgroundStyle style);
-
-META_EXPORT
-void meta_background_set_blend (MetaBackground *self,
- GFile *file1,
- GFile *file2,
- double blend_factor,
- CDesktopBackgroundStyle style);
-
-#endif /* META_BACKGROUND_H */
diff --git a/src/meta/meta-wayland-background-actor.h b/src/meta/meta-wayland-background-actor.h
new file mode 100644
index 000000000..962da80c8
--- /dev/null
+++ b/src/meta/meta-wayland-background-actor.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 Linux Mint
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_WAYLAND_BACKGROUND_ACTOR_H
+#define META_WAYLAND_BACKGROUND_ACTOR_H
+
+#include
+#include
+
+#define META_TYPE_WAYLAND_BACKGROUND_ACTOR (meta_wayland_background_actor_get_type ())
+
+META_EXPORT
+G_DECLARE_FINAL_TYPE (MetaWaylandBackgroundActor,
+ meta_wayland_background_actor,
+ META, WAYLAND_BACKGROUND_ACTOR,
+ ClutterActor)
+
+META_EXPORT
+ClutterActor * meta_wayland_background_actor_new_for_monitor (MetaDisplay *display,
+ int monitor);
+
+#endif /* META_WAYLAND_BACKGROUND_ACTOR_H */
diff --git a/src/meta/meta-x11-background-actor.h b/src/meta/meta-x11-background-actor.h
index e9d175148..9f91eb9d2 100644
--- a/src/meta/meta-x11-background-actor.h
+++ b/src/meta/meta-x11-background-actor.h
@@ -46,4 +46,8 @@ G_DECLARE_FINAL_TYPE (MetaX11BackgroundActor,
META_EXPORT
ClutterActor *meta_x11_background_actor_new_for_display (MetaDisplay *display);
+META_EXPORT
+ClutterActor *meta_x11_background_actor_new_for_monitor (MetaDisplay *display,
+ int monitor);
+
#endif /* META_X11_BACKGROUND_ACTOR_H */
diff --git a/src/wayland/meta-wayland-background-actor.c b/src/wayland/meta-wayland-background-actor.c
new file mode 100644
index 000000000..6e5e88bfd
--- /dev/null
+++ b/src/wayland/meta-wayland-background-actor.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2024 Linux Mint
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "meta/meta-wayland-background-actor.h"
+
+#include "backends/meta-backend-private.h"
+#include "backends/meta-logical-monitor.h"
+#include "backends/meta-monitor-manager-private.h"
+#include "compositor/meta-surface-actor-wayland.h"
+#include "meta/compositor-muffin.h"
+#include "wayland/meta-wayland-layer-shell.h"
+#include "wayland/meta-wayland-outputs.h"
+#include "wayland/meta-wayland-private.h"
+
+struct _MetaWaylandBackgroundActor
+{
+ ClutterActor parent_instance;
+
+ MetaDisplay *display;
+ int monitor_index;
+ float dim_factor;
+
+ ClutterActor *clone;
+ MetaWaylandLayerSurface *tracked_surface;
+ MetaWaylandLayerShell *layer_shell;
+ gulong mapped_handler_id;
+ gulong unmapped_handler_id;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DIM_FACTOR,
+ PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST];
+
+G_DEFINE_TYPE (MetaWaylandBackgroundActor, meta_wayland_background_actor, CLUTTER_TYPE_ACTOR)
+
+static void
+meta_wayland_background_actor_remove_clone (MetaWaylandBackgroundActor *self)
+{
+ if (self->clone)
+ {
+ clutter_actor_remove_child (CLUTTER_ACTOR (self), self->clone);
+ self->clone = NULL;
+ }
+
+ self->tracked_surface = NULL;
+}
+
+static void
+meta_wayland_background_actor_attach_surface (MetaWaylandBackgroundActor *self,
+ MetaWaylandLayerSurface *layer_surface)
+{
+ MetaWaylandActorSurface *actor_surface;
+ MetaSurfaceActor *surface_actor;
+
+ meta_wayland_background_actor_remove_clone (self);
+
+ actor_surface = META_WAYLAND_ACTOR_SURFACE (layer_surface);
+ surface_actor = meta_wayland_actor_surface_get_actor (actor_surface);
+
+ if (!surface_actor)
+ return;
+
+ self->tracked_surface = layer_surface;
+ self->clone = clutter_clone_new (CLUTTER_ACTOR (surface_actor));
+ clutter_actor_add_child (CLUTTER_ACTOR (self), self->clone);
+}
+
+static MetaWaylandOutput *
+get_output_for_monitor (MetaWaylandBackgroundActor *self)
+{
+ MetaWaylandCompositor *compositor;
+
+ compositor = meta_wayland_compositor_get_default ();
+ return meta_wayland_compositor_get_output_for_monitor (compositor,
+ self->monitor_index);
+}
+
+static void
+on_layer_surface_mapped (MetaWaylandLayerShell *layer_shell,
+ MetaWaylandLayerSurface *layer_surface,
+ gpointer user_data)
+{
+ MetaWaylandBackgroundActor *self = user_data;
+ MetaWaylandOutput *output;
+
+ if (self->tracked_surface != NULL)
+ return;
+
+ if (meta_wayland_layer_surface_get_layer (layer_surface) !=
+ META_LAYER_SHELL_LAYER_BACKGROUND)
+ return;
+
+ output = get_output_for_monitor (self);
+ if (output && meta_wayland_layer_surface_get_output (layer_surface) != output)
+ return;
+
+ meta_wayland_background_actor_attach_surface (self, layer_surface);
+}
+
+static void
+on_layer_surface_unmapped (MetaWaylandLayerShell *layer_shell,
+ MetaWaylandLayerSurface *layer_surface,
+ gpointer user_data)
+{
+ MetaWaylandBackgroundActor *self = user_data;
+
+ if (self->tracked_surface != layer_surface)
+ return;
+
+ meta_wayland_background_actor_remove_clone (self);
+}
+
+static void
+meta_wayland_background_actor_disconnect_signals (MetaWaylandBackgroundActor *self)
+{
+ if (self->layer_shell)
+ {
+ if (self->mapped_handler_id != 0)
+ {
+ g_signal_handler_disconnect (self->layer_shell, self->mapped_handler_id);
+ self->mapped_handler_id = 0;
+ }
+
+ if (self->unmapped_handler_id != 0)
+ {
+ g_signal_handler_disconnect (self->layer_shell, self->unmapped_handler_id);
+ self->unmapped_handler_id = 0;
+ }
+
+ self->layer_shell = NULL;
+ }
+}
+
+static void
+meta_wayland_background_actor_dispose (GObject *object)
+{
+ MetaWaylandBackgroundActor *self = META_WAYLAND_BACKGROUND_ACTOR (object);
+
+ meta_wayland_background_actor_disconnect_signals (self);
+ meta_wayland_background_actor_remove_clone (self);
+
+ G_OBJECT_CLASS (meta_wayland_background_actor_parent_class)->dispose (object);
+}
+
+static gboolean
+get_monitor_rect (MetaWaylandBackgroundActor *self,
+ MetaRectangle *rect)
+{
+ MetaBackend *backend = meta_get_backend ();
+ MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend);
+ MetaLogicalMonitor *logical_monitor;
+
+ logical_monitor = meta_monitor_manager_get_logical_monitor_from_number (
+ monitor_manager, self->monitor_index);
+
+ if (!logical_monitor)
+ return FALSE;
+
+ *rect = logical_monitor->rect;
+ return TRUE;
+}
+
+static void
+meta_wayland_background_actor_get_preferred_width (ClutterActor *actor,
+ gfloat for_height,
+ gfloat *min_width_p,
+ gfloat *natural_width_p)
+{
+ MetaRectangle rect;
+ float width = 0;
+
+ if (get_monitor_rect (META_WAYLAND_BACKGROUND_ACTOR (actor), &rect))
+ width = rect.width;
+
+ if (min_width_p)
+ *min_width_p = width;
+ if (natural_width_p)
+ *natural_width_p = width;
+}
+
+static void
+meta_wayland_background_actor_get_preferred_height (ClutterActor *actor,
+ gfloat for_width,
+ gfloat *min_height_p,
+ gfloat *natural_height_p)
+{
+ MetaRectangle rect;
+ float height = 0;
+
+ if (get_monitor_rect (META_WAYLAND_BACKGROUND_ACTOR (actor), &rect))
+ height = rect.height;
+
+ if (min_height_p)
+ *min_height_p = height;
+ if (natural_height_p)
+ *natural_height_p = height;
+}
+
+static gboolean
+meta_wayland_background_actor_get_paint_volume (ClutterActor *actor,
+ ClutterPaintVolume *volume)
+{
+ MetaRectangle rect;
+
+ if (!get_monitor_rect (META_WAYLAND_BACKGROUND_ACTOR (actor), &rect))
+ return FALSE;
+
+ clutter_paint_volume_set_width (volume, rect.width);
+ clutter_paint_volume_set_height (volume, rect.height);
+
+ return TRUE;
+}
+
+static void
+meta_wayland_background_actor_set_dim_factor (MetaWaylandBackgroundActor *self,
+ float dim_factor)
+{
+ if (self->dim_factor == dim_factor)
+ return;
+
+ self->dim_factor = dim_factor;
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DIM_FACTOR]);
+}
+
+static void
+meta_wayland_background_actor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MetaWaylandBackgroundActor *self = META_WAYLAND_BACKGROUND_ACTOR (object);
+
+ switch (prop_id)
+ {
+ case PROP_DIM_FACTOR:
+ g_value_set_float (value, self->dim_factor);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_wayland_background_actor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaWaylandBackgroundActor *self = META_WAYLAND_BACKGROUND_ACTOR (object);
+
+ switch (prop_id)
+ {
+ case PROP_DIM_FACTOR:
+ meta_wayland_background_actor_set_dim_factor (self, g_value_get_float (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_wayland_background_actor_class_init (MetaWaylandBackgroundActorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ object_class->dispose = meta_wayland_background_actor_dispose;
+ object_class->get_property = meta_wayland_background_actor_get_property;
+ object_class->set_property = meta_wayland_background_actor_set_property;
+
+ actor_class->get_preferred_width = meta_wayland_background_actor_get_preferred_width;
+ actor_class->get_preferred_height = meta_wayland_background_actor_get_preferred_height;
+ actor_class->get_paint_volume = meta_wayland_background_actor_get_paint_volume;
+
+ obj_props[PROP_DIM_FACTOR] =
+ g_param_spec_float ("dim-factor",
+ "Dim factor",
+ "Factor to dim the background by",
+ 0.0, 1.0, 1.0,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, PROP_LAST, obj_props);
+}
+
+static void
+meta_wayland_background_actor_init (MetaWaylandBackgroundActor *self)
+{
+ self->dim_factor = 1.0;
+}
+
+/**
+ * meta_wayland_background_actor_new_for_monitor:
+ * @display: a #MetaDisplay
+ * @monitor: the monitor index
+ *
+ * Creates a new actor that clones the layer-shell background surface
+ * for the given monitor. The background is provided by cinnamon-settings-daemon
+ * as a layer-shell surface on the BACKGROUND layer with namespace "desktop".
+ *
+ * Return value: (transfer full): the newly created background actor
+ */
+ClutterActor *
+meta_wayland_background_actor_new_for_monitor (MetaDisplay *display,
+ int monitor)
+{
+ MetaWaylandBackgroundActor *self;
+ MetaWaylandCompositor *compositor;
+ MetaWaylandLayerShell *layer_shell;
+ MetaWaylandOutput *output;
+ MetaWaylandLayerSurface *surface;
+
+ g_return_val_if_fail (META_IS_DISPLAY (display), NULL);
+ g_return_val_if_fail (meta_is_wayland_compositor (), NULL);
+
+ self = g_object_ref_sink (g_object_new (META_TYPE_WAYLAND_BACKGROUND_ACTOR, NULL));
+ self->display = display;
+ self->monitor_index = monitor;
+
+ compositor = meta_wayland_compositor_get_default ();
+ layer_shell = meta_wayland_compositor_get_layer_shell (compositor);
+
+ if (!layer_shell)
+ return CLUTTER_ACTOR (self);
+
+ self->layer_shell = layer_shell;
+
+ output = meta_wayland_compositor_get_output_for_monitor (compositor, monitor);
+
+ surface = meta_wayland_layer_shell_find_surface (layer_shell,
+ META_LAYER_SHELL_LAYER_BACKGROUND,
+ NULL,
+ output);
+
+ if (surface)
+ meta_wayland_background_actor_attach_surface (self, surface);
+
+ self->mapped_handler_id =
+ g_signal_connect (layer_shell, "layer-surface-mapped",
+ G_CALLBACK (on_layer_surface_mapped), self);
+
+ self->unmapped_handler_id =
+ g_signal_connect (layer_shell, "layer-surface-unmapped",
+ G_CALLBACK (on_layer_surface_unmapped), self);
+
+ return CLUTTER_ACTOR (self);
+}
diff --git a/src/wayland/meta-wayland-layer-shell.c b/src/wayland/meta-wayland-layer-shell.c
new file mode 100644
index 000000000..5e97a340b
--- /dev/null
+++ b/src/wayland/meta-wayland-layer-shell.c
@@ -0,0 +1,1419 @@
+/*
+ * Copyright (C) 2024 Linux Mint
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "wayland/meta-wayland-layer-shell.h"
+
+#include "backends/meta-logical-monitor.h"
+#include "compositor/compositor-private.h"
+#include "compositor/meta-surface-actor-wayland.h"
+#include "core/meta-workspace-manager-private.h"
+#include "core/workspace-private.h"
+#include "meta/boxes.h"
+#include "wayland/meta-wayland-data-device.h"
+#include "wayland/meta-wayland-outputs.h"
+#include "wayland/meta-wayland-private.h"
+#include "wayland/meta-wayland-surface.h"
+#include "wayland/meta-wayland-versions.h"
+#include "wayland/meta-wayland-xdg-shell.h"
+
+#include "wlr-layer-shell-unstable-v1-server-protocol.h"
+
+/* Layer shell global */
+struct _MetaWaylandLayerShell
+{
+ GObject parent;
+ GList *shell_resources;
+ GList *layer_surfaces;
+ MetaWaylandCompositor *compositor;
+ gulong workareas_changed_handler_id;
+};
+
+enum
+{
+ LAYER_SHELL_SIGNAL_LAYER_SURFACE_MAPPED,
+ LAYER_SHELL_SIGNAL_LAYER_SURFACE_UNMAPPED,
+ LAYER_SHELL_N_SIGNALS
+};
+
+static guint layer_shell_signals[LAYER_SHELL_N_SIGNALS];
+
+G_DEFINE_TYPE (MetaWaylandLayerShell, meta_wayland_layer_shell, G_TYPE_OBJECT)
+
+/* Layer surface state (pending/current) */
+typedef struct
+{
+ uint32_t anchor;
+ int32_t exclusive_zone;
+ struct {
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+ int32_t left;
+ } margin;
+ uint32_t desired_width;
+ uint32_t desired_height;
+ MetaLayerShellLayer layer;
+ uint32_t keyboard_interactivity;
+} MetaWaylandLayerSurfaceState;
+
+/* Layer surface */
+struct _MetaWaylandLayerSurface
+{
+ MetaWaylandActorSurface parent;
+
+ struct wl_resource *resource;
+ MetaWaylandOutput *output;
+ char *namespace;
+ MetaLayerShellLayer initial_layer;
+
+ MetaWaylandLayerSurfaceState current;
+ MetaWaylandLayerSurfaceState pending;
+
+ uint32_t configure_serial;
+ gboolean configured;
+ gboolean mapped;
+};
+
+G_DEFINE_TYPE (MetaWaylandLayerSurface, meta_wayland_layer_surface,
+ META_TYPE_WAYLAND_ACTOR_SURFACE)
+
+enum
+{
+ PROP_0,
+ PROP_OUTPUT,
+ PROP_NAMESPACE,
+ PROP_INITIAL_LAYER,
+ N_PROPS
+};
+
+static GParamSpec *layer_surface_props[N_PROPS] = { NULL, };
+
+static void meta_wayland_layer_surface_send_configure (MetaWaylandLayerSurface *layer_surface);
+
+static MetaWaylandLayerShell *
+meta_wayland_layer_shell_from_compositor (MetaWaylandCompositor *compositor)
+{
+ return g_object_get_data (G_OBJECT (compositor), "-meta-wayland-layer-shell");
+}
+
+static void
+on_workareas_changed (MetaDisplay *display,
+ MetaWaylandLayerShell *layer_shell)
+{
+ meta_wayland_layer_shell_on_workarea_changed (layer_shell->compositor);
+}
+
+static void
+meta_wayland_layer_shell_ensure_signal_connected (MetaWaylandLayerShell *layer_shell)
+{
+ MetaDisplay *display;
+
+ if (layer_shell->workareas_changed_handler_id != 0)
+ return;
+
+ display = meta_get_display ();
+ if (!display)
+ return;
+
+ layer_shell->workareas_changed_handler_id =
+ g_signal_connect (display, "workareas-changed",
+ G_CALLBACK (on_workareas_changed),
+ layer_shell);
+}
+
+static MetaSide
+get_strut_side_from_anchor (uint32_t anchor)
+{
+ gboolean anchored_top = (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP);
+ gboolean anchored_bottom = (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
+ gboolean anchored_left = (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
+ gboolean anchored_right = (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
+
+ if (anchored_top && !anchored_bottom)
+ return META_SIDE_TOP;
+ else if (anchored_bottom && !anchored_top)
+ return META_SIDE_BOTTOM;
+ else if (anchored_left && !anchored_right)
+ return META_SIDE_LEFT;
+ else if (anchored_right && !anchored_left)
+ return META_SIDE_RIGHT;
+ else
+ return -1;
+}
+
+/*
+ * Calculate the total exclusive zone offset from OTHER layer surfaces
+ * on the same edge that were created before this surface.
+ *
+ * Surfaces are stored in reverse creation order (newest first), so
+ * surfaces that appear AFTER this one in the list were created earlier
+ * and should be positioned closer to the edge.
+ */
+static int
+get_other_layer_surfaces_exclusive_offset (MetaWaylandLayerSurface *layer_surface,
+ MetaWaylandCompositor *compositor,
+ MetaSide side)
+{
+ MetaWaylandLayerShell *layer_shell;
+ GList *l;
+ int offset = 0;
+ gboolean found_self = FALSE;
+
+ layer_shell = meta_wayland_layer_shell_from_compositor (compositor);
+ if (!layer_shell)
+ return 0;
+
+ /* Iterate through all layer surfaces. Surfaces after this one in the list
+ * were created earlier and are "before" us for stacking purposes. */
+ for (l = layer_shell->layer_surfaces; l; l = l->next)
+ {
+ MetaWaylandLayerSurface *other = l->data;
+
+ if (other == layer_surface)
+ {
+ found_self = TRUE;
+ continue;
+ }
+
+ /* Only count surfaces that come after us (created before us) */
+ if (!found_self)
+ continue;
+
+ /* Only count mapped surfaces with exclusive_zone > 0 on the same edge */
+ if (!other->mapped || other->current.exclusive_zone <= 0)
+ continue;
+
+ MetaSide other_side = get_strut_side_from_anchor (other->current.anchor);
+ if (other_side != side)
+ continue;
+
+ /* Add this surface's exclusive zone (plus its margin on this edge) */
+ switch (side)
+ {
+ case META_SIDE_TOP:
+ offset += other->current.exclusive_zone + other->current.margin.top;
+ break;
+ case META_SIDE_BOTTOM:
+ offset += other->current.exclusive_zone + other->current.margin.bottom;
+ break;
+ case META_SIDE_LEFT:
+ offset += other->current.exclusive_zone + other->current.margin.left;
+ break;
+ case META_SIDE_RIGHT:
+ offset += other->current.exclusive_zone + other->current.margin.right;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return offset;
+}
+
+static gboolean
+get_layer_surface_bounds (MetaWaylandLayerSurface *layer_surface,
+ MetaWaylandLayerSurfaceState *state,
+ MetaRectangle *output_rect,
+ MetaRectangle *usable_area)
+{
+ MetaWaylandSurface *surface;
+ MetaLogicalMonitor *logical_monitor = NULL;
+ MetaRectangle monitor_rect = { 0, 0, 0, 0 };
+
+ surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (layer_surface));
+
+ if (layer_surface->output && layer_surface->output->logical_monitor)
+ {
+ logical_monitor = layer_surface->output->logical_monitor;
+ monitor_rect = logical_monitor->rect;
+ }
+ else
+ {
+ MetaBackend *backend = meta_get_backend ();
+ MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend);
+ MetaLogicalMonitor *primary = meta_monitor_manager_get_primary_logical_monitor (monitor_manager);
+
+ if (primary)
+ {
+ logical_monitor = primary;
+ monitor_rect = primary->rect;
+ }
+ else
+ {
+ monitor_rect.width = 1920;
+ monitor_rect.height = 1080;
+ }
+ }
+
+ *output_rect = monitor_rect;
+
+ if (state->exclusive_zone == -1)
+ {
+ /* Full output, ignore all panels */
+ *usable_area = monitor_rect;
+ }
+ else if (logical_monitor)
+ {
+ MetaDisplay *display = meta_get_display ();
+ MetaWorkspaceManager *workspace_manager;
+ MetaWorkspace *workspace;
+
+ *usable_area = monitor_rect;
+ if (display)
+ {
+ workspace_manager = meta_display_get_workspace_manager (display);
+ if (workspace_manager)
+ {
+ workspace = meta_workspace_manager_get_active_workspace (workspace_manager);
+ if (workspace)
+ {
+ if (state->exclusive_zone > 0)
+ {
+ MetaSide side;
+ int other_surfaces_offset;
+
+ /* For surfaces that claim exclusive space, use workarea
+ * excluding layer-shell struts to avoid circular dependency
+ * (surface's own strut affecting its position). */
+ meta_workspace_get_work_area_for_logical_monitor_excluding_layer_shell (
+ workspace, logical_monitor, usable_area);
+
+ /* Also account for other layer surfaces on the same edge
+ * that were created before this one. */
+ side = get_strut_side_from_anchor (state->anchor);
+ if (side != (MetaSide) -1 && surface && surface->compositor)
+ {
+ other_surfaces_offset =
+ get_other_layer_surfaces_exclusive_offset (layer_surface,
+ surface->compositor,
+ side);
+ switch (side)
+ {
+ case META_SIDE_TOP:
+ usable_area->y += other_surfaces_offset;
+ usable_area->height -= other_surfaces_offset;
+ break;
+ case META_SIDE_BOTTOM:
+ usable_area->height -= other_surfaces_offset;
+ break;
+ case META_SIDE_LEFT:
+ usable_area->x += other_surfaces_offset;
+ usable_area->width -= other_surfaces_offset;
+ break;
+ case META_SIDE_RIGHT:
+ usable_area->width -= other_surfaces_offset;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* For surfaces with exclusive_zone == 0, use full workarea
+ * (they respect all panels including other layer surfaces). */
+ meta_workspace_get_work_area_for_logical_monitor (workspace,
+ logical_monitor,
+ usable_area);
+ }
+ return TRUE;
+ }
+ }
+ }
+ }
+ else
+ {
+ *usable_area = monitor_rect;
+ }
+
+ return TRUE;
+}
+
+static MetaStrut *
+meta_wayland_layer_surface_create_strut (MetaWaylandLayerSurface *layer_surface)
+{
+ MetaWaylandLayerSurfaceState *state = &layer_surface->current;
+ MetaRectangle output_rect = { 0, 0, 0, 0 };
+ MetaRectangle usable_area = { 0, 0, 0, 0 };
+ MetaStrut *strut;
+ MetaSide side;
+ int offset_top, offset_bottom, offset_left, offset_right;
+
+ if (state->exclusive_zone <= 0 || !layer_surface->mapped)
+ return NULL;
+
+ side = get_strut_side_from_anchor (state->anchor);
+ if (side == (MetaSide) -1)
+ return NULL;
+
+ get_layer_surface_bounds (layer_surface, state, &output_rect, &usable_area);
+
+ /* Calculate how much the workarea is offset from output on each edge
+ * (this accounts for Cinnamon panels via builtin_struts). */
+ offset_top = usable_area.y - output_rect.y;
+ offset_bottom = (output_rect.y + output_rect.height) -
+ (usable_area.y + usable_area.height);
+ offset_left = usable_area.x - output_rect.x;
+ offset_right = (output_rect.x + output_rect.width) -
+ (usable_area.x + usable_area.width);
+
+ strut = g_new0 (MetaStrut, 1);
+ strut->side = side;
+
+ /* Create strut from OUTPUT edge, extending to cover both the existing
+ * workarea offset (Cinnamon panels) AND this surface's exclusive zone.
+ * This matches how builtin_struts are processed. */
+ switch (side)
+ {
+ case META_SIDE_TOP:
+ strut->rect.x = output_rect.x;
+ strut->rect.y = output_rect.y;
+ strut->rect.width = output_rect.width;
+ strut->rect.height = offset_top + state->exclusive_zone + state->margin.top;
+ break;
+ case META_SIDE_BOTTOM:
+ strut->rect.x = output_rect.x;
+ strut->rect.height = offset_bottom + state->exclusive_zone + state->margin.bottom;
+ strut->rect.y = output_rect.y + output_rect.height - strut->rect.height;
+ strut->rect.width = output_rect.width;
+ break;
+ case META_SIDE_LEFT:
+ strut->rect.x = output_rect.x;
+ strut->rect.y = output_rect.y;
+ strut->rect.width = offset_left + state->exclusive_zone + state->margin.left;
+ strut->rect.height = output_rect.height;
+ break;
+ case META_SIDE_RIGHT:
+ strut->rect.y = output_rect.y;
+ strut->rect.width = offset_right + state->exclusive_zone + state->margin.right;
+ strut->rect.x = output_rect.x + output_rect.width - strut->rect.width;
+ strut->rect.height = output_rect.height;
+ break;
+ default:
+ g_free (strut);
+ return NULL;
+ }
+
+ return strut;
+}
+
+static void
+layer_surface_set_size (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t width,
+ uint32_t height)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+
+ layer_surface->pending.desired_width = width;
+ layer_surface->pending.desired_height = height;
+}
+
+static void
+layer_surface_set_anchor (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t anchor)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+
+ if (anchor > (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT))
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR,
+ "Invalid anchor value");
+ return;
+ }
+
+ layer_surface->pending.anchor = anchor;
+}
+
+static void
+layer_surface_set_exclusive_zone (struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t zone)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+
+ layer_surface->pending.exclusive_zone = zone;
+}
+
+static void
+layer_surface_set_margin (struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t top,
+ int32_t right,
+ int32_t bottom,
+ int32_t left)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+
+ layer_surface->pending.margin.top = top;
+ layer_surface->pending.margin.right = right;
+ layer_surface->pending.margin.bottom = bottom;
+ layer_surface->pending.margin.left = left;
+}
+
+static void
+layer_surface_set_keyboard_interactivity (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t keyboard_interactivity)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+
+ if (keyboard_interactivity > ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND)
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_KEYBOARD_INTERACTIVITY,
+ "Invalid keyboard interactivity value");
+ return;
+ }
+
+ layer_surface->pending.keyboard_interactivity = keyboard_interactivity;
+}
+
+static void
+layer_surface_get_popup (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *popup_resource)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+ MetaWaylandSurface *surface =
+ meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (layer_surface));
+ MetaWaylandXdgPopup *xdg_popup;
+
+ if (!popup_resource)
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE,
+ "popup resource is NULL");
+ return;
+ }
+
+ xdg_popup = wl_resource_get_user_data (popup_resource);
+ if (!xdg_popup || !META_IS_WAYLAND_XDG_POPUP (xdg_popup))
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE,
+ "popup is not a valid xdg_popup");
+ return;
+ }
+
+ meta_wayland_xdg_popup_set_parent_surface (xdg_popup, surface);
+}
+
+static void
+layer_surface_ack_configure (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+
+ layer_surface->configure_serial = serial;
+ layer_surface->configured = TRUE;
+}
+
+static void
+layer_surface_destroy (struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static void
+layer_surface_set_layer (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t layer)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+
+ if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY)
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER,
+ "Invalid layer value");
+ return;
+ }
+
+ layer_surface->pending.layer = layer;
+}
+
+static void
+layer_surface_set_exclusive_edge (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t edge)
+{
+ /* TODO: Implement exclusive edge */
+}
+
+static const struct zwlr_layer_surface_v1_interface layer_surface_interface = {
+ layer_surface_set_size,
+ layer_surface_set_anchor,
+ layer_surface_set_exclusive_zone,
+ layer_surface_set_margin,
+ layer_surface_set_keyboard_interactivity,
+ layer_surface_get_popup,
+ layer_surface_ack_configure,
+ layer_surface_destroy,
+ layer_surface_set_layer,
+ layer_surface_set_exclusive_edge,
+};
+
+static void
+layer_surface_resource_destroyed (struct wl_resource *resource)
+{
+ MetaWaylandLayerSurface *layer_surface = wl_resource_get_user_data (resource);
+
+ if (layer_surface)
+ {
+ layer_surface->resource = NULL;
+ g_free (layer_surface->namespace);
+ layer_surface->namespace = NULL;
+ }
+}
+
+/* Calculate surface position based on anchor, margin, and output/workarea geometry */
+static void
+calculate_surface_position (MetaWaylandLayerSurface *layer_surface,
+ int *out_x,
+ int *out_y)
+{
+ MetaRectangle output_rect = { 0, 0, 0, 0 };
+ MetaRectangle usable_area = { 0, 0, 0, 0 };
+ MetaRectangle *bounds;
+ MetaWaylandSurface *surface;
+ int width, height;
+ int x = 0, y = 0;
+ uint32_t anchor;
+
+ surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (layer_surface));
+ if (!surface || !surface->buffer_ref->buffer)
+ {
+ *out_x = 0;
+ *out_y = 0;
+ return;
+ }
+
+ get_layer_surface_bounds (layer_surface, &layer_surface->current,
+ &output_rect, &usable_area);
+
+ /* Use appropriate bounds based on exclusive_zone:
+ * -1: use full output (extend under panels)
+ * 0 or >0: use workarea (respect builtin panels like Cinnamon's) */
+ if (layer_surface->current.exclusive_zone == -1)
+ bounds = &output_rect;
+ else
+ bounds = &usable_area;
+
+ width = meta_wayland_surface_get_width (surface);
+ height = meta_wayland_surface_get_height (surface);
+ anchor = layer_surface->current.anchor;
+
+ /* Calculate X position */
+ if ((anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) &&
+ (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT))
+ {
+ /* Horizontally centered, stretched */
+ x = bounds->x + layer_surface->current.margin.left;
+ }
+ else if (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)
+ {
+ x = bounds->x + layer_surface->current.margin.left;
+ }
+ else if (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)
+ {
+ x = bounds->x + bounds->width - width - layer_surface->current.margin.right;
+ }
+ else
+ {
+ /* Horizontally centered */
+ x = bounds->x + (bounds->width - width) / 2;
+ }
+
+ /* Calculate Y position */
+ if ((anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) &&
+ (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM))
+ {
+ /* Vertically centered, stretched */
+ y = bounds->y + layer_surface->current.margin.top;
+ }
+ else if (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)
+ {
+ y = bounds->y + layer_surface->current.margin.top;
+ }
+ else if (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)
+ {
+ y = bounds->y + bounds->height - height - layer_surface->current.margin.bottom;
+ }
+ else
+ {
+ /* Vertically centered */
+ y = bounds->y + (bounds->height - height) / 2;
+ }
+
+ *out_x = x;
+ *out_y = y;
+}
+
+static ClutterActor *
+get_layer_container_for_layer (MetaWaylandLayerSurface *layer_surface)
+{
+ MetaDisplay *display;
+ ClutterActor *layer_container = NULL;
+
+ display = meta_get_display ();
+
+ switch (layer_surface->current.layer)
+ {
+ case META_LAYER_SHELL_LAYER_BACKGROUND:
+ case META_LAYER_SHELL_LAYER_BOTTOM:
+ /* Use bottom_window_group for background and bottom layers */
+ layer_container = meta_get_bottom_window_group_for_display (display);
+ break;
+ case META_LAYER_SHELL_LAYER_TOP:
+ /* Use top_window_group for top layer */
+ layer_container = meta_get_top_window_group_for_display (display);
+ break;
+ case META_LAYER_SHELL_LAYER_OVERLAY:
+ /* Use feedback_group for overlay (topmost) */
+ layer_container = meta_get_feedback_group_for_display (display);
+ break;
+ }
+
+ return layer_container;
+}
+
+static void
+meta_wayland_layer_surface_apply_state (MetaWaylandSurfaceRole *surface_role,
+ MetaWaylandSurfaceState *pending)
+{
+ MetaWaylandLayerSurface *layer_surface = META_WAYLAND_LAYER_SURFACE (surface_role);
+ MetaWaylandSurface *surface =
+ meta_wayland_surface_role_get_surface (surface_role);
+ MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (surface_role);
+ MetaSurfaceActor *surface_actor;
+ ClutterActor *layer_container;
+ gboolean had_buffer;
+ gboolean has_buffer;
+ gboolean struts_changed = FALSE;
+ MetaWaylandLayerSurfaceState old_state;
+ int x, y;
+
+ had_buffer = layer_surface->mapped;
+ has_buffer = surface->buffer_ref->buffer != NULL;
+
+ /* Save old state for strut change detection */
+ old_state = layer_surface->current;
+
+ /* Copy pending state to current */
+ layer_surface->current = layer_surface->pending;
+
+ /* Chain up to handle frame callbacks */
+ meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending);
+
+ /* If client committed without a buffer and hasn't been properly configured,
+ * send a configure with the calculated size based on their anchors. */
+ if (!has_buffer && !layer_surface->configured)
+ {
+ meta_wayland_layer_surface_send_configure (layer_surface);
+ }
+
+ surface_actor = meta_wayland_actor_surface_get_actor (actor_surface);
+ if (!surface_actor)
+ return;
+
+ layer_container = get_layer_container_for_layer (layer_surface);
+ if (!layer_container)
+ return;
+
+ if (has_buffer)
+ {
+ if (!had_buffer)
+ {
+ /* Surface is being mapped */
+ ClutterActor *actor = CLUTTER_ACTOR (surface_actor);
+
+ clutter_actor_set_reactive (actor, TRUE);
+
+ if (layer_surface->current.layer == META_LAYER_SHELL_LAYER_BACKGROUND)
+ clutter_actor_insert_child_at_index (layer_container, actor, 0);
+ else
+ clutter_actor_add_child (layer_container, actor);
+ layer_surface->mapped = TRUE;
+
+ /* Mapping may affect struts */
+ if (layer_surface->current.exclusive_zone > 0)
+ struts_changed = TRUE;
+
+ g_debug ("Layer surface mapped: namespace=%s layer=%d",
+ layer_surface->namespace ? layer_surface->namespace : "(null)",
+ layer_surface->current.layer);
+
+ {
+ MetaWaylandLayerShell *layer_shell =
+ meta_wayland_layer_shell_from_compositor (surface->compositor);
+ if (layer_shell)
+ g_signal_emit (layer_shell,
+ layer_shell_signals[LAYER_SHELL_SIGNAL_LAYER_SURFACE_MAPPED],
+ 0, layer_surface);
+ }
+ }
+ else
+ {
+ /* Check if strut-affecting properties changed while mapped */
+ if (layer_surface->current.exclusive_zone != old_state.exclusive_zone ||
+ layer_surface->current.anchor != old_state.anchor ||
+ layer_surface->current.margin.top != old_state.margin.top ||
+ layer_surface->current.margin.bottom != old_state.margin.bottom ||
+ layer_surface->current.margin.left != old_state.margin.left ||
+ layer_surface->current.margin.right != old_state.margin.right)
+ {
+ if (layer_surface->current.exclusive_zone > 0 ||
+ old_state.exclusive_zone > 0)
+ struts_changed = TRUE;
+ }
+ }
+
+ /* Sync actor state */
+ meta_wayland_actor_surface_sync_actor_state (actor_surface);
+
+ /* Update position */
+ calculate_surface_position (layer_surface, &x, &y);
+ clutter_actor_set_position (CLUTTER_ACTOR (surface_actor), x, y);
+ }
+ else if (had_buffer && !has_buffer)
+ {
+ /* Surface is being unmapped */
+ ClutterActor *actor = CLUTTER_ACTOR (surface_actor);
+
+ clutter_actor_set_reactive (actor, FALSE);
+
+ if (clutter_actor_get_parent (actor))
+ clutter_actor_remove_child (layer_container, actor);
+
+ layer_surface->mapped = FALSE;
+
+ /* Unmapping may affect struts */
+ if (old_state.exclusive_zone > 0)
+ struts_changed = TRUE;
+
+ g_debug ("Layer surface unmapped: namespace=%s",
+ layer_surface->namespace ? layer_surface->namespace : "(null)");
+
+ {
+ MetaWaylandLayerShell *layer_shell =
+ meta_wayland_layer_shell_from_compositor (surface->compositor);
+ if (layer_shell)
+ g_signal_emit (layer_shell,
+ layer_shell_signals[LAYER_SHELL_SIGNAL_LAYER_SURFACE_UNMAPPED],
+ 0, layer_surface);
+ }
+ }
+
+ /* Update workspace struts if needed */
+ if (struts_changed)
+ meta_wayland_layer_shell_update_struts (surface->compositor);
+}
+
+static void
+meta_wayland_layer_surface_send_configure (MetaWaylandLayerSurface *layer_surface)
+{
+ MetaWaylandSurface *surface =
+ meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (layer_surface));
+ MetaWaylandLayerSurfaceState *state = &layer_surface->pending;
+ MetaRectangle output_rect = { 0, 0, 0, 0 };
+ MetaRectangle usable_area = { 0, 0, 0, 0 };
+ MetaRectangle *bounds;
+ uint32_t width, height;
+ uint32_t serial;
+
+ if (!layer_surface->resource)
+ return;
+
+ get_layer_surface_bounds (layer_surface, state, &output_rect, &usable_area);
+
+ /* Use appropriate bounds based on exclusive_zone:
+ * -1: use full output (extend under panels)
+ * 0 or >0: use workarea (respect builtin panels like Cinnamon's) */
+ if (state->exclusive_zone == -1)
+ bounds = &output_rect;
+ else
+ bounds = &usable_area;
+
+ /* Calculate configure size based on anchors and desired size */
+ if (state->desired_width != 0)
+ width = state->desired_width;
+ else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) &&
+ (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT))
+ width = bounds->width -
+ state->margin.left -
+ state->margin.right;
+ else
+ width = 0;
+
+ if (state->desired_height != 0)
+ height = state->desired_height;
+ else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) &&
+ (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM))
+ height = bounds->height -
+ state->margin.top -
+ state->margin.bottom;
+ else
+ height = 0;
+
+ serial = wl_display_next_serial (surface->compositor->wayland_display);
+ zwlr_layer_surface_v1_send_configure (layer_surface->resource, serial, width, height);
+
+ g_debug ("Layer surface configured: serial=%u size=%ux%u", serial, width, height);
+}
+
+static void
+meta_wayland_layer_surface_dispose (GObject *object)
+{
+ MetaWaylandLayerSurface *layer_surface = META_WAYLAND_LAYER_SURFACE (object);
+ MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (object);
+ MetaWaylandSurface *surface;
+ MetaSurfaceActor *surface_actor;
+ gboolean had_struts;
+
+ had_struts = layer_surface->mapped && layer_surface->current.exclusive_zone > 0;
+
+ if (layer_surface->mapped)
+ {
+ surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (layer_surface));
+ if (surface && surface->compositor)
+ {
+ MetaWaylandLayerShell *layer_shell =
+ meta_wayland_layer_shell_from_compositor (surface->compositor);
+ if (layer_shell)
+ g_signal_emit (layer_shell,
+ layer_shell_signals[LAYER_SHELL_SIGNAL_LAYER_SURFACE_UNMAPPED],
+ 0, layer_surface);
+ }
+ layer_surface->mapped = FALSE;
+ }
+
+ /* Remove from layer container */
+ surface_actor = meta_wayland_actor_surface_get_actor (actor_surface);
+ if (surface_actor)
+ {
+ ClutterActor *actor = CLUTTER_ACTOR (surface_actor);
+ ClutterActor *parent = clutter_actor_get_parent (actor);
+
+ if (parent)
+ clutter_actor_remove_child (parent, actor);
+ }
+
+ /* Remove from tracking list and update struts */
+ surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (layer_surface));
+ if (surface && surface->compositor)
+ {
+ MetaWaylandLayerShell *layer_shell =
+ meta_wayland_layer_shell_from_compositor (surface->compositor);
+
+ if (layer_shell)
+ {
+ layer_shell->layer_surfaces = g_list_remove (layer_shell->layer_surfaces,
+ layer_surface);
+ if (had_struts)
+ meta_wayland_layer_shell_update_struts (surface->compositor);
+ }
+ }
+
+ g_clear_pointer (&layer_surface->namespace, g_free);
+
+ G_OBJECT_CLASS (meta_wayland_layer_surface_parent_class)->dispose (object);
+}
+
+static void
+meta_wayland_layer_surface_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaWaylandLayerSurface *layer_surface = META_WAYLAND_LAYER_SURFACE (object);
+
+ switch (prop_id)
+ {
+ case PROP_OUTPUT:
+ layer_surface->output = g_value_get_pointer (value);
+ break;
+ case PROP_NAMESPACE:
+ layer_surface->namespace = g_value_dup_string (value);
+ break;
+ case PROP_INITIAL_LAYER:
+ layer_surface->initial_layer = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+meta_wayland_layer_surface_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MetaWaylandLayerSurface *layer_surface = META_WAYLAND_LAYER_SURFACE (object);
+
+ switch (prop_id)
+ {
+ case PROP_OUTPUT:
+ g_value_set_pointer (value, layer_surface->output);
+ break;
+ case PROP_NAMESPACE:
+ g_value_set_string (value, layer_surface->namespace);
+ break;
+ case PROP_INITIAL_LAYER:
+ g_value_set_uint (value, layer_surface->initial_layer);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+meta_wayland_layer_surface_constructed (GObject *object)
+{
+ MetaWaylandLayerSurface *layer_surface = META_WAYLAND_LAYER_SURFACE (object);
+
+ G_OBJECT_CLASS (meta_wayland_layer_surface_parent_class)->constructed (object);
+
+ /* Apply the initial layer from construction property */
+ layer_surface->pending.layer = layer_surface->initial_layer;
+}
+
+static void
+meta_wayland_layer_surface_init (MetaWaylandLayerSurface *layer_surface)
+{
+ /* Default state */
+ layer_surface->pending.layer = META_LAYER_SHELL_LAYER_BACKGROUND;
+ layer_surface->pending.keyboard_interactivity =
+ ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
+ layer_surface->pending.exclusive_zone = 0;
+ layer_surface->configured = FALSE;
+ layer_surface->mapped = FALSE;
+}
+
+static void
+meta_wayland_layer_surface_assigned (MetaWaylandSurfaceRole *surface_role)
+{
+ MetaWaylandSurfaceRoleClass *surface_role_class =
+ META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_layer_surface_parent_class);
+ MetaWaylandSurface *surface =
+ meta_wayland_surface_role_get_surface (surface_role);
+
+ surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs ();
+
+ if (surface_role_class->assigned)
+ surface_role_class->assigned (surface_role);
+}
+
+static void
+meta_wayland_layer_surface_class_init (MetaWaylandLayerSurfaceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MetaWaylandSurfaceRoleClass *surface_role_class =
+ META_WAYLAND_SURFACE_ROLE_CLASS (klass);
+
+ object_class->constructed = meta_wayland_layer_surface_constructed;
+ object_class->dispose = meta_wayland_layer_surface_dispose;
+ object_class->set_property = meta_wayland_layer_surface_set_property;
+ object_class->get_property = meta_wayland_layer_surface_get_property;
+
+ surface_role_class->assigned = meta_wayland_layer_surface_assigned;
+ surface_role_class->apply_state = meta_wayland_layer_surface_apply_state;
+
+ layer_surface_props[PROP_OUTPUT] =
+ g_param_spec_pointer ("output", NULL, NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ layer_surface_props[PROP_NAMESPACE] =
+ g_param_spec_string ("namespace", NULL, NULL, NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ layer_surface_props[PROP_INITIAL_LAYER] =
+ g_param_spec_uint ("initial-layer", NULL, NULL,
+ 0, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+ META_LAYER_SHELL_LAYER_BACKGROUND,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, N_PROPS, layer_surface_props);
+}
+
+MetaLayerShellLayer
+meta_wayland_layer_surface_get_layer (MetaWaylandLayerSurface *layer_surface)
+{
+ return layer_surface->current.layer;
+}
+
+MetaWaylandOutput *
+meta_wayland_layer_surface_get_output (MetaWaylandLayerSurface *layer_surface)
+{
+ return layer_surface->output;
+}
+
+gboolean
+meta_wayland_layer_surface_wants_keyboard_focus (MetaWaylandLayerSurface *layer_surface)
+{
+ return layer_surface->current.keyboard_interactivity !=
+ ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
+}
+
+/* Layer shell protocol implementation */
+static void
+layer_shell_get_layer_surface (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface_resource,
+ struct wl_resource *output_resource,
+ uint32_t layer,
+ const char *namespace)
+{
+ MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
+ MetaWaylandOutput *output = NULL;
+ MetaWaylandLayerSurface *layer_surface;
+
+ /* Check if surface already has a role */
+ if (surface->role)
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SHELL_V1_ERROR_ROLE,
+ "Surface already has a role");
+ return;
+ }
+
+ /* Validate layer value */
+ if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY)
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER,
+ "Invalid layer value");
+ return;
+ }
+
+ /* Check if surface already has a buffer */
+ if (surface->buffer_ref->buffer)
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED,
+ "Surface has a buffer attached");
+ return;
+ }
+
+ if (output_resource)
+ output = wl_resource_get_user_data (output_resource);
+
+ /* Create layer surface role via meta_wayland_surface_assign_role.
+ * This ensures the surface is associated with the role during construction,
+ * which is required by MetaWaylandActorSurface's constructed() handler. */
+ if (!meta_wayland_surface_assign_role (surface,
+ META_TYPE_WAYLAND_LAYER_SURFACE,
+ "output", output,
+ "namespace", namespace,
+ "initial-layer", layer,
+ NULL))
+ {
+ wl_resource_post_error (resource,
+ ZWLR_LAYER_SHELL_V1_ERROR_ROLE,
+ "wl_surface@%d already has a different role",
+ wl_resource_get_id (surface_resource));
+ return;
+ }
+
+ layer_surface = META_WAYLAND_LAYER_SURFACE (surface->role);
+
+ layer_surface->resource =
+ wl_resource_create (client,
+ &zwlr_layer_surface_v1_interface,
+ wl_resource_get_version (resource),
+ id);
+
+ wl_resource_set_implementation (layer_surface->resource,
+ &layer_surface_interface,
+ layer_surface,
+ layer_surface_resource_destroyed);
+
+ /* Add to tracking list and ensure signal is connected */
+ {
+ MetaWaylandLayerShell *layer_shell = wl_resource_get_user_data (resource);
+ layer_shell->layer_surfaces = g_list_prepend (layer_shell->layer_surfaces,
+ layer_surface);
+ meta_wayland_layer_shell_ensure_signal_connected (layer_shell);
+ }
+
+ g_debug ("Layer surface created: namespace=%s layer=%d output=%p",
+ namespace ? namespace : "(null)", layer, output);
+
+ /* Send initial configure now that resource is ready */
+ meta_wayland_layer_surface_send_configure (layer_surface);
+}
+
+static void
+layer_shell_destroy (struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static const struct zwlr_layer_shell_v1_interface layer_shell_interface = {
+ layer_shell_get_layer_surface,
+ layer_shell_destroy,
+};
+
+static void
+layer_shell_destructor (struct wl_resource *resource)
+{
+ MetaWaylandLayerShell *layer_shell = wl_resource_get_user_data (resource);
+
+ layer_shell->shell_resources = g_list_remove (layer_shell->shell_resources,
+ resource);
+}
+
+static void
+bind_layer_shell (struct wl_client *client,
+ void *data,
+ uint32_t version,
+ uint32_t id)
+{
+ MetaWaylandLayerShell *layer_shell = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create (client,
+ &zwlr_layer_shell_v1_interface,
+ version,
+ id);
+ wl_resource_set_implementation (resource,
+ &layer_shell_interface,
+ layer_shell,
+ layer_shell_destructor);
+
+ layer_shell->shell_resources = g_list_prepend (layer_shell->shell_resources,
+ resource);
+}
+
+static void
+meta_wayland_layer_shell_dispose (GObject *object)
+{
+ MetaWaylandLayerShell *layer_shell = META_WAYLAND_LAYER_SHELL (object);
+
+ if (layer_shell->workareas_changed_handler_id != 0)
+ {
+ MetaDisplay *display = meta_get_display ();
+ if (display)
+ g_signal_handler_disconnect (display, layer_shell->workareas_changed_handler_id);
+ layer_shell->workareas_changed_handler_id = 0;
+ }
+
+ g_clear_pointer (&layer_shell->layer_surfaces, g_list_free);
+ g_clear_pointer (&layer_shell->shell_resources, g_list_free);
+
+ G_OBJECT_CLASS (meta_wayland_layer_shell_parent_class)->dispose (object);
+}
+
+static void
+meta_wayland_layer_shell_init (MetaWaylandLayerShell *layer_shell)
+{
+}
+
+static void
+meta_wayland_layer_shell_class_init (MetaWaylandLayerShellClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = meta_wayland_layer_shell_dispose;
+
+ layer_shell_signals[LAYER_SHELL_SIGNAL_LAYER_SURFACE_MAPPED] =
+ g_signal_new ("layer-surface-mapped",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ META_TYPE_WAYLAND_LAYER_SURFACE);
+
+ layer_shell_signals[LAYER_SHELL_SIGNAL_LAYER_SURFACE_UNMAPPED] =
+ g_signal_new ("layer-surface-unmapped",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ META_TYPE_WAYLAND_LAYER_SURFACE);
+}
+
+static MetaWaylandLayerShell *
+meta_wayland_layer_shell_new (MetaWaylandCompositor *compositor)
+{
+ MetaWaylandLayerShell *layer_shell;
+
+ layer_shell = g_object_new (META_TYPE_WAYLAND_LAYER_SHELL, NULL);
+ layer_shell->compositor = compositor;
+
+ if (wl_global_create (compositor->wayland_display,
+ &zwlr_layer_shell_v1_interface,
+ META_ZWLR_LAYER_SHELL_V1_VERSION,
+ layer_shell, bind_layer_shell) == NULL)
+ {
+ g_warning ("Failed to register wlr_layer_shell_v1 global");
+ g_object_unref (layer_shell);
+ return NULL;
+ }
+
+ g_debug ("Layer shell protocol initialized (version %d)", META_ZWLR_LAYER_SHELL_V1_VERSION);
+
+ return layer_shell;
+}
+
+void
+meta_wayland_layer_shell_update_struts (MetaWaylandCompositor *compositor)
+{
+ MetaWaylandLayerShell *layer_shell;
+ MetaDisplay *display;
+ MetaWorkspaceManager *workspace_manager;
+ GList *workspaces;
+ GList *l;
+ GSList *struts = NULL;
+
+ layer_shell = meta_wayland_layer_shell_from_compositor (compositor);
+ if (!layer_shell)
+ return;
+
+ display = meta_get_display ();
+ if (!display)
+ return;
+
+ workspace_manager = meta_display_get_workspace_manager (display);
+ if (!workspace_manager)
+ return;
+
+ for (l = layer_shell->layer_surfaces; l; l = l->next)
+ {
+ MetaWaylandLayerSurface *surface = l->data;
+ MetaStrut *strut = meta_wayland_layer_surface_create_strut (surface);
+
+ if (strut)
+ struts = g_slist_prepend (struts, strut);
+ }
+
+ workspaces = meta_workspace_manager_get_workspaces (workspace_manager);
+ for (l = workspaces; l; l = l->next)
+ {
+ MetaWorkspace *workspace = l->data;
+ meta_workspace_set_layer_shell_struts (workspace, struts);
+ }
+
+ g_slist_free_full (struts, g_free);
+}
+
+void
+meta_wayland_layer_shell_on_workarea_changed (MetaWaylandCompositor *compositor)
+{
+ MetaWaylandLayerShell *layer_shell;
+ GList *l;
+
+ layer_shell = meta_wayland_layer_shell_from_compositor (compositor);
+ if (!layer_shell)
+ return;
+
+ for (l = layer_shell->layer_surfaces; l; l = l->next)
+ {
+ MetaWaylandLayerSurface *surface = l->data;
+
+ /* Surfaces with exclusive_zone != -1 use workarea bounds and need
+ * repositioning when workarea changes. Surfaces with exclusive_zone == -1
+ * use full output and aren't affected. */
+ if (surface->current.exclusive_zone != -1 && surface->mapped)
+ {
+ MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (surface);
+ MetaSurfaceActor *surface_actor = meta_wayland_actor_surface_get_actor (actor_surface);
+
+ if (surface_actor)
+ {
+ int x, y;
+ calculate_surface_position (surface, &x, &y);
+ clutter_actor_set_position (CLUTTER_ACTOR (surface_actor), x, y);
+ }
+
+ /* Also send configure in case size changed */
+ meta_wayland_layer_surface_send_configure (surface);
+ }
+ }
+
+ /* Recalculate layer-shell struts since surface positions changed */
+ meta_wayland_layer_shell_update_struts (compositor);
+}
+
+MetaWaylandLayerShell *
+meta_wayland_compositor_get_layer_shell (MetaWaylandCompositor *compositor)
+{
+ return meta_wayland_layer_shell_from_compositor (compositor);
+}
+
+MetaWaylandLayerSurface *
+meta_wayland_layer_shell_find_surface (MetaWaylandLayerShell *layer_shell,
+ MetaLayerShellLayer layer,
+ const char *namespace_str,
+ MetaWaylandOutput *output)
+{
+ GList *l;
+
+ g_return_val_if_fail (layer_shell != NULL, NULL);
+
+ for (l = layer_shell->layer_surfaces; l; l = l->next)
+ {
+ MetaWaylandLayerSurface *surface = l->data;
+
+ if (!surface->mapped)
+ continue;
+
+ if (surface->current.layer != layer)
+ continue;
+
+ if (namespace_str != NULL &&
+ g_strcmp0 (surface->namespace, namespace_str) != 0)
+ continue;
+
+ if (output != NULL && surface->output != output)
+ continue;
+
+ return surface;
+ }
+
+ return NULL;
+}
+
+const char *
+meta_wayland_layer_surface_get_namespace (MetaWaylandLayerSurface *layer_surface)
+{
+ g_return_val_if_fail (layer_surface != NULL, NULL);
+
+ return layer_surface->namespace;
+}
+
+void
+meta_wayland_init_layer_shell (MetaWaylandCompositor *compositor)
+{
+ g_object_set_data_full (G_OBJECT (compositor), "-meta-wayland-layer-shell",
+ meta_wayland_layer_shell_new (compositor),
+ g_object_unref);
+}
diff --git a/src/wayland/meta-wayland-layer-shell.h b/src/wayland/meta-wayland-layer-shell.h
new file mode 100644
index 000000000..1809f5a12
--- /dev/null
+++ b/src/wayland/meta-wayland-layer-shell.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 Linux Mint
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_WAYLAND_LAYER_SHELL_H
+#define META_WAYLAND_LAYER_SHELL_H
+
+#include "wayland/meta-wayland.h"
+#include "wayland/meta-wayland-actor-surface.h"
+
+
+/* Layer shell layer values - matches protocol enum */
+typedef enum
+{
+ META_LAYER_SHELL_LAYER_BACKGROUND = 0,
+ META_LAYER_SHELL_LAYER_BOTTOM = 1,
+ META_LAYER_SHELL_LAYER_TOP = 2,
+ META_LAYER_SHELL_LAYER_OVERLAY = 3,
+} MetaLayerShellLayer;
+
+#define META_TYPE_WAYLAND_LAYER_SHELL (meta_wayland_layer_shell_get_type ())
+G_DECLARE_FINAL_TYPE (MetaWaylandLayerShell, meta_wayland_layer_shell,
+ META, WAYLAND_LAYER_SHELL, GObject)
+
+#define META_TYPE_WAYLAND_LAYER_SURFACE (meta_wayland_layer_surface_get_type ())
+G_DECLARE_FINAL_TYPE (MetaWaylandLayerSurface, meta_wayland_layer_surface,
+ META, WAYLAND_LAYER_SURFACE, MetaWaylandActorSurface)
+
+void meta_wayland_init_layer_shell (MetaWaylandCompositor *compositor);
+
+MetaLayerShellLayer meta_wayland_layer_surface_get_layer (MetaWaylandLayerSurface *layer_surface);
+MetaWaylandOutput * meta_wayland_layer_surface_get_output (MetaWaylandLayerSurface *layer_surface);
+gboolean meta_wayland_layer_surface_wants_keyboard_focus (MetaWaylandLayerSurface *layer_surface);
+
+void meta_wayland_layer_shell_update_struts (MetaWaylandCompositor *compositor);
+void meta_wayland_layer_shell_on_workarea_changed (MetaWaylandCompositor *compositor);
+
+MetaWaylandLayerShell * meta_wayland_compositor_get_layer_shell (MetaWaylandCompositor *compositor);
+
+MetaWaylandLayerSurface * meta_wayland_layer_shell_find_surface (MetaWaylandLayerShell *layer_shell,
+ MetaLayerShellLayer layer,
+ const char *namespace_str,
+ MetaWaylandOutput *output);
+
+const char * meta_wayland_layer_surface_get_namespace (MetaWaylandLayerSurface *layer_surface);
+
+#endif /* META_WAYLAND_LAYER_SHELL_H */
diff --git a/src/wayland/meta-wayland-outputs.c b/src/wayland/meta-wayland-outputs.c
index b75434256..3b7941437 100644
--- a/src/wayland/meta-wayland-outputs.c
+++ b/src/wayland/meta-wayland-outputs.c
@@ -778,3 +778,39 @@ meta_wayland_outputs_init (MetaWaylandCompositor *compositor)
NULL,
bind_xdg_output_manager);
}
+
+typedef struct
+{
+ int monitor_index;
+ MetaWaylandOutput *result;
+} FindOutputData;
+
+static void
+find_output_for_monitor (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ MetaWaylandOutput *output = value;
+ FindOutputData *data = user_data;
+
+ if (data->result != NULL)
+ return;
+
+ if (output->logical_monitor &&
+ output->logical_monitor->number == data->monitor_index)
+ data->result = output;
+}
+
+MetaWaylandOutput *
+meta_wayland_compositor_get_output_for_monitor (MetaWaylandCompositor *compositor,
+ int monitor_index)
+{
+ FindOutputData data = { monitor_index, NULL };
+
+ g_return_val_if_fail (compositor != NULL, NULL);
+ g_return_val_if_fail (compositor->outputs != NULL, NULL);
+
+ g_hash_table_foreach (compositor->outputs, find_output_for_monitor, &data);
+
+ return data.result;
+}
diff --git a/src/wayland/meta-wayland-outputs.h b/src/wayland/meta-wayland-outputs.h
index 381febcdb..46c3774dd 100644
--- a/src/wayland/meta-wayland-outputs.h
+++ b/src/wayland/meta-wayland-outputs.h
@@ -52,4 +52,7 @@ struct _MetaWaylandOutput
void meta_wayland_outputs_init (MetaWaylandCompositor *compositor);
+MetaWaylandOutput * meta_wayland_compositor_get_output_for_monitor (MetaWaylandCompositor *compositor,
+ int monitor_index);
+
#endif /* META_WAYLAND_OUTPUTS_H */
diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c
index 9aef940cb..1af879055 100644
--- a/src/wayland/meta-wayland-pointer.c
+++ b/src/wayland/meta-wayland-pointer.c
@@ -62,6 +62,7 @@
#include "wayland/meta-wayland-private.h"
#include "wayland/meta-wayland-seat.h"
#include "wayland/meta-wayland-surface.h"
+#include "wayland/meta-wayland-layer-shell.h"
#include "wayland/meta-xwayland.h"
#ifdef HAVE_NATIVE_BACKEND
@@ -424,6 +425,9 @@ meta_wayland_pointer_send_button (MetaWaylandPointer *pointer,
event_type == CLUTTER_BUTTON_PRESS ? 1 : 0);
}
+ if (event_type == CLUTTER_BUTTON_PRESS)
+ pointer->grab_serial = serial;
+
meta_wayland_pointer_broadcast_frame (pointer);
}
@@ -485,6 +489,30 @@ default_grab_button (MetaWaylandPointerGrab *grab,
MetaWaylandPointer *pointer = grab->pointer;
meta_wayland_pointer_send_button (pointer, event);
+
+ if (clutter_event_type (event) == CLUTTER_BUTTON_PRESS &&
+ pointer->focus_surface)
+ {
+ MetaWaylandSurfaceRole *role = pointer->focus_surface->role;
+
+ if (role && META_IS_WAYLAND_LAYER_SURFACE (role) &&
+ meta_wayland_layer_surface_wants_keyboard_focus (
+ META_WAYLAND_LAYER_SURFACE (role)))
+ {
+ MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
+
+ if (meta_wayland_seat_has_keyboard (seat))
+ {
+ MetaDisplay *display = meta_get_display ();
+
+ if (display->focus_window)
+ meta_display_update_focus_window (display, NULL);
+
+ meta_wayland_keyboard_set_focus (seat->keyboard,
+ pointer->focus_surface);
+ }
+ }
+ }
}
static const MetaWaylandPointerGrabInterface default_pointer_grab_interface = {
@@ -696,13 +724,6 @@ handle_button_event (MetaWaylandPointer *pointer,
}
pointer->grab->interface->button (pointer->grab, event);
-
- if (implicit_grab)
- {
- MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
-
- pointer->grab_serial = wl_display_get_serial (seat->wl_display);
- }
}
static void
diff --git a/src/wayland/meta-wayland-shell-surface.c b/src/wayland/meta-wayland-shell-surface.c
index 12d291043..6bfeee332 100644
--- a/src/wayland/meta-wayland-shell-surface.c
+++ b/src/wayland/meta-wayland-shell-surface.c
@@ -244,6 +244,7 @@ meta_wayland_shell_surface_surface_pre_apply_state (MetaWaylandSurfaceRole *sur
MetaWaylandSurface *surface =
meta_wayland_surface_role_get_surface (surface_role);
+ /* Queue calc_showing when buffer is detached (unmap) */
if (pending->newly_attached &&
!surface->buffer_ref->buffer &&
priv->window)
@@ -285,6 +286,10 @@ meta_wayland_shell_surface_surface_apply_state (MetaWaylandSurfaceRole *surface
meta_wayland_surface_get_width (surface) * geometry_scale;
window->buffer_rect.height =
meta_wayland_surface_get_height (surface) * geometry_scale;
+
+ /* Queue calc_showing when buffer is newly attached - needed for window to become visible */
+ if (pending->newly_attached)
+ meta_window_queue (window, META_QUEUE_CALC_SHOWING);
}
static MetaWindow *
@@ -342,9 +347,14 @@ meta_wayland_shell_surface_sync_actor_state (MetaWaylandActorSurface *actor_surf
MetaWaylandActorSurfaceClass *actor_surface_class =
META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_shell_surface_parent_class);
MetaWindow *toplevel_window;
+ MetaWindow *window;
toplevel_window = meta_wayland_surface_get_toplevel_window (surface);
- if (!toplevel_window)
+ window = meta_wayland_surface_get_window (surface);
+
+ /* For popups parented to layer surfaces, there's no toplevel window,
+ * but the popup itself has a window. Allow sync in that case. */
+ if (!toplevel_window && !window)
return;
actor_surface_class->sync_actor_state (actor_surface);
diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
index 7c9a711d8..68d7a57c1 100644
--- a/src/wayland/meta-wayland-surface.c
+++ b/src/wayland/meta-wayland-surface.c
@@ -42,6 +42,7 @@
#include "wayland/meta-wayland-buffer.h"
#include "wayland/meta-wayland-data-device.h"
#include "wayland/meta-wayland-gtk-shell.h"
+#include "wayland/meta-wayland-layer-shell.h"
#include "wayland/meta-wayland-keyboard.h"
#include "wayland/meta-wayland-outputs.h"
#include "wayland/meta-wayland-pointer.h"
@@ -1327,7 +1328,8 @@ update_surface_output_state (gpointer key, gpointer value, gpointer user_data)
MetaLogicalMonitor *logical_monitor;
gboolean is_on_logical_monitor;
- g_assert (surface->role);
+ if (!surface->role)
+ return;
logical_monitor = wayland_output->logical_monitor;
if (!logical_monitor)
@@ -1503,6 +1505,7 @@ meta_wayland_shell_init (MetaWaylandCompositor *compositor)
{
meta_wayland_xdg_shell_init (compositor);
meta_wayland_init_gtk_shell (compositor);
+ meta_wayland_init_layer_shell (compositor);
meta_wayland_init_viewporter (compositor);
}
diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
index 666501229..2f84bf080 100644
--- a/src/wayland/meta-wayland-versions.h
+++ b/src/wayland/meta-wayland-versions.h
@@ -60,5 +60,6 @@
#define META_ZWP_PRIMARY_SELECTION_V1_VERSION 1
#define META_XDG_TOPLEVEL_TAG_V1_VERSION 1
#define META_WP_CURSOR_SHAPE_VERSION 2
+#define META_ZWLR_LAYER_SHELL_V1_VERSION 4
#endif
diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c
index 988123857..ceb85fb06 100644
--- a/src/wayland/meta-wayland-xdg-shell.c
+++ b/src/wayland/meta-wayland-xdg-shell.c
@@ -26,8 +26,11 @@
#include "wayland/meta-wayland-xdg-shell.h"
#include "backends/meta-logical-monitor.h"
+#include "compositor/meta-surface-actor-wayland.h"
+#include "compositor/meta-window-actor-private.h"
#include "core/boxes-private.h"
#include "core/window-private.h"
+#include "wayland/meta-wayland-layer-shell.h"
#include "wayland/meta-wayland-outputs.h"
#include "wayland/meta-wayland-popup.h"
#include "wayland/meta-wayland-private.h"
@@ -136,6 +139,16 @@ struct _MetaWaylandXdgPopup
*/
MetaPlacementRule placement_rule;
+ /* Stored positioner data for when parent is set later (e.g., layer-shell) */
+ MetaRectangle anchor_rect;
+ int32_t width;
+ int32_t height;
+ uint32_t gravity;
+ uint32_t anchor;
+ uint32_t constraint_adjustment;
+ int32_t offset_x;
+ int32_t offset_y;
+
MetaWaylandSeat *grab_seat;
uint32_t grab_serial;
} setup;
@@ -1095,7 +1108,9 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup)
xdg_popup->setup.grab_seat = NULL;
xdg_popup->dismissed_by_client = FALSE;
- if (!meta_wayland_surface_get_window (parent_surface))
+ /* Allow both regular window parents and layer surface parents */
+ if (!meta_wayland_surface_get_window (parent_surface) &&
+ !META_IS_WAYLAND_LAYER_SURFACE (parent_surface->role))
{
xdg_popup_send_popup_done (xdg_popup->resource);
return;
@@ -1267,14 +1282,19 @@ meta_wayland_xdg_popup_post_apply_state (MetaWaylandSurfaceRole *surface_role,
}
parent_window = meta_wayland_surface_get_window (xdg_popup->parent_surface);
- meta_window_get_buffer_rect (window, &buffer_rect);
- meta_window_get_buffer_rect (parent_window, &parent_buffer_rect);
- if (!meta_rectangle_overlap (&buffer_rect, &parent_buffer_rect) &&
- !meta_rectangle_is_adjacent_to (&buffer_rect, &parent_buffer_rect))
+
+ /* Skip overlap check for layer surface parents (no parent window) */
+ if (parent_window)
{
- g_warning ("Buggy client caused popup to be placed outside of "
- "parent window");
- dismiss_invalid_popup (xdg_popup);
+ meta_window_get_buffer_rect (window, &buffer_rect);
+ meta_window_get_buffer_rect (parent_window, &parent_buffer_rect);
+ if (!meta_rectangle_overlap (&buffer_rect, &parent_buffer_rect) &&
+ !meta_rectangle_is_adjacent_to (&buffer_rect, &parent_buffer_rect))
+ {
+ g_warning ("Buggy client caused popup to be placed outside of "
+ "parent window");
+ dismiss_invalid_popup (xdg_popup);
+ }
}
}
@@ -1324,11 +1344,18 @@ meta_wayland_xdg_popup_configure (MetaWaylandShellSurface *shell_surface,
*
* FIXME: Could maybe add a signal that is emitted before the window is
* created so that we can avoid incorrect intermediate foci.
+ *
+ * However, layer-shell surfaces don't have a MetaWindow, so we still need
+ * to send configure for popups parented to layer surfaces.
*/
- if (!parent_window)
+ if (!parent_window && !META_IS_WAYLAND_LAYER_SURFACE (xdg_popup->parent_surface->role))
return;
- geometry_scale = meta_window_wayland_get_geometry_scale (parent_window);
+ if (parent_window)
+ geometry_scale = meta_window_wayland_get_geometry_scale (parent_window);
+ else
+ geometry_scale = 1; /* Layer surfaces use scale 1 */
+
x = configuration->rel_x / geometry_scale;
y = configuration->rel_y / geometry_scale;
if (xdg_popup->pending_repositioned)
@@ -1971,32 +1998,31 @@ xdg_surface_constructor_get_popup (struct wl_client *client,
MetaWaylandXdgPopup *xdg_popup;
MetaWaylandXdgSurface *xdg_surface;
- if (!parent_resource)
+ if (parent_resource)
{
- wl_resource_post_error (xdg_wm_base_resource,
- XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT,
- "Parent surface is null but Mutter does not yet "
- "support specifying parent surfaces via other "
- "protocols");
- return;
- }
+ parent_surface = surface_from_xdg_surface_resource (parent_resource);
+ if (!parent_surface || !META_IS_WAYLAND_XDG_SURFACE (parent_surface->role))
+ {
+ wl_resource_post_error (xdg_wm_base_resource,
+ XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT,
+ "Invalid popup parent role");
+ return;
+ }
- parent_surface = surface_from_xdg_surface_resource (parent_resource);
- if (!parent_surface || !META_IS_WAYLAND_XDG_SURFACE (parent_surface->role))
- {
- wl_resource_post_error (xdg_wm_base_resource,
- XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT,
- "Invalid popup parent role");
- return;
+ parent_window = meta_wayland_surface_get_window (parent_surface);
+ if (!parent_window)
+ {
+ wl_resource_post_error (xdg_wm_base_resource,
+ XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT,
+ "Invalid popup parent window");
+ return;
+ }
}
-
- parent_window = meta_wayland_surface_get_window (parent_surface);
- if (!parent_window)
+ else
{
- wl_resource_post_error (xdg_wm_base_resource,
- XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT,
- "Invalid popup parent window");
- return;
+ /* Parent will be set later via another protocol (e.g., wlr-layer-shell) */
+ parent_surface = NULL;
+ parent_window = NULL;
}
if (!meta_wayland_surface_assign_role (surface,
@@ -2026,8 +2052,23 @@ xdg_surface_constructor_get_popup (struct wl_client *client,
meta_wayland_xdg_surface_constructor_finalize (constructor, xdg_surface);
xdg_positioner = wl_resource_get_user_data (positioner_resource);
- xdg_popup->setup.placement_rule =
- meta_wayland_xdg_positioner_to_placement (xdg_positioner, parent_window);
+ if (parent_window)
+ {
+ xdg_popup->setup.placement_rule =
+ meta_wayland_xdg_positioner_to_placement (xdg_positioner, parent_window);
+ }
+ else
+ {
+ /* Store positioner data for later use (e.g., with layer-shell parent) */
+ xdg_popup->setup.anchor_rect = xdg_positioner->anchor_rect;
+ xdg_popup->setup.width = xdg_positioner->width;
+ xdg_popup->setup.height = xdg_positioner->height;
+ xdg_popup->setup.gravity = xdg_positioner->gravity;
+ xdg_popup->setup.anchor = xdg_positioner->anchor;
+ xdg_popup->setup.constraint_adjustment = xdg_positioner->constraint_adjustment;
+ xdg_popup->setup.offset_x = xdg_positioner->offset_x;
+ xdg_popup->setup.offset_y = xdg_positioner->offset_y;
+ }
xdg_popup->setup.parent_surface = parent_surface;
}
@@ -2529,6 +2570,51 @@ bind_xdg_wm_base (struct wl_client *client,
shell_client, xdg_wm_base_destructor);
}
+void
+meta_wayland_xdg_popup_set_parent_surface (MetaWaylandXdgPopup *xdg_popup,
+ MetaWaylandSurface *parent_surface)
+{
+ g_return_if_fail (META_IS_WAYLAND_XDG_POPUP (xdg_popup));
+
+ xdg_popup->setup.parent_surface = parent_surface;
+
+ /* If parent is a layer surface, compute placement rule from stored positioner data */
+ if (META_IS_WAYLAND_LAYER_SURFACE (parent_surface->role))
+ {
+ MetaSurfaceActor *surface_actor;
+ ClutterActor *actor;
+ float parent_x, parent_y;
+ float parent_width, parent_height;
+ MetaPlacementRule *rule = &xdg_popup->setup.placement_rule;
+
+ surface_actor = meta_wayland_surface_get_actor (parent_surface);
+ actor = CLUTTER_ACTOR (surface_actor);
+ clutter_actor_get_position (actor, &parent_x, &parent_y);
+ clutter_actor_get_size (actor, &parent_width, &parent_height);
+
+ /* Build placement rule from stored positioner data (window-local coordinates).
+ * parent_rect provides the global offset - don't add it to anchor_rect too. */
+ rule->anchor_rect.x = xdg_popup->setup.anchor_rect.x;
+ rule->anchor_rect.y = xdg_popup->setup.anchor_rect.y;
+ rule->anchor_rect.width = xdg_popup->setup.anchor_rect.width;
+ rule->anchor_rect.height = xdg_popup->setup.anchor_rect.height;
+ rule->width = xdg_popup->setup.width;
+ rule->height = xdg_popup->setup.height;
+ rule->anchor = positioner_anchor_to_placement_anchor (xdg_popup->setup.anchor);
+ rule->gravity = positioner_gravity_to_placement_gravity (xdg_popup->setup.gravity);
+ rule->constraint_adjustment = xdg_popup->setup.constraint_adjustment;
+ rule->offset_x = xdg_popup->setup.offset_x;
+ rule->offset_y = xdg_popup->setup.offset_y;
+ rule->is_reactive = FALSE;
+
+ /* Set parent_rect to the layer surface bounds */
+ rule->parent_rect.x = (int)parent_x;
+ rule->parent_rect.y = (int)parent_y;
+ rule->parent_rect.width = (int)parent_width;
+ rule->parent_rect.height = (int)parent_height;
+ }
+}
+
void
meta_wayland_xdg_shell_init (MetaWaylandCompositor *compositor)
{
diff --git a/src/wayland/meta-wayland-xdg-shell.h b/src/wayland/meta-wayland-xdg-shell.h
index f90e29bea..5631aed44 100644
--- a/src/wayland/meta-wayland-xdg-shell.h
+++ b/src/wayland/meta-wayland-xdg-shell.h
@@ -50,4 +50,7 @@ G_DECLARE_FINAL_TYPE (MetaWaylandXdgPopup,
void meta_wayland_xdg_shell_init (MetaWaylandCompositor *compositor);
+void meta_wayland_xdg_popup_set_parent_surface (MetaWaylandXdgPopup *xdg_popup,
+ MetaWaylandSurface *parent_surface);
+
#endif /* META_WAYLAND_XDG_SHELL_H */
diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c
index 10d7f0127..bd8c90a50 100644
--- a/src/wayland/meta-window-wayland.c
+++ b/src/wayland/meta-window-wayland.c
@@ -483,7 +483,7 @@ meta_window_wayland_update_main_monitor (MetaWindow *window,
/* If the window is not a toplevel window (i.e. it's a popup window) just use
* the monitor of the toplevel. */
toplevel_window = meta_wayland_surface_get_toplevel_window (window->surface);
- if (toplevel_window != window)
+ if (toplevel_window && toplevel_window != window)
{
meta_window_update_monitor (toplevel_window, flags);
window->monitor = toplevel_window->monitor;
@@ -976,8 +976,17 @@ meta_window_wayland_finish_move_resize (MetaWindow *window,
MetaWindow *parent;
parent = meta_window_get_transient_for (window);
- rect.x = parent->rect.x + acked_configuration->rel_x;
- rect.y = parent->rect.y + acked_configuration->rel_y;
+ if (parent)
+ {
+ rect.x = parent->rect.x + acked_configuration->rel_x;
+ rect.y = parent->rect.y + acked_configuration->rel_y;
+ }
+ else
+ {
+ /* Layer-shell popups have no parent MetaWindow, use placement rule's parent_rect */
+ rect.x = window->placement.rule->parent_rect.x + acked_configuration->rel_x;
+ rect.y = window->placement.rule->parent_rect.y + acked_configuration->rel_y;
+ }
}
else if (acked_configuration->has_position)
{
@@ -1059,6 +1068,9 @@ meta_window_place_with_placement_rule (MetaWindow *window,
META_GRAVITY_NORTH_WEST,
window->unconstrained_rect);
window->calc_placement = FALSE;
+
+ /* Mark as placed so meta_window_force_placement won't override our position */
+ window->placed = TRUE;
}
void
diff --git a/src/wayland/protocol/wlr-layer-shell-unstable-v1.xml b/src/wayland/protocol/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 000000000..6998081ec
--- /dev/null
+++ b/src/wayland/protocol/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,406 @@
+
+
+
+ Copyright © 2017 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+
+ Clients can use this interface to assign the surface_layer role to
+ wl_surfaces. Such surfaces are assigned to a "layer" of the output and
+ rendered with a defined z-depth respective to each other. They may also be
+ anchored to the edges and corners of a screen and specify input handling
+ semantics. This interface should be suitable for the implementation of
+ many desktop shell components, and a broad number of other applications
+ that interact with the desktop.
+
+
+
+
+ Create a layer surface for an existing surface. This assigns the role of
+ layer_surface, or raises a protocol error if another role is already
+ assigned.
+
+ Creating a layer surface from a wl_surface which has a buffer attached
+ or committed is a client error, and any attempts by a client to attach
+ or manipulate a buffer prior to the first layer_surface.configure call
+ must also be treated as errors.
+
+ After creating a layer_surface object and setting it up, the client
+ must perform an initial commit without any buffer attached.
+ The compositor will reply with a layer_surface.configure event.
+ The client must acknowledge it and is then allowed to attach a buffer
+ to map the surface.
+
+ You may pass NULL for output to allow the compositor to decide which
+ output to use. Generally this will be the one that the user most
+ recently interacted with.
+
+ Clients can specify a namespace that defines the purpose of the layer
+ surface.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ These values indicate which layers a surface can be rendered in. They
+ are ordered by z depth, bottom-most first. Traditional shell surfaces
+ will typically be rendered between the bottom and top layers.
+ Fullscreen shell surfaces are typically rendered at the top layer.
+ Multiple surfaces can share a single layer, and ordering within a
+ single layer is undefined.
+
+
+
+
+
+
+
+
+
+
+
+
+ This request indicates that the client will not use the layer_shell
+ object any more. Objects that have been created through this instance
+ are not affected.
+
+
+
+
+
+
+ An interface that may be implemented by a wl_surface, for surfaces that
+ are designed to be rendered as a layer of a stacked desktop-like
+ environment.
+
+ Layer surface state (layer, size, anchor, exclusive zone,
+ margin, interactivity) is double-buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ Attaching a null buffer to a layer surface unmaps it.
+
+ Unmapping a layer_surface means that the surface cannot be shown by the
+ compositor until it is explicitly mapped again. The layer_surface
+ returns to the state it had right after layer_shell.get_layer_surface.
+ The client can re-map the surface by performing a commit without any
+ buffer attached, waiting for a configure event and handling it as usual.
+
+
+
+
+ Sets the size of the surface in surface-local coordinates. The
+ compositor will display the surface centered with respect to its
+ anchors.
+
+ If you pass 0 for either value, the compositor will assign it and
+ inform you of the assignment in the configure event. You must set your
+ anchor to opposite edges in the dimensions you omit; not doing so is a
+ protocol error. Both values are 0 by default.
+
+ Size is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+
+ Requests that the compositor anchor the surface to the specified edges
+ and corners. If two orthogonal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left corner of the output); otherwise the anchor point
+ will be centered on that edge, or in the center if none is specified.
+
+ Anchor is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+ Requests that the compositor avoids occluding an area with other
+ surfaces. The compositor's use of this information is
+ implementation-dependent - do not assume that this region will not
+ actually be occluded.
+
+ A positive value is only meaningful if the surface is anchored to one
+ edge or an edge and both perpendicular edges. If the surface is not
+ anchored, anchored to only two perpendicular edges (a corner), anchored
+ to only two parallel edges or anchored to all edges, a positive value
+ will be treated the same as zero.
+
+ A positive zone is the distance from the edge in surface-local
+ coordinates to consider exclusive.
+
+ Surfaces that do not wish to have an exclusive zone may instead specify
+ how they should interact with surfaces that do. If set to zero, the
+ surface indicates that it would like to be moved to avoid occluding
+ surfaces with a positive exclusive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accommodate for other
+ surfaces, and the compositor should extend it all the way to the edges
+ it is anchored to.
+
+ For example, a panel might set its exclusive zone to 10, so that
+ maximized shell surfaces are not shown on top of it. A notification
+ might set its exclusive zone to 0, so that it is moved to avoid
+ occluding the panel, but shell surfaces are shown underneath it. A
+ wallpaper or lock screen might set their exclusive zone to -1, so that
+ they stretch below or over the panel.
+
+ The default value is 0.
+
+ Exclusive zone is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+ Requests that the surface be placed some distance away from the anchor
+ point on the output, in surface-local coordinates. Setting this value
+ for edges you are not anchored to has no effect.
+
+ The exclusive zone includes the margin.
+
+ Margin is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+
+
+
+ Types of keyboard interaction possible for layer shell surfaces. The
+ rationale for this is twofold: (1) some applications are not interested
+ in keyboard events and not allowing them to be focused can improve the
+ desktop experience; (2) some applications will want to take exclusive
+ keyboard focus.
+
+
+
+
+ This value indicates that this surface is not interested in keyboard
+ events and the compositor should never assign it the keyboard focus.
+
+ This is the default value, set for newly created layer shell surfaces.
+
+ This is useful for e.g. desktop widgets that display information or
+ only have interaction with non-keyboard input devices.
+
+
+
+
+ Request exclusive keyboard focus if this surface is above the shell surface layer.
+
+ For the top and overlay layers, the seat will always give
+ exclusive keyboard focus to the top-most layer which has keyboard
+ interactivity set to exclusive. If this layer contains multiple
+ surfaces with keyboard interactivity set to exclusive, the compositor
+ determines the one receiving keyboard events in an implementation-
+ defined manner. In this case, no guarantee is made when this surface
+ will receive keyboard focus (if ever).
+
+ For the bottom and background layers, the compositor is allowed to use
+ normal focus semantics.
+
+ This setting is mainly intended for applications that need to ensure
+ they receive all keyboard events, such as a lock screen or a password
+ prompt.
+
+
+
+
+ This requests the compositor to allow this surface to be focused and
+ unfocused by the user in an implementation-defined manner. The user
+ should be able to unfocus this surface even regardless of the layer
+ it is on.
+
+ Typically, the compositor will want to use its normal mechanism to
+ manage keyboard focus between layer shell surfaces with this setting
+ and regular toplevels on the desktop layer (e.g. click to focus).
+ Nevertheless, it is possible for a compositor to require a special
+ interaction to focus or unfocus layer shell surfaces (e.g. requiring
+ a click even if focus follows the mouse normally, or providing a
+ keybinding to switch focus between layers).
+
+ This setting is mainly intended for desktop shell components (e.g.
+ panels) that allow keyboard interaction. Using this option can allow
+ implementing a desktop shell that can be fully usable without the
+ mouse.
+
+
+
+
+
+
+ Set how keyboard events are delivered to this surface. By default,
+ layer shell surfaces do not receive keyboard events; this request can
+ be used to change this.
+
+ This setting is inherited by child surfaces set by the get_popup
+ request.
+
+ Layer surfaces receive pointer, touch, and tablet events normally. If
+ you do not want to receive them, set the input region on your surface
+ to an empty region.
+
+ Keyboard interactivity is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+ This assigns an xdg_popup's parent to this layer_surface. This popup
+ should have been created via xdg_surface::get_popup with the parent set
+ to NULL, and this request must be invoked before committing the popup's
+ initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+
+
+
+
+
+
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+
+
+
+
+
+
+ This request destroys the layer surface.
+
+
+
+
+
+ The configure event asks the client to resize its surface.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ The client is free to dismiss all but the last configure event it
+ received.
+
+ The width and height arguments specify the size of the window in
+ surface-local coordinates.
+
+ The size is a hint, in the sense that the client is free to ignore it if
+ it doesn't resize, pick a smaller size (to satisfy aspect ratio or
+ resize in steps of NxM pixels). If the client picks a smaller size and
+ is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
+ surface will be centered on this axis.
+
+ If the width or height arguments are zero, it means the client should
+ decide its own window dimension.
+
+
+
+
+
+
+
+
+ The closed event is sent by the compositor when the surface will no
+ longer be shown. The output may have been destroyed or the user may
+ have asked for it to be removed. Further changes to the surface will be
+ ignored. The client should destroy the resource after receiving this
+ event, and create a new surface if they so choose.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Change the layer that the surface is rendered on.
+
+ Layer is double-buffered, see wl_surface.commit.
+
+
+
+
+
+
+
+
+ Requests an edge for the exclusive zone to apply. The exclusive
+ edge will be automatically deduced from anchor points when possible,
+ but when the surface is anchored to a corner, it will be necessary
+ to set it explicitly to disambiguate, as it is not possible to deduce
+ which one of the two corner edges should be used.
+
+ The edge must be one the surface is anchored to, otherwise the
+ invalid_exclusive_edge protocol error will be raised.
+
+
+
+
+
diff --git a/src/x11/meta-x11-background-actor.c b/src/x11/meta-x11-background-actor.c
index 4361e1936..83db205d0 100644
--- a/src/x11/meta-x11-background-actor.c
+++ b/src/x11/meta-x11-background-actor.c
@@ -77,6 +77,7 @@ typedef struct
cairo_region_t *visible_region;
float dim_factor;
gboolean transition_running;
+ int monitor_index;
} MetaX11BackgroundActorPrivate;
struct _MetaX11BackgroundActor
@@ -379,9 +380,19 @@ meta_x11_background_actor_get_preferred_width (ClutterActor *actor,
{
MetaX11BackgroundActor *self = META_X11_BACKGROUND_ACTOR (actor);
MetaX11BackgroundActorPrivate *priv = self->priv;
- int width, height;
+ int width;
- meta_display_get_size (priv->background->display, &width, &height);
+ if (priv->monitor_index >= 0)
+ {
+ MetaRectangle rect;
+ meta_display_get_monitor_geometry (priv->background->display, priv->monitor_index, &rect);
+ width = rect.width;
+ }
+ else
+ {
+ int height;
+ meta_display_get_size (priv->background->display, &width, &height);
+ }
if (min_width_p)
*min_width_p = width;
@@ -398,9 +409,19 @@ meta_x11_background_actor_get_preferred_height (ClutterActor *actor,
{
MetaX11BackgroundActor *self = META_X11_BACKGROUND_ACTOR (actor);
MetaX11BackgroundActorPrivate *priv = self->priv;
- int width, height;
+ int height;
- meta_display_get_size (priv->background->display, &width, &height);
+ if (priv->monitor_index >= 0)
+ {
+ MetaRectangle rect;
+ meta_display_get_monitor_geometry (priv->background->display, priv->monitor_index, &rect);
+ height = rect.height;
+ }
+ else
+ {
+ int width;
+ meta_display_get_size (priv->background->display, &width, &height);
+ }
if (min_height_p)
*min_height_p = height;
@@ -416,7 +437,17 @@ meta_x11_background_actor_get_paint_volume (ClutterActor *actor,
MetaX11BackgroundActorPrivate *priv = self->priv;
int width, height;
- meta_display_get_size (priv->background->display, &width, &height);
+ if (priv->monitor_index >= 0)
+ {
+ MetaRectangle rect;
+ meta_display_get_monitor_geometry (priv->background->display, priv->monitor_index, &rect);
+ width = rect.width;
+ height = rect.height;
+ }
+ else
+ {
+ meta_display_get_size (priv->background->display, &width, &height);
+ }
clutter_paint_volume_set_width (volume, width);
clutter_paint_volume_set_height (volume, height);
@@ -516,6 +547,7 @@ meta_x11_background_actor_init (MetaX11BackgroundActor *self)
self->priv = meta_x11_background_actor_get_instance_private (self);
self->priv->dim_factor = 1.0;
self->priv->transition_running = FALSE;
+ self->priv->monitor_index = -1;
}
/**
@@ -554,6 +586,53 @@ meta_x11_background_actor_new_for_display (MetaDisplay *display)
return CLUTTER_ACTOR (self);
}
+/**
+ * meta_x11_background_actor_new_for_monitor:
+ * @display: the #MetaDisplay
+ * @monitor: the monitor index
+ *
+ * Creates a new actor to draw the background for the given monitor.
+ * The actor is sized to the monitor geometry and clips the root pixmap
+ * texture so that only the portion corresponding to this monitor is visible.
+ *
+ * Return value: (transfer none): the newly created background actor
+ */
+ClutterActor *
+meta_x11_background_actor_new_for_monitor (MetaDisplay *display,
+ int monitor)
+{
+ MetaX11BackgroundActor *self;
+ MetaX11BackgroundActorPrivate *priv;
+ MetaRectangle rect;
+
+ g_return_val_if_fail (META_IS_DISPLAY (display), NULL);
+
+ if (meta_is_wayland_compositor ())
+ return NULL;
+
+ self = g_object_new (META_TYPE_X11_BACKGROUND_ACTOR, NULL);
+ priv = self->priv;
+
+ priv->monitor_index = monitor;
+ priv->background = meta_display_background_get (display);
+ priv->background->actors = g_slist_prepend (priv->background->actors, self);
+
+ priv->bottom_actor = meta_x11_background_new (display);
+ clutter_actor_add_child (CLUTTER_ACTOR (self), priv->bottom_actor);
+ priv->top_actor = meta_x11_background_new (display);
+ clutter_actor_add_child (CLUTTER_ACTOR (self), priv->top_actor);
+
+ meta_display_get_monitor_geometry (display, monitor, &rect);
+ clutter_actor_set_position (priv->bottom_actor, -rect.x, -rect.y);
+ clutter_actor_set_position (priv->top_actor, -rect.x, -rect.y);
+ clutter_actor_set_clip_to_allocation (CLUTTER_ACTOR (self), TRUE);
+
+ set_texture_on_actors (self);
+ update_wrap_mode_of_actor (self);
+
+ return CLUTTER_ACTOR (self);
+}
+
/**
* meta_x11_background_actor_update:
* @display: a #MetaDisplay