2017-02-06 14:37:42 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
|
|
|
*
|
|
|
|
* Licensed under the GNU General Public License Version 2
|
|
|
|
*
|
|
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <colord.h>
|
|
|
|
|
2017-02-10 16:32:24 +00:00
|
|
|
#include "cc-night-light-widget.h"
|
2017-02-06 14:37:42 +00:00
|
|
|
#include "cc-display-resources.h"
|
|
|
|
|
2017-02-10 16:32:24 +00:00
|
|
|
struct _CcNightLightWidget {
|
2017-02-06 14:37:42 +00:00
|
|
|
GtkDrawingArea parent;
|
|
|
|
gdouble to;
|
|
|
|
gdouble from;
|
|
|
|
gdouble now;
|
|
|
|
cairo_surface_t *surface_sunrise;
|
|
|
|
cairo_surface_t *surface_sunset;
|
2017-02-14 11:20:06 +00:00
|
|
|
CcNightLightWidgetMode mode;
|
2017-02-06 14:37:42 +00:00
|
|
|
};
|
|
|
|
|
2017-02-10 16:32:24 +00:00
|
|
|
G_DEFINE_TYPE (CcNightLightWidget, cc_night_light_widget, GTK_TYPE_DRAWING_AREA);
|
2017-02-06 14:37:42 +00:00
|
|
|
|
2017-02-10 16:32:24 +00:00
|
|
|
static gboolean cc_night_light_widget_draw (GtkWidget *widget, cairo_t *cr);
|
2017-02-06 14:37:42 +00:00
|
|
|
|
|
|
|
void
|
2017-02-10 16:32:24 +00:00
|
|
|
cc_night_light_widget_set_to (CcNightLightWidget *self, gdouble to)
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
|
|
|
self->to = to;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-02-10 16:32:24 +00:00
|
|
|
cc_night_light_widget_set_from (CcNightLightWidget *self, gdouble from)
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
|
|
|
self->from = from;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-02-10 16:32:24 +00:00
|
|
|
cc_night_light_widget_set_now (CcNightLightWidget *self, gdouble now)
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
|
|
|
self->now = now;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
|
|
}
|
|
|
|
|
2017-02-14 11:20:06 +00:00
|
|
|
void
|
|
|
|
cc_night_light_widget_set_mode (CcNightLightWidget *self,
|
|
|
|
CcNightLightWidgetMode mode)
|
|
|
|
{
|
|
|
|
self->mode = mode;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
|
|
}
|
2017-02-06 14:37:42 +00:00
|
|
|
|
|
|
|
static cairo_status_t
|
|
|
|
_png_read_func (void *closure, unsigned char *data, unsigned int length)
|
|
|
|
{
|
|
|
|
GInputStream *stream = G_INPUT_STREAM (closure);
|
|
|
|
gssize read;
|
|
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
read = g_input_stream_read (stream, data, (gsize) length, NULL, &error);
|
|
|
|
if (read < 0)
|
|
|
|
{
|
|
|
|
g_warning ("failed to read form stream: %s", error->message);
|
|
|
|
return CAIRO_STATUS_READ_ERROR;
|
|
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2017-04-07 12:46:09 +01:00
|
|
|
static const char *scaled_sizes[] = {
|
|
|
|
NULL,
|
|
|
|
"16x16",
|
|
|
|
};
|
|
|
|
|
2017-02-06 14:37:42 +00:00
|
|
|
static cairo_surface_t *
|
2017-04-07 12:46:09 +01:00
|
|
|
read_surface_from_resource (const gchar *dirname,
|
|
|
|
int scale_factor,
|
|
|
|
const gchar *basename)
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
2017-04-07 12:46:09 +01:00
|
|
|
/* Always ensure that we have a resource for the scale factor */
|
|
|
|
scale_factor = CLAMP (scale_factor, 1, G_N_ELEMENTS (scaled_sizes) - 1);
|
|
|
|
|
2017-02-06 14:37:42 +00:00
|
|
|
g_autoptr(GError) error = NULL;
|
|
|
|
g_autoptr(GInputStream) stream = NULL;
|
2017-04-07 12:46:09 +01:00
|
|
|
g_autofree char *path = g_build_filename (dirname, scaled_sizes[scale_factor], basename, NULL);
|
|
|
|
|
2017-02-06 14:37:42 +00:00
|
|
|
stream = g_resource_open_stream (cc_display_get_resource (), path,
|
|
|
|
G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
|
|
|
|
if (stream == NULL)
|
|
|
|
{
|
|
|
|
g_error ("failed to load PNG data: %s", error->message);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return cairo_image_surface_create_from_png_stream (_png_read_func, stream);
|
|
|
|
}
|
|
|
|
|
2017-04-07 12:46:09 +01:00
|
|
|
static void
|
|
|
|
cc_night_light_widget_style_updated (GtkWidget *widget)
|
|
|
|
{
|
|
|
|
GTK_WIDGET_CLASS (cc_night_light_widget_parent_class)->style_updated (widget);
|
|
|
|
|
|
|
|
CcNightLightWidget *self = CC_NIGHT_LIGHT_WIDGET (widget);
|
|
|
|
GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
|
|
|
|
|
|
|
|
g_clear_pointer (&self->surface_sunrise, (GDestroyNotify) cairo_surface_destroy);
|
|
|
|
g_clear_pointer (&self->surface_sunset, (GDestroyNotify) cairo_surface_destroy);
|
|
|
|
|
|
|
|
self->surface_sunrise =
|
|
|
|
gtk_icon_theme_load_surface (icon_theme, "daytime-sunrise-symbolic",
|
|
|
|
16,
|
|
|
|
gtk_widget_get_scale_factor (widget),
|
|
|
|
gtk_widget_get_window (widget),
|
|
|
|
0,
|
|
|
|
NULL);
|
|
|
|
if (self->surface_sunrise == NULL)
|
|
|
|
self->surface_sunrise = read_surface_from_resource ("/org/gnome/control-center/display",
|
|
|
|
gtk_widget_get_scale_factor (widget),
|
|
|
|
"sunrise.png");
|
|
|
|
|
|
|
|
self->surface_sunset =
|
|
|
|
gtk_icon_theme_load_surface (icon_theme, "daytime-sunset-symbolic",
|
|
|
|
16,
|
|
|
|
gtk_widget_get_scale_factor (widget),
|
|
|
|
gtk_widget_get_window (widget),
|
|
|
|
0,
|
|
|
|
NULL);
|
|
|
|
if (self->surface_sunset == NULL)
|
|
|
|
self->surface_sunset = read_surface_from_resource ("/org/gnome/control-center/display",
|
|
|
|
gtk_widget_get_scale_factor (widget),
|
|
|
|
"sunset.png");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cc_night_light_widget_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
CcNightLightWidget *self = CC_NIGHT_LIGHT_WIDGET (object);
|
|
|
|
|
|
|
|
g_clear_pointer (&self->surface_sunrise, (GDestroyNotify) cairo_surface_destroy);
|
|
|
|
g_clear_pointer (&self->surface_sunset, (GDestroyNotify) cairo_surface_destroy);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (cc_night_light_widget_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cc_night_light_widget_class_init (CcNightLightWidgetClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
object_class->finalize = cc_night_light_widget_finalize;
|
|
|
|
widget_class->draw = cc_night_light_widget_draw;
|
|
|
|
widget_class->style_updated = cc_night_light_widget_style_updated;
|
|
|
|
}
|
|
|
|
|
2017-02-06 14:37:42 +00:00
|
|
|
static void
|
2017-02-10 16:32:24 +00:00
|
|
|
cc_night_light_widget_init (CcNightLightWidget *self)
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
|
|
|
self->to = 8;
|
|
|
|
self->from = 16;
|
|
|
|
self->now = 11;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
is_frac_day_between (gdouble value, gdouble to, gdouble from)
|
|
|
|
{
|
|
|
|
/* wraparound to the next day */
|
|
|
|
if (from < to)
|
|
|
|
from += 24;
|
|
|
|
|
|
|
|
/* wraparound to the previous day */
|
|
|
|
if (value < from && value < to)
|
|
|
|
value += 24;
|
|
|
|
|
|
|
|
/* test limits */
|
|
|
|
return value > to && value <= from;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
rounded_rectangle (cairo_t *cr,
|
|
|
|
gdouble x,
|
|
|
|
gdouble y,
|
|
|
|
gdouble radius,
|
|
|
|
gdouble width,
|
|
|
|
gdouble height)
|
|
|
|
{
|
|
|
|
gdouble degrees = G_PI / 180.0;
|
|
|
|
|
|
|
|
cairo_new_sub_path (cr);
|
|
|
|
cairo_arc (cr,
|
|
|
|
x + width - radius,
|
|
|
|
y + radius,
|
|
|
|
radius,
|
|
|
|
-90 * degrees,
|
|
|
|
0 * degrees);
|
|
|
|
cairo_arc (cr,
|
|
|
|
x + width - radius,
|
|
|
|
y + height - radius,
|
|
|
|
radius,
|
|
|
|
0 * degrees,
|
|
|
|
90 * degrees);
|
|
|
|
cairo_arc (cr,
|
|
|
|
x + radius,
|
|
|
|
y + height - radius,
|
|
|
|
radius,
|
|
|
|
90 * degrees,
|
|
|
|
180 * degrees);
|
|
|
|
cairo_arc (cr,
|
|
|
|
x + radius,
|
|
|
|
y + radius,
|
|
|
|
radius,
|
|
|
|
180 * degrees,
|
|
|
|
270 * degrees);
|
|
|
|
cairo_close_path (cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2017-02-10 16:32:24 +00:00
|
|
|
cc_night_light_widget_draw (GtkWidget *widget, cairo_t *cr)
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
|
|
|
CdColorRGB color;
|
|
|
|
CdColorRGB color_temperature;
|
|
|
|
CdColorRGB color_unity;
|
|
|
|
GtkAllocation rect;
|
|
|
|
const guint arrow_sz = 5; /* px */
|
|
|
|
const guint icon_sz = 16; /* px */
|
2017-02-15 13:30:31 +00:00
|
|
|
const guint pad_upper_sz = 6; /* px */
|
|
|
|
const guint pad_lower_sz = 4; /* px */
|
2017-02-06 14:37:42 +00:00
|
|
|
guint line_x = 0; /* px */
|
2017-02-15 13:30:31 +00:00
|
|
|
guint bar_sz;
|
2017-02-06 14:37:42 +00:00
|
|
|
|
2017-02-10 16:32:24 +00:00
|
|
|
CcNightLightWidget *self = (CcNightLightWidget*) widget;
|
2017-02-06 14:37:42 +00:00
|
|
|
g_return_val_if_fail (self != NULL, FALSE);
|
2017-02-10 16:32:24 +00:00
|
|
|
g_return_val_if_fail (CC_IS_NIGHT_LIGHT_WIDGET (self), FALSE);
|
2017-02-06 14:37:42 +00:00
|
|
|
|
|
|
|
cd_color_rgb_set (&color_temperature, 0.992, 0.796, 0.612);
|
|
|
|
cd_color_rgb_set (&color_unity, 0.773, 0.862, 0.953);
|
|
|
|
|
2017-02-15 13:30:31 +00:00
|
|
|
/*
|
|
|
|
* /
|
|
|
|
* | icon_sz
|
|
|
|
* \
|
|
|
|
* <- pad_upper_sz
|
|
|
|
* /
|
|
|
|
* | bar_sz (calculated)
|
|
|
|
* \
|
|
|
|
* <- pad_lower_sz
|
|
|
|
* /
|
|
|
|
* | arrow_sz
|
|
|
|
* \
|
|
|
|
*/
|
2017-02-06 14:37:42 +00:00
|
|
|
gtk_widget_get_allocation (widget, &rect);
|
2017-02-15 13:30:31 +00:00
|
|
|
bar_sz = rect.height - (icon_sz + pad_upper_sz + pad_lower_sz + arrow_sz);
|
2017-02-06 14:37:42 +00:00
|
|
|
|
|
|
|
/* clip to a rounded rectangle */
|
|
|
|
cairo_save (cr);
|
|
|
|
cairo_set_line_width (cr, 1);
|
2017-02-15 13:30:31 +00:00
|
|
|
rounded_rectangle (cr, 0, icon_sz + pad_upper_sz, 6,
|
|
|
|
rect.width, bar_sz);
|
2017-02-06 14:37:42 +00:00
|
|
|
cairo_clip (cr);
|
|
|
|
|
|
|
|
/* draw each color line */
|
|
|
|
gdouble subsect = 24.f / (gdouble) rect.width;
|
2017-02-15 13:30:31 +00:00
|
|
|
if (gtk_widget_is_sensitive (widget))
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
2017-02-15 13:30:31 +00:00
|
|
|
cairo_set_line_width (cr, 1);
|
|
|
|
for (guint x = 0; x < rect.width; x += 1)
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
2017-02-15 13:30:31 +00:00
|
|
|
gdouble frac_hour = subsect * x;
|
|
|
|
if (is_frac_day_between (frac_hour, self->to - 1, self->to))
|
|
|
|
{
|
|
|
|
gdouble frac = 1.f - (self->to - frac_hour);
|
|
|
|
cd_color_rgb_interpolate (&color_temperature,
|
|
|
|
&color_unity,
|
|
|
|
frac,
|
|
|
|
&color);
|
|
|
|
}
|
|
|
|
else if (is_frac_day_between (frac_hour, self->from - 1, self->from))
|
|
|
|
{
|
|
|
|
gdouble frac = self->from - frac_hour;
|
|
|
|
cd_color_rgb_interpolate (&color_temperature,
|
|
|
|
&color_unity,
|
|
|
|
frac,
|
|
|
|
&color);
|
|
|
|
}
|
|
|
|
else if (is_frac_day_between (frac_hour, self->to, self->from))
|
|
|
|
{
|
|
|
|
cd_color_rgb_copy (&color_unity, &color);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cd_color_rgb_copy (&color_temperature, &color);
|
|
|
|
}
|
|
|
|
cairo_set_source_rgb (cr, color.R, color.G, color.B);
|
|
|
|
cairo_move_to (cr, x + 0.5, icon_sz + pad_upper_sz);
|
|
|
|
cairo_line_to (cr, x + 0.5, icon_sz + pad_upper_sz + bar_sz);
|
|
|
|
cairo_stroke (cr);
|
2017-02-06 14:37:42 +00:00
|
|
|
}
|
2017-02-15 13:30:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rounded_rectangle (cr, 0, icon_sz + pad_upper_sz, 6,
|
|
|
|
rect.width, bar_sz);
|
|
|
|
cairo_set_source_rgb (cr, 0.95f, 0.95f, 0.95f);
|
|
|
|
cairo_fill (cr);
|
2017-02-06 14:37:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* apply border */
|
2017-02-15 13:30:31 +00:00
|
|
|
rounded_rectangle (cr, 0, icon_sz + pad_upper_sz, 6,
|
|
|
|
rect.width, bar_sz);
|
2017-02-06 14:37:42 +00:00
|
|
|
cairo_set_source_rgb (cr, 0.65, 0.65, 0.65);
|
|
|
|
cairo_set_line_width (cr, 1);
|
|
|
|
cairo_stroke (cr);
|
|
|
|
cairo_restore (cr);
|
|
|
|
|
|
|
|
/* apply arrow */
|
2017-02-15 13:30:31 +00:00
|
|
|
if (gtk_widget_is_sensitive (widget))
|
|
|
|
{
|
|
|
|
line_x = self->now / subsect;
|
|
|
|
cairo_move_to (cr,
|
|
|
|
line_x - arrow_sz + 0.5,
|
|
|
|
icon_sz + pad_upper_sz + bar_sz + pad_lower_sz + arrow_sz);
|
|
|
|
cairo_line_to (cr,
|
|
|
|
line_x + arrow_sz + 0.5,
|
|
|
|
icon_sz + pad_upper_sz + bar_sz + pad_lower_sz + arrow_sz);
|
|
|
|
cairo_line_to (cr,
|
|
|
|
line_x + 0.5,
|
|
|
|
icon_sz + pad_upper_sz + bar_sz + pad_lower_sz);
|
|
|
|
cairo_close_path (cr);
|
|
|
|
cairo_set_source_rgb (cr, 0.333, 0.333, 0.333);
|
|
|
|
cairo_fill (cr);
|
|
|
|
}
|
2017-02-06 14:37:42 +00:00
|
|
|
|
|
|
|
/* draw icons */
|
2017-02-15 13:30:31 +00:00
|
|
|
if (gtk_widget_is_sensitive (widget) &&
|
|
|
|
self->mode == CC_NIGHT_LIGHT_WIDGET_MODE_AUTOMATIC)
|
2017-02-14 11:20:06 +00:00
|
|
|
{
|
|
|
|
if (self->to <= 0)
|
|
|
|
line_x = rect.width - icon_sz;
|
|
|
|
else
|
|
|
|
line_x = MIN (MAX ((self->to / subsect) - (icon_sz / 2), 0), rect.width - icon_sz);
|
|
|
|
cairo_set_source_surface (cr, self->surface_sunrise, line_x, 0);
|
|
|
|
cairo_paint (cr);
|
|
|
|
if (self->from <= 0)
|
|
|
|
line_x = rect.width - icon_sz;
|
|
|
|
else
|
|
|
|
line_x = MIN (MAX ((self->from / subsect) - (icon_sz / 2), 0), rect.width - icon_sz);
|
|
|
|
cairo_set_source_surface (cr, self->surface_sunset, line_x, 0);
|
|
|
|
cairo_paint (cr);
|
|
|
|
}
|
2017-02-15 13:30:31 +00:00
|
|
|
|
2017-02-06 14:37:42 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
GtkWidget *
|
2017-02-10 16:32:24 +00:00
|
|
|
cc_night_light_widget_new (void)
|
2017-02-06 14:37:42 +00:00
|
|
|
{
|
2017-02-10 16:32:24 +00:00
|
|
|
return g_object_new (CC_TYPE_NIGHT_LIGHT_WIDGET, NULL);
|
2017-02-06 14:37:42 +00:00
|
|
|
}
|
|
|
|
|