2
* Copyright (C) 2011 Canonical Ltd
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Gordon Allott <gord.alott@canonical.com>
21
#include <gdk-pixbuf/gdk-pixbuf.h>
22
#include <libgnome-desktop/gnome-bg.h>
23
#include <unity-misc/gnome-bg-slideshow.h>
24
#include "ubus-server.h"
25
#include "UBusMessages.h"
28
int level_of_recursion;
29
const int MAX_LEVEL_OF_RECURSION = 16;
30
const int MIN_LEVEL_OF_RECURSION = 2;
35
: _transition_handler (0),
37
_current_slide (NULL),
38
_slideshow_handler(0),
39
_current_color (nux::color::DimGray),
40
_new_color (nux::color::DimGray),
41
_old_color (nux::color::DimGray),
42
_hires_time_start(10),
44
_ubus_handle_request_colour(0)
46
background_monitor = gnome_bg_new ();
47
client = g_settings_new ("org.gnome.desktop.background");
48
gnome_bg_load_from_preferences (background_monitor, client);
51
pixbuf = GetPixbufFromBG ();
52
LoadPixbufToHash (pixbuf);
53
g_object_unref (pixbuf);
56
new glib::Signal<void, GnomeBG*>(background_monitor,
58
sigc::mem_fun(this, &BGHash::OnBackgroundChanged)));
61
new glib::Signal<void, GSettings*, gchar*>(client,
63
sigc::mem_fun(this, &BGHash::OnGSettingsChanged)));
65
UBusServer *ubus = ubus_server_get_default ();
67
// avoids making a new object method when all we are doing is
68
// calling a method with no logic
69
auto request_lambda = [](GVariant *data, gpointer self) {
70
reinterpret_cast<BGHash*>(self)->DoUbusColorEmit();
72
_ubus_handle_request_colour = ubus_server_register_interest (ubus, UBUS_BACKGROUND_REQUEST_COLOUR_EMIT,
73
(UBusCallback)request_lambda,
79
g_object_unref (client);
80
g_object_unref (background_monitor);
81
UBusServer *ubus = ubus_server_get_default ();
82
ubus_server_unregister_interest (ubus, _ubus_handle_request_colour);
85
void BGHash::OnGSettingsChanged (GSettings *settings, gchar *key)
87
gnome_bg_load_from_preferences (background_monitor, settings);
90
void BGHash::OnBackgroundChanged (GnomeBG *bg)
92
const gchar *filename = gnome_bg_get_filename (bg);
93
if (_bg_slideshow != NULL)
95
slideshow_unref (_bg_slideshow);
97
_current_slide = NULL;
100
if (_slideshow_handler)
102
g_source_remove (_slideshow_handler);
103
_slideshow_handler = 0;
106
if (g_str_has_suffix (filename, "xml"))
108
GError *error = NULL;
110
if (_bg_slideshow != NULL)
112
slideshow_unref (_bg_slideshow);
113
_bg_slideshow = NULL;
116
_bg_slideshow = read_slideshow_file (filename, &error);
120
g_warning ("BGHash.cpp: could not load filename %s: %s", filename, error->message);
121
g_error_free (error);
123
else if (_bg_slideshow == NULL)
125
g_warning ("BGHash.cpp: could not load filename %s", filename);
129
// we loaded fine, hook up to the slideshow
130
time_t current_time = time(0);
131
double now = (double) current_time;
133
double time_diff = now - _bg_slideshow->start_time;
134
double progress = fmod (time_diff, _bg_slideshow->total_duration);
136
// progress now holds how many seconds we are in to this slideshow.
137
// iterate over the slideshows until we get in to the current slideshow
138
Slide *slide_iteration;
139
Slide *slide_current = NULL;
141
double time_to_next_change = 0;
144
for (list = _bg_slideshow->slides->head; list != NULL; list = list->next)
146
slide_iteration = reinterpret_cast<Slide *>(list->data);
147
if (elapsed + slide_iteration->duration > progress)
149
slide_current = slide_iteration;
150
time_to_next_change = slide_current->duration- (progress - elapsed);
154
elapsed += slide_iteration->duration;
157
if (slide_current == NULL)
159
slide_current = reinterpret_cast<Slide *>(g_queue_peek_head(_bg_slideshow->slides));
160
time_to_next_change = slide_current->duration;
163
// time_to_next_change now holds the seconds until the next slide change
164
// the next slide change may or may not be a fixed slide.
165
_slideshow_handler = g_timeout_add ((guint)(time_to_next_change * 1000),
166
(GSourceFunc)OnSlideshowTransition,
169
// find our current slide now
170
if (slide_current->file1 == NULL)
172
g_warning ("BGHash.cpp: could not load filename %s - slide has no filename", filename);
176
FileSize *fs = reinterpret_cast<FileSize *>(slide_current->file1->data);
177
filename = reinterpret_cast<gchar *>(fs->file);
180
_current_slide = slide_current;
184
LoadFileToHash (filename);
187
gboolean BGHash::OnSlideshowTransition (BGHash *self)
189
// the timing might be a bit weird, GnomeBG works in floating points
190
// for the time value, so we might end up being a bit before the change
191
// or a bit after, after is fine, but before is bad
192
// so we need to check if the slide gnomebg tells us we are on is the
193
// old slide or a new one
195
Slide *proposed_slide = get_current_slide (self->_bg_slideshow, &delta);
196
if (proposed_slide == self->_current_slide)
198
// boo, so we arrived early, we need to get the next slide
199
GList *list = g_queue_find (self->_bg_slideshow->slides, self->_current_slide);
200
if (list->next == NULL)
202
// we hit the end of the slideshow, so we wrap around
203
proposed_slide = reinterpret_cast<Slide *>(self->_bg_slideshow->slides->head->data);
207
proposed_slide = reinterpret_cast<Slide *>(list->next->data);
211
// at this point proposed_slide contains the slide we currently have
212
// if its a transition slide, we need to transition to the next image
213
// if its fixed, we can stay still, the previous transition slide already
214
// got us to this image.
216
// quickly hook up the next transition
218
self->_slideshow_handler = g_timeout_add ((guint)(proposed_slide->duration * 1000),
219
(GSourceFunc)OnSlideshowTransition,
222
if (proposed_slide->fixed == FALSE)
224
const gchar *filename = NULL;
225
if (proposed_slide->file1 == NULL)
227
g_warning ("BGHash.cpp: could not load filename %s - slide has no filename", filename);
231
FileSize *fs = reinterpret_cast<FileSize *>(proposed_slide->file2->data);
232
filename = reinterpret_cast<gchar *>(fs->file);
235
if (filename != NULL)
236
self->LoadFileToHash (filename);
239
self->_current_slide = proposed_slide;
243
nux::Color BGHash::InterpolateColor (nux::Color colora, nux::Color colorb, float value)
245
// takes two colours, transitions between them, we can do it linearly or whatever
246
// i don't think it will matter that much
247
// it doesn't happen too often
248
return colora + ((colorb - colora) * value);
251
void BGHash::TransitionToNewColor(nux::color::Color new_color)
253
if (_transition_handler)
255
// we are currently in a transition
256
g_source_remove (_transition_handler);
259
_old_color = _current_color;
260
_new_color = new_color;
262
_hires_time_start = g_get_monotonic_time();
263
_hires_time_end = 500 * 1000; // 500 milliseconds
264
_transition_handler = g_timeout_add (1000/60, (GSourceFunc)BGHash::OnTransitionCallback, this);
268
gboolean BGHash::OnTransitionCallback(BGHash *self)
270
return self->DoTransitionCallback();
273
gboolean BGHash::DoTransitionCallback ()
275
guint64 current_time = g_get_monotonic_time();
276
float timediff = ((float)current_time - _hires_time_start) / _hires_time_end;
278
_current_color = InterpolateColor(_old_color,
284
if (current_time > _hires_time_start + _hires_time_end)
286
_transition_handler = 0;
295
void BGHash::DoUbusColorEmit()
297
ubus_server_send_message(ubus_server_get_default(),
298
UBUS_BACKGROUND_COLOR_CHANGED,
299
g_variant_new ("(dddd)",
304
//FIXME - using default colours for now
305
//_current_color.red,
306
//_current_color.green,
307
//_current_color.blue,
308
//_current_color.alpha)
312
GdkPixbuf *BGHash::GetPixbufFromBG ()
314
GnomeDesktopThumbnailFactory *factory;
316
GdkScreen *screen = gdk_screen_get_default ();
317
int width = 0, height = 0;
319
factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE);
320
gnome_bg_get_image_size (background_monitor, factory,
321
gdk_screen_get_height(screen),
322
gdk_screen_get_width (screen),
325
pixbuf = gnome_bg_create_thumbnail (background_monitor, factory,
326
gdk_screen_get_default (),
332
void BGHash::LoadPixbufToHash (GdkPixbuf *pixbuf)
337
nux::Color new_color = HashColor (pixbuf);
338
TransitionToNewColor (new_color);
341
void BGHash::LoadFileToHash(const std::string path)
343
GError *error = NULL;
344
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (path.c_str (), &error);
348
_current_color = nux::Color(0.2, 0.2, 0.2, 0.9);
349
g_error_free (error);
354
LoadPixbufToHash (pixbuf);
358
inline nux::Color GetPixbufSample (GdkPixbuf *pixbuf, int x, int y)
360
guchar *img = gdk_pixbuf_get_pixels (pixbuf);
361
guchar *pixel = img + ((y * gdk_pixbuf_get_rowstride(pixbuf)) + (x * gdk_pixbuf_get_n_channels (pixbuf)));
362
return nux::Color ((float)(*(pixel + 0)), (float)(*(pixel + 1)), (float)(*(pixel + 2)));
365
inline bool is_color_different (const nux::Color color_a, const nux::Color color_b)
367
nux::Color diff = color_a - color_b;
368
if (fabs (diff.red) > 0.15 || fabs (diff.green) > 0.15 || fabs (diff.blue) > 0.15)
374
nux::Color GetQuadAverage (int x, int y, int width, int height, GdkPixbuf *pixbuf)
376
level_of_recursion++;
377
// samples four corners
382
nux::Color corner1 = GetPixbufSample(pixbuf, x , y );
383
nux::Color corner2 = GetPixbufSample(pixbuf, x + width, y );
384
nux::Color corner3 = GetPixbufSample(pixbuf, x , y + height);
385
nux::Color corner4 = GetPixbufSample(pixbuf, x + width, y + height);
387
nux::Color centre = GetPixbufSample(pixbuf, x + (width/2), y + (height / 2));
390
if ((is_color_different(corner1, centre) ||
391
level_of_recursion < MIN_LEVEL_OF_RECURSION) &&
392
level_of_recursion < MAX_LEVEL_OF_RECURSION)
394
corner1 = GetQuadAverage(x, y, width/2, height/2, pixbuf);
398
if ((is_color_different(corner2, centre) ||
399
level_of_recursion < MIN_LEVEL_OF_RECURSION) &&
400
level_of_recursion < MAX_LEVEL_OF_RECURSION)
402
corner2 = GetQuadAverage(x + width/2, y, width/2, height/2, pixbuf);
406
if ((is_color_different(corner3, centre) ||
407
level_of_recursion < MIN_LEVEL_OF_RECURSION) &&
408
level_of_recursion < MAX_LEVEL_OF_RECURSION)
410
corner3 = GetQuadAverage(x, y + height/2, width/2, height/2, pixbuf);
414
if ((is_color_different(corner4, centre) ||
415
level_of_recursion < MIN_LEVEL_OF_RECURSION) &&
416
level_of_recursion < MAX_LEVEL_OF_RECURSION)
418
corner4 = GetQuadAverage(x + width/2, y + height/2, width/2, height/2, pixbuf);
421
nux::Color average = ( (corner1 * 3)
426
level_of_recursion--;
430
nux::Color BGHash::HashColor(GdkPixbuf *pixbuf)
432
level_of_recursion = 0;
433
nux::Color average = GetQuadAverage (0, 0,
434
gdk_pixbuf_get_width (pixbuf) - 1,
435
gdk_pixbuf_get_height (pixbuf) - 1,
439
nux::Color matched_color = MatchColor (average);
440
return matched_color;
443
nux::Color BGHash::MatchColor (const nux::Color base_color)
445
nux::Color colors[12];
447
colors[ 0] = nux::Color (0x540e44);
448
colors[ 1] = nux::Color (0x6e0b2a);
449
colors[ 2] = nux::Color (0x841617);
450
colors[ 3] = nux::Color (0x84371b);
451
colors[ 4] = nux::Color (0x864d20);
452
colors[ 5] = nux::Color (0x857f31);
453
colors[ 6] = nux::Color (0x1d6331);
454
colors[ 7] = nux::Color (0x11582e);
455
colors[ 8] = nux::Color (0x0e5955);
456
colors[ 9] = nux::Color (0x192b59);
457
colors[10] = nux::Color (0x1b134c);
458
colors[11] = nux::Color (0x2c0d46);
460
nux::Color bw_colors[2];
461
//bw_colors[0] = nux::Color (211, 215, 207); //Aluminium 2
462
bw_colors[0] = nux::Color (136, 138, 133); //Aluminium 4
463
bw_colors[1] = nux::Color (46 , 52 , 54 ); //Aluminium 6
465
float closest_diff = 200.0f;
466
nux::Color chosen_color;
467
nux::color::HueSaturationValue base_hsv (base_color);
469
if (base_hsv.saturation < 0.08)
472
for (int i = 0; i < 3; i++)
474
nux::color::HueSaturationValue comparison_hsv (bw_colors[i]);
475
float color_diff = fabs(base_hsv.value - comparison_hsv.value);
476
if (color_diff < closest_diff)
478
chosen_color = bw_colors[i];
479
closest_diff = color_diff;
486
for (int i = 0; i < 11; i++)
488
nux::color::HueSaturationValue comparison_hsv (colors[i]);
489
float color_diff = fabs(base_hsv.hue - comparison_hsv.hue);
491
if (color_diff < closest_diff)
493
chosen_color = colors[i];
494
closest_diff = color_diff;
498
nux::color::HueLightnessSaturation hsv_color (chosen_color);
500
hsv_color.saturation = base_hsv.saturation;
501
hsv_color.lightness = 0.2;
502
chosen_color = nux::Color (nux::color::RedGreenBlue(hsv_color));
505
// apply design to the colour
506
chosen_color.alpha = 0.5f;
513
nux::Color BGHash::CurrentColor ()
515
return _current_color;