/*
*
* LiveWallpaper
*
* 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 3 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 .
*
* Copyright (C) 2012-2016 Maximilian Schnarr
*
*/
/**
* SECTION: background
* @Short_description: background image
*
* The #LwBackground object represents a static background image or color gradient.
*
* To use #LwBackground a plugin has to specify a default background. There are two
* types of default backgrounds:
*
*
* An image (lw_background_new_from_file())
*
*
* A color gradient (lw_background_new_from_colors())
*
*
* Then the background is ready to be drawn. Use lw_background_draw() to draw the
* background to the current output. This is usually done before all other things
* will be drawn.
*
* In addition to this, LwBackground provides support for custom background
* images. If the user checked the "use-custom-bg" key inside the configurator,
* #LwBackground will show the user defined background image instead of the default
* background. It is not possible to deactivate this behaviour at the moment.
*/
#include
#include
/**
* LwBackgroundRenderType:
* @LwBackgroundZoom: Scales the image to fill the whole screen, no borders
* @LwBackgroundCentered: Puts the image in the center and keeps original size
* @LwBackgroundScaled: Scales the image to be completely on the screen, with borders
* @LwBackgroundStretched: Stretches the image to fill the whole screen, won't keep the
* aspect ratio. This is the value if "Fill" is chosen inside
* the configurator.
* @LwBackgroundTiled: Tiles the image and keeps original size
*
* All supported types to render a background image.
*/
/**
* LwBackgroundShadingType:
* @LwBackgroundHorizontal: Horizontal color gradient
* @LwBackgroundVertical: Vertical color gradient
* @LwBackgroundSolid: Solid color
*
* All supported shading types of #LwBackground.
*/
typedef enum
{
LwBackgroundDefault,
LwBackgroundCustomImage,
LwBackgroundSingleColor,
LwBackgroundHorizontalGradient,
LwBackgroundVerticalGradient
} LwBackgroundMode;
struct _LwBackgroundPrivate
{
GSettings *settings;
LwBackgroundMode bg_mode;
LwTexture *bg_texture;
LwBackgroundRenderType bg_render_type;
LwTexture *default_bg;
LwBackgroundRenderType default_render_type;
gchar *image;
LwBackgroundRenderType render_type;
GdkRGBA primary_color;
GdkRGBA secondary_color;
};
enum
{
PROP_0,
PROP_IMAGE,
PROP_RENDER_TYPE,
PROP_PRIMARY_COLOR,
PROP_SECONDARY_COLOR,
N_PROPERTIES
};
static GParamSpec *obj_properties[N_PROPERTIES] = {NULL, };
/**
* LwBackground:
*
* This structure holds all required information to render a background image.
*/
G_DEFINE_TYPE(LwBackground, lw_background, G_TYPE_OBJECT)
static void lw_background_changed(GSettings *settings, G_GNUC_UNUSED gchar *key, LwBackground *self);
/**
* lw_background_new_from_file:
* @path: Name of the default background image
* @type: #LwBackgroundRenderType to use for default background image
*
* Creates a new #LwBackground with an default background image.
* Note that the background you set won't be necessary the one displayed,
* it can be overrided by the user's configuration.
*
* Returns: A new #LwBackground. Use g_object_unref() to free the #LwBackground.
*
* Since: 0.5
*/
LwBackground*
lw_background_new_from_file (const gchar *path, LwBackgroundRenderType type)
{
LwTexture *texture = lw_texture_new_from_file(path);
return lw_background_new_from_texture(texture, type);
}
/**
* lw_background_new_from_resource:
* @path: Name of the default background image
* @type: #LwBackgroundRenderType to use for default background image
*
* Creates a new #LwBackground with an default background image.
* Note that the background you set won't be necessary the one displayed,
* it can be overrided by the user's configuration.
*
* Returns: A new #LwBackground. Use g_object_unref() to free the #LwBackground.
*
* Since: 0.5
*/
LwBackground*
lw_background_new_from_resource (const gchar *path,
LwBackgroundRenderType type)
{
LwTexture *texture = lw_texture_new_from_resource (path);
return lw_background_new_from_texture(texture, type);
}
static LwTexture*
create_texture_from_colors(GdkColor *primary_color,
GdkColor *secondary_color,
LwBackgroundShadingType type)
{
LwTexture *texture;
guint width, height;
GLushort data[8];
data[0] = primary_color->red;
data[1] = primary_color->green;
data[2] = primary_color->blue;
data[3] = 65535;
data[4] = secondary_color->red;
data[5] = secondary_color->green;
data[6] = secondary_color->blue;
data[7] = 65535;
switch(type)
{
case LwBackgroundHorizontal:
width = 2;
height = 1;
break;
case LwBackgroundVertical:
width = 1;
height = 2;
break;
case LwBackgroundSolid:
default:
width = 1;
height = 1;
}
texture = lw_texture_new_from_data((guchar *) data, width, height, GL_RGBA, GL_UNSIGNED_SHORT);
lw_texture_set_filter(texture, GL_LINEAR);
return texture;
}
static LwTexture*
create_texture_from_rgba(GdkRGBA *primary_rgba,
GdkRGBA *secondary_rgba,
LwBackgroundShadingType type)
{
GdkColor primary_color, secondary_color;
primary_color.red = primary_rgba->red * 65535;
primary_color.green = primary_rgba->green * 65535;
primary_color.blue = primary_rgba->blue * 65535;
secondary_color.red = secondary_rgba->red * 65535;
secondary_color.green = secondary_rgba->green * 65535;
secondary_color.blue = secondary_rgba->blue * 65535;
return create_texture_from_colors(&primary_color, &secondary_color, type);
}
/**
* lw_background_new_from_colors:
* @primary_color: Left or bottom color when drawing gradients, or the solid color
* @secondary_color: Right or top color when drawing gradients, not used for solid color
* @type: The type of color gradient
*
* Creates a new #LwBackground with an color gradient as default background.
*
* Returns: A new #LwBackground. Use g_object_unref() to free the #LwBackground.
*
* Since: 0.5
*/
LwBackground*
lw_background_new_from_colors(GdkColor *primary_color,
GdkColor *secondary_color,
LwBackgroundShadingType type)
{
LwTexture *texture = create_texture_from_colors(primary_color, secondary_color, type);
return lw_background_new_from_texture(texture, LwBackgroundStretched);
}
/**
* lw_background_new_from_texture:
* @texture: The #LwTexture to use as default background
* @type: #LwBackgroundRenderType to use for default background
*
* Creates a new #LwBackground from a #LwTexture.
*
* Returns: A new #LwBackground. Use g_object_unref() to free the #LwBackground.
*
* Since: 0.5
*/
LwBackground*
lw_background_new_from_texture(LwTexture *texture,
LwBackgroundRenderType type)
{
LwBackground *background = g_object_new(LW_TYPE_BACKGROUND, NULL);
background->priv->default_bg = texture;
background->priv->default_render_type = type;
lw_background_changed(background->priv->settings, "default-bg", background);
return background;
}
static void
lw_background_set_property(GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
LwBackground *self = LW_BACKGROUND(object);
switch(property_id)
{
case PROP_IMAGE:
g_free(self->priv->image);
self->priv->image = g_strdup(g_value_get_string(value));
if(self->priv->bg_mode == LwBackgroundCustomImage)
lw_background_changed(self->priv->settings, "bg-image", self);
break;
case PROP_RENDER_TYPE:
self->priv->render_type = g_value_get_uint(value);
if(self->priv->bg_mode == LwBackgroundCustomImage)
self->priv->bg_render_type = self->priv->render_type;
break;
case PROP_PRIMARY_COLOR:
self->priv->primary_color = *((GdkRGBA*) g_value_get_boxed(value));
if(self->priv->bg_mode == LwBackgroundSingleColor ||
self->priv->bg_mode == LwBackgroundHorizontalGradient ||
self->priv->bg_mode == LwBackgroundVerticalGradient)
lw_background_changed(self->priv->settings, "bg-primary-color", self);
break;
case PROP_SECONDARY_COLOR:
self->priv->secondary_color = *((GdkRGBA*) g_value_get_boxed(value));
if(self->priv->bg_mode == LwBackgroundHorizontalGradient ||
self->priv->bg_mode == LwBackgroundVerticalGradient)
lw_background_changed(self->priv->settings, "bg-secondary-color", self);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void
lw_background_get_property(GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
LwBackground *self = LW_BACKGROUND(object);
switch(property_id)
{
case PROP_IMAGE:
g_value_set_string(value, self->priv->image);
break;
case PROP_RENDER_TYPE:
g_value_set_uint(value, self->priv->render_type);
break;
case PROP_PRIMARY_COLOR:
g_value_set_boxed(value, &self->priv->primary_color);
break;
case PROP_SECONDARY_COLOR:
g_value_set_boxed(value, &self->priv->secondary_color);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
/**
* lw_background_draw:
* @self: A #LwBackground
* @output: The active output
*
* Draws the background to the active output.
*/
void
lw_background_draw(LwBackground *self, LwOutput *output)
{
/* Use gint instead of guint! */
gint width = lw_output_get_width(output),
height = lw_output_get_height(output);
gint tex_width, tex_height;
LwTexture* tex = self->priv->bg_texture;
LwBackgroundRenderType type = self->priv->bg_render_type;
LwTextureMatrix m;
if(tex == NULL && self->priv->default_bg != NULL)
{
/* Fallback to default background */
tex = self->priv->default_bg;
type = self->priv->default_render_type;
}
else if(tex == NULL) return;
tex_width = lw_texture_get_width(tex);
tex_height = lw_texture_get_height(tex);
/* Save and reset the coordinate system for drawing the background. */
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, width, height, 0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
/* Apply render type */
m = tex->matrix;
switch(type)
{
case LwBackgroundCentered:
m.x0 -= ((width - tex_width) / 2) * m.xx;
m.y0 -= ((height - tex_height) / 2) * m.yy;
lw_texture_set_wrap(tex, GL_CLAMP_TO_EDGE);
break;
case LwBackgroundScaled:
{
gfloat s = MIN((gfloat) width / tex_width,
(gfloat) height / tex_height);
m.xx /= s;
m.yy /= s;
m.x0 -= ((width - (tex_width * s)) / 2) * m.xx;
m.y0 -= ((height - (tex_height * s)) / 2) * m.yy;
lw_texture_set_wrap(tex, GL_CLAMP_TO_EDGE);
break;
}
case LwBackgroundStretched:
m.xx /= (gfloat) width / tex_width;
m.yy /= (gfloat) height / tex_height;
lw_texture_set_wrap(tex, GL_CLAMP_TO_EDGE);
break;
case LwBackgroundTiled:
lw_texture_set_wrap(tex, GL_REPEAT);
break;
case LwBackgroundZoom:
default:
{
gfloat s = MAX((gfloat) width / tex_width,
(gfloat) height / tex_height);
m.xx /= s;
m.yy /= s;
m.x0 -= ((width - (tex_width * s)) / 2) * m.xx;
m.y0 -= ((height - (tex_height * s)) / 2) * m.yy;
lw_texture_set_wrap(tex, GL_CLAMP_TO_EDGE);
}
}
/* Draw background */
glColor3f(1.0f, 1.0f, 1.0f);
lw_texture_enable(tex);
glBegin(GL_QUADS);
glTexCoord2f(
LW_TEX_COORD_X(m, 0.0f),
LW_TEX_COORD_Y(m, 0.0f)
);
glVertex2f(0.0f, 0.0f);
glTexCoord2f(
LW_TEX_COORD_X(m, 0.0f),
LW_TEX_COORD_Y(m, height)
);
glVertex2f(0.0f, height);
glTexCoord2f(
LW_TEX_COORD_X(m, width),
LW_TEX_COORD_Y(m, height)
);
glVertex2f(width, height);
glTexCoord2f(
LW_TEX_COORD_X(m, width),
LW_TEX_COORD_Y(m, 0.0f)
);
glVertex2f(width, 0.0f);
glEnd();
lw_texture_disable(tex);
/* Restore the coordinate system. */
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
static void
lw_background_changed(GSettings *settings, G_GNUC_UNUSED gchar *key, LwBackground *self)
{
g_clear_object(&self->priv->bg_texture);
self->priv->bg_mode = g_settings_get_enum(settings, "bg-mode");
switch(self->priv->bg_mode)
{
case LwBackgroundCustomImage:
{
/* Load image */
if (self->priv->image[0] != '\0')
self->priv->bg_texture = lw_texture_new_from_file(self->priv->image);
self->priv->bg_render_type = self->priv->render_type;
break;
}
case LwBackgroundSingleColor:
self->priv->bg_texture = create_texture_from_rgba(&self->priv->primary_color,
&self->priv->secondary_color,
LwBackgroundSolid);
self->priv->bg_render_type = LwBackgroundStretched;
break;
case LwBackgroundHorizontalGradient:
self->priv->bg_texture = create_texture_from_rgba(&self->priv->primary_color,
&self->priv->secondary_color,
LwBackgroundHorizontal);
self->priv->bg_render_type = LwBackgroundStretched;
break;
case LwBackgroundVerticalGradient:
self->priv->bg_texture = create_texture_from_rgba(&self->priv->primary_color,
&self->priv->secondary_color,
LwBackgroundVertical);
self->priv->bg_render_type = LwBackgroundStretched;
break;
case LwBackgroundDefault:
default:
self->priv->bg_texture = self->priv->default_bg;
if(self->priv->default_bg)
g_object_ref(self->priv->default_bg);
self->priv->bg_render_type = self->priv->default_render_type;
}
}
static void
lw_background_init(LwBackground *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, LW_TYPE_BACKGROUND,
LwBackgroundPrivate);
self->priv->settings = g_settings_new("net.launchpad.livewallpaper");
self->priv->bg_mode = LwBackgroundDefault;
self->priv->bg_texture = NULL;
self->priv->bg_render_type = LwBackgroundZoom;
self->priv->default_bg = NULL;
self->priv->default_render_type = LwBackgroundZoom;
/* Bind settings to properties */
g_signal_connect(self->priv->settings, "changed::bg-mode",
G_CALLBACK(lw_background_changed), self);
g_settings_bind(self->priv->settings, "bg-image",
self, "image", G_SETTINGS_BIND_GET);
lw_settings_bind_enum(self->priv->settings, "bg-render-type",
self, "render-type", G_SETTINGS_BIND_GET);
lw_settings_bind_color(self->priv->settings, "bg-primary-color",
self, "primary-color", G_SETTINGS_BIND_GET);
lw_settings_bind_color(self->priv->settings, "bg-secondary-color",
self, "secondary-color", G_SETTINGS_BIND_GET);
}
static void
lw_background_dispose(GObject *object)
{
LwBackground *self = LW_BACKGROUND(object);
g_clear_object(&self->priv->settings);
g_clear_object(&self->priv->bg_texture);
g_clear_object(&self->priv->default_bg);
/* Chain up to the parent class */
G_OBJECT_CLASS(lw_background_parent_class)->dispose(object);
}
static void
lw_background_finalize(GObject *object)
{
LwBackground *self = LW_BACKGROUND(object);
g_free(self->priv->image);
/* Chain up to the parent class */
G_OBJECT_CLASS(lw_background_parent_class)->finalize(object);
}
static void
lw_background_class_init(LwBackgroundClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
gobject_class->set_property = lw_background_set_property;
gobject_class->get_property = lw_background_get_property;
gobject_class->dispose = lw_background_dispose;
gobject_class->finalize = lw_background_finalize;
g_type_class_add_private(klass, sizeof(LwBackgroundPrivate));
/**
* LwBackground:image:
*
* Path to use for the background image
*/
obj_properties[PROP_IMAGE] = g_param_spec_string("image", "Image", "Path to use for the background image", NULL, G_PARAM_READWRITE);
/**
* LwBackground:render-type:
*
* Determines how the background image is rendered
*/
obj_properties[PROP_RENDER_TYPE] = g_param_spec_uint("render-type", "Render Type", "Determines how the background image is rendered", 0, 4, 0, G_PARAM_READWRITE);
/**
* LwBackground:primary-color:
*
* Left or bottom color when drawing gradients, or the solid color
*/
obj_properties[PROP_PRIMARY_COLOR] = g_param_spec_boxed("primary-color", "Primary Color", "Left or bottom color when drawing gradients, or the solid color", GDK_TYPE_RGBA, G_PARAM_READWRITE);
/**
* LwBackground:secondary-color:
*
* Right or top color when drawing gradients, not used for solid color
*/
obj_properties[PROP_SECONDARY_COLOR] = g_param_spec_boxed("secondary-color", "Secondary Color", "Right and top color when drawing gradients, not used for solid color", GDK_TYPE_RGBA, G_PARAM_READWRITE);
g_object_class_install_properties(gobject_class, N_PROPERTIES, obj_properties);
}