From 297c23884695544d354df301559a0b43b76ded9a Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Mon, 2 Mar 2026 09:43:28 -0500 Subject: [PATCH] Support wlr-layer-shell-protocol. Extracted from https://github.com/linuxmint/muffin/pull/784 This is primarily to enable nemo-desktop and csd-background as to run as gtk-layer-shell clients and integrates their windows into the compositor stage. Immediate benefits: - csd-background can run as a wayland client (currently uses xwayland), and its surfaces can be cloned 'for free' to be re-used in Cinnamon's screensaver, alt-tab, expo modes. - nemo-desktop can run as a wayland client (also currently xwayland) and be automatically contained in a monitor's workarea without relying on Cinnamon to inform it. - Remove existing never-used background code. - Add new method to create per-monitor backgrounds. - Add new method to access stage background actors. --- clutter/clutter/cogl/clutter-stage-cogl.c | 2 +- debian/libmuffin0.symbols | 28 +- src/compositor/compositor.c | 251 ++- .../meta-background-actor-private.h | 10 - src/compositor/meta-background-actor.c | 1014 ------------ src/compositor/meta-background-group.c | 66 - src/compositor/meta-background-image.c | 370 ----- src/compositor/meta-background-private.h | 14 - src/compositor/meta-background.c | 1038 ------------ src/compositor/meta-surface-actor.c | 4 +- src/compositor/plugins/default.c | 36 +- src/core/constraints.c | 6 +- src/core/window.c | 19 + src/core/workspace-private.h | 9 + src/core/workspace.c | 99 ++ src/meson.build | 10 +- src/meta/compositor-muffin.h | 7 + src/meta/meson.build | 10 +- src/meta/meta-background-actor.h | 70 - src/meta/meta-background-group.h | 26 - src/meta/meta-background-image.h | 69 - src/meta/meta-background.h | 74 - src/meta/meta-wayland-background-actor.h | 38 + src/meta/meta-x11-background-actor.h | 4 + src/wayland/meta-wayland-background-actor.c | 372 +++++ src/wayland/meta-wayland-layer-shell.c | 1419 +++++++++++++++++ src/wayland/meta-wayland-layer-shell.h | 62 + src/wayland/meta-wayland-outputs.c | 36 + src/wayland/meta-wayland-outputs.h | 3 + src/wayland/meta-wayland-pointer.c | 35 +- src/wayland/meta-wayland-shell-surface.c | 12 +- src/wayland/meta-wayland-surface.c | 5 +- src/wayland/meta-wayland-versions.h | 1 + src/wayland/meta-wayland-xdg-shell.c | 154 +- src/wayland/meta-wayland-xdg-shell.h | 3 + src/wayland/meta-window-wayland.c | 18 +- .../protocol/wlr-layer-shell-unstable-v1.xml | 406 +++++ src/x11/meta-x11-background-actor.c | 89 +- 38 files changed, 2970 insertions(+), 2919 deletions(-) delete mode 100644 src/compositor/meta-background-actor-private.h delete mode 100644 src/compositor/meta-background-actor.c delete mode 100644 src/compositor/meta-background-group.c delete mode 100644 src/compositor/meta-background-image.c delete mode 100644 src/compositor/meta-background-private.h delete mode 100644 src/compositor/meta-background.c delete mode 100644 src/meta/meta-background-actor.h delete mode 100644 src/meta/meta-background-group.h delete mode 100644 src/meta/meta-background-image.h delete mode 100644 src/meta/meta-background.h create mode 100644 src/meta/meta-wayland-background-actor.h create mode 100644 src/wayland/meta-wayland-background-actor.c create mode 100644 src/wayland/meta-wayland-layer-shell.c create mode 100644 src/wayland/meta-wayland-layer-shell.h create mode 100644 src/wayland/protocol/wlr-layer-shell-unstable-v1.xml 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