1
0
Fork 0
mutter-performance-source/src/compositor/meta-background.c
Sebastian Wick 5dc92aa134 Revert "background: Make the final monitor texture RGB instead of RGBA"
This reverts commit 93e938e22f.

The alpha channel is required for the background-color to show where the
background-image is not visible.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1911
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3459>
2023-12-19 12:30:19 +00:00

1021 lines
32 KiB
C

/* -*- 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "compositor/meta-background-private.h"
#include <string.h>
#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;
GDesktopBackgroundStyle style;
GDesktopBackgroundShading 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];
g_clear_object (&monitor->fbo);
g_clear_object (&monitor->texture);
}
}
static void
free_color_texture (MetaBackground *self)
{
g_clear_object (&self->color_texture);
}
static void
free_wallpaper_texture (MetaBackground *self)
{
g_clear_object (&self->wallpaper_texture);
self->wallpaper_allocation_failed = FALSE;
}
static void
invalidate_monitor_backgrounds (MetaBackground *self)
{
free_fbos (self);
g_clear_pointer (&self->monitors, g_free);
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 == G_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_clear_object (imagep);
}
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);
MetaContext *context = meta_display_get_context (self->display);
MetaBackend *backend = meta_context_get_backend (context);
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
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", NULL, NULL,
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 (MtkRectangle *monitor_area,
MtkRectangle *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,
MtkRectangle *monitor_rect,
float monitor_scale,
CoglTexture *texture,
MtkRectangle *texture_area)
{
MtkRectangle 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 G_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 G_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 G_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 G_DESKTOP_BACKGROUND_STYLE_SCALED:
case G_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 == G_DESKTOP_BACKGROUND_STYLE_SCALED &&
(monitor_x_scale < monitor_y_scale)) ||
(self->style == G_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 G_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,
MtkRectangle *monitor_area,
float monitor_scale)
{
MtkRectangle texture_area;
gboolean bare_region_visible;
get_texture_area (self, monitor_area, monitor_scale, texture, &texture_area);
switch (self->style)
{
case G_DESKTOP_BACKGROUND_STYLE_STRETCHED:
case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
case G_DESKTOP_BACKGROUND_STYLE_ZOOM:
case G_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 G_DESKTOP_BACKGROUND_STYLE_CENTERED:
case G_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 (MtkRectangle)) != 0;
break;
case G_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 == G_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 G_DESKTOP_BACKGROUND_SHADING_VERTICAL:
width = 1;
height = 2;
break;
case G_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_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", 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);
g_clear_object (&self->wallpaper_texture);
g_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.);
g_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);
g_object_unref (pipeline);
}
g_object_unref (fbo);
}
return self->wallpaper_texture != NULL;
}
static CoglPipelineWrapMode
get_wrap_mode (GDesktopBackgroundStyle style)
{
switch (style)
{
case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
return COGL_PIPELINE_WRAP_MODE_REPEAT;
case G_DESKTOP_BACKGROUND_STYLE_NONE:
case G_DESKTOP_BACKGROUND_STYLE_STRETCHED:
case G_DESKTOP_BACKGROUND_STYLE_CENTERED:
case G_DESKTOP_BACKGROUND_STYLE_SCALED:
case G_DESKTOP_BACKGROUND_STYLE_ZOOM:
case G_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,
MtkRectangle *texture_area,
CoglPipelineWrapMode *wrap_mode)
{
MetaBackgroundMonitor *monitor;
MtkRectangle geometry;
MtkRectangle 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 == G_DESKTOP_BACKGROUND_STYLE_WALLPAPER &&
self->shading_direction == G_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)
{
MetaContext *context = meta_display_get_context (self->display);
MetaBackend *backend = meta_context_get_backend (context);
GError *catch_error = NULL;
gboolean bare_region_visible = FALSE;
int texture_width, texture_height;
if (meta_backend_is_stage_views_scaled (backend))
{
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 != G_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.)
*/
g_clear_object (&monitor->texture);
g_clear_object (&monitor->fbo);
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);
g_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);
g_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);
g_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,
G_DESKTOP_BACKGROUND_SHADING_SOLID,
color, &dummy);
}
void
meta_background_set_gradient (MetaBackground *self,
GDesktopBackgroundShading 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,
GDesktopBackgroundStyle 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,
GDesktopBackgroundStyle 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);
}