~ubuntu-branches/ubuntu/oneiric/unity/oneiric

« back to all changes in this revision

Viewing changes to plugins/unityshell/src/BGHash.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2011-08-01 19:53:15 UTC
  • mfrom: (1.1.49 upstream)
  • Revision ID: james.westby@ubuntu.com-20110801195315-mrewa9g7ctnk41oh
Tags: 4.6.0-0ubuntu1
* New upstream release.
  - compiz crashed with SIGSEGV in __strlen_sse2() (LP: #814619)
  - PlacesHomeView::PlacesHomeView leaks memory (LP: #818450)
  - PluginAdapter::MaximizeIfBigEnough leaks memory (LP: #818477)
  - Launcher - Make Launcher left of screen reveal more responsive and less
    prone to false positives (LP: #765819)
  - Launcher - clicking on a App launcher icon incorrectly un-minimizes
    windows (LP: #783434)
  - Unity doesn't get any mouse wheel scroll event in Indicators InputArea
    (LP: #814574)
  - Unity launcher gets cluttered when having multiple partitions and/or
    external volumes attached (LP: #713423)
  - Unity panel menus and indicators slow to respond. Too much lag.
    (LP: #742664)
  - In Unity the distinction between GVolume, GDrive and GMount is a bit
    confusing. (LP: #799890)
  - Launcher - When a item is deleted by dragging to Trash, the trash should
    pulse once before the Launcher disappears (LP: #750311)
  - ccsm needs an option to change launcher opacity (LP: #815032)
  - add a ccsm option to hide volumes in launcher (LP: #794707)
  - scale plugin doesn't work as desired when "Click Desktop To Show
    Desktop" is true (LP: #810315)
  - mute/unmute sound when user clicks on sound applet using scroll button
    or middle mouse button (LP: #609860)
  - Secondary activate (i.e. middle click) support for indicators advanced
    usage (LP: #812933)
* debian/control:
  - dep on latest libunity-misc
  - dep on latest nux
  - add build-dep on libgnome-desktop-3-dev
* debian/rules:
  - bump libunity-core-4.0-4 shlib for ABI break
  - don't ship unity dialogs right now. Not ready for alpha quality
* distro-patch the grey to darker grey (until the blur is done in nux)
* Switch to dpkg-source 3.0 (quilt) format
* debian/patches/01_revert_removed_function_for_unity2d_to_build.patch:
  - revert a removed API making unity-2d not building

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2011 Canonical Ltd
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 * Authored by: Gordon Allott <gord.alott@canonical.com>
 
17
 */
 
18
 
 
19
#include "BGHash.h"
 
20
#include <Nux/Nux.h>
 
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"
 
26
 
 
27
namespace {
 
28
  int level_of_recursion;
 
29
  const int MAX_LEVEL_OF_RECURSION = 16;
 
30
  const int MIN_LEVEL_OF_RECURSION = 2;
 
31
}
 
32
namespace unity {
 
33
 
 
34
  BGHash::BGHash ()
 
35
    : _transition_handler (0),
 
36
      _bg_slideshow (NULL),
 
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),
 
43
      _hires_time_end(20),
 
44
      _ubus_handle_request_colour(0)
 
45
  {
 
46
    background_monitor = gnome_bg_new ();
 
47
    client = g_settings_new ("org.gnome.desktop.background");
 
48
    gnome_bg_load_from_preferences (background_monitor, client);
 
49
 
 
50
    GdkPixbuf *pixbuf;
 
51
    pixbuf = GetPixbufFromBG ();
 
52
    LoadPixbufToHash (pixbuf);
 
53
    g_object_unref (pixbuf);
 
54
 
 
55
    signal_manager_.Add(
 
56
      new glib::Signal<void, GnomeBG*>(background_monitor,
 
57
                                       "changed",
 
58
                                       sigc::mem_fun(this, &BGHash::OnBackgroundChanged)));
 
59
 
 
60
    signal_manager_.Add(
 
61
      new glib::Signal<void, GSettings*, gchar*>(client,
 
62
                                                 "changed",
 
63
                                                 sigc::mem_fun(this, &BGHash::OnGSettingsChanged))); 
 
64
 
 
65
    UBusServer *ubus = ubus_server_get_default ();
 
66
 
 
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();
 
71
    };
 
72
    _ubus_handle_request_colour = ubus_server_register_interest (ubus, UBUS_BACKGROUND_REQUEST_COLOUR_EMIT,
 
73
                                                                 (UBusCallback)request_lambda,
 
74
                                                                  this);
 
75
  }
 
76
 
 
77
  BGHash::~BGHash ()
 
78
  {
 
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);
 
83
  }
 
84
 
 
85
  void BGHash::OnGSettingsChanged (GSettings *settings, gchar *key)
 
86
  {
 
87
    gnome_bg_load_from_preferences (background_monitor, settings);
 
88
  }
 
89
 
 
90
  void BGHash::OnBackgroundChanged (GnomeBG *bg)
 
91
  {
 
92
    const gchar *filename = gnome_bg_get_filename (bg);
 
93
    if (_bg_slideshow != NULL)
 
94
    {
 
95
      slideshow_unref (_bg_slideshow);
 
96
      _bg_slideshow = NULL;
 
97
      _current_slide = NULL;
 
98
    }
 
99
 
 
100
    if (_slideshow_handler)
 
101
    {
 
102
      g_source_remove (_slideshow_handler);
 
103
      _slideshow_handler = 0;
 
104
    }
 
105
 
 
106
    if (g_str_has_suffix (filename, "xml"))
 
107
    {
 
108
      GError *error = NULL;
 
109
 
 
110
      if (_bg_slideshow != NULL)
 
111
      {
 
112
        slideshow_unref (_bg_slideshow);
 
113
        _bg_slideshow = NULL;
 
114
      }
 
115
 
 
116
      _bg_slideshow = read_slideshow_file (filename, &error);
 
117
 
 
118
      if (error != NULL)
 
119
      {
 
120
        g_warning ("BGHash.cpp: could not load filename %s: %s", filename, error->message);
 
121
        g_error_free (error);
 
122
      }
 
123
      else if (_bg_slideshow == NULL)
 
124
      {
 
125
        g_warning ("BGHash.cpp: could not load filename %s", filename);
 
126
      }
 
127
      else
 
128
      {
 
129
        // we loaded fine, hook up to the slideshow
 
130
        time_t current_time = time(0);
 
131
        double now = (double) current_time;
 
132
 
 
133
        double time_diff = now - _bg_slideshow->start_time;
 
134
        double progress = fmod (time_diff, _bg_slideshow->total_duration);
 
135
 
 
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;
 
140
        double elapsed = 0;
 
141
        double time_to_next_change = 0;
 
142
        GList *list;
 
143
 
 
144
        for (list = _bg_slideshow->slides->head; list != NULL; list = list->next)
 
145
        {
 
146
          slide_iteration = reinterpret_cast<Slide *>(list->data);
 
147
          if (elapsed + slide_iteration->duration > progress)
 
148
          {
 
149
            slide_current = slide_iteration;
 
150
            time_to_next_change = slide_current->duration- (progress - elapsed);
 
151
            break;
 
152
          }
 
153
 
 
154
          elapsed += slide_iteration->duration;
 
155
        }
 
156
 
 
157
        if (slide_current == NULL)
 
158
        {
 
159
          slide_current = reinterpret_cast<Slide *>(g_queue_peek_head(_bg_slideshow->slides));
 
160
          time_to_next_change = slide_current->duration;
 
161
        }
 
162
 
 
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,
 
167
                                                  (gpointer)this);
 
168
 
 
169
        // find our current slide now
 
170
        if (slide_current->file1 == NULL)
 
171
        {
 
172
          g_warning ("BGHash.cpp: could not load filename %s - slide has no filename", filename);
 
173
        }
 
174
        else
 
175
        {
 
176
          FileSize *fs = reinterpret_cast<FileSize *>(slide_current->file1->data);
 
177
          filename = reinterpret_cast<gchar *>(fs->file);
 
178
        }
 
179
 
 
180
        _current_slide = slide_current;
 
181
      }
 
182
    }
 
183
 
 
184
    LoadFileToHash (filename);
 
185
  }
 
186
 
 
187
  gboolean BGHash::OnSlideshowTransition (BGHash *self)
 
188
  {
 
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
 
194
    double delta = 0.0;
 
195
    Slide *proposed_slide = get_current_slide (self->_bg_slideshow, &delta);
 
196
    if (proposed_slide == self->_current_slide)
 
197
    {
 
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)
 
201
      {
 
202
        // we hit the end of the slideshow, so we wrap around
 
203
        proposed_slide = reinterpret_cast<Slide *>(self->_bg_slideshow->slides->head->data);
 
204
      }
 
205
      else
 
206
      {
 
207
        proposed_slide = reinterpret_cast<Slide *>(list->next->data);
 
208
      }
 
209
    }
 
210
 
 
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.
 
215
 
 
216
    // quickly hook up the next transition
 
217
 
 
218
    self->_slideshow_handler = g_timeout_add ((guint)(proposed_slide->duration * 1000),
 
219
                                              (GSourceFunc)OnSlideshowTransition,
 
220
                                              (gpointer)self);
 
221
 
 
222
    if (proposed_slide->fixed == FALSE)
 
223
    {
 
224
      const gchar *filename = NULL;
 
225
      if (proposed_slide->file1 == NULL)
 
226
      {
 
227
        g_warning ("BGHash.cpp: could not load filename %s - slide has no filename", filename);
 
228
      }
 
229
      else
 
230
      {
 
231
        FileSize *fs = reinterpret_cast<FileSize *>(proposed_slide->file2->data);
 
232
        filename = reinterpret_cast<gchar *>(fs->file);
 
233
      }
 
234
 
 
235
      if (filename != NULL)
 
236
        self->LoadFileToHash (filename);
 
237
    }
 
238
 
 
239
    self->_current_slide = proposed_slide;
 
240
    return false;
 
241
  }
 
242
 
 
243
  nux::Color BGHash::InterpolateColor (nux::Color colora, nux::Color colorb, float value)
 
244
  {
 
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);
 
249
  }
 
250
 
 
251
  void BGHash::TransitionToNewColor(nux::color::Color new_color)
 
252
  {
 
253
    if (_transition_handler)
 
254
    {
 
255
      // we are currently in a transition
 
256
      g_source_remove (_transition_handler);
 
257
    }
 
258
 
 
259
    _old_color = _current_color;
 
260
    _new_color = new_color;
 
261
 
 
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);
 
265
 
 
266
  }
 
267
 
 
268
  gboolean BGHash::OnTransitionCallback(BGHash *self)
 
269
  {
 
270
    return self->DoTransitionCallback();
 
271
  }
 
272
 
 
273
  gboolean BGHash::DoTransitionCallback ()
 
274
  {
 
275
    guint64 current_time = g_get_monotonic_time();
 
276
    float timediff = ((float)current_time - _hires_time_start) / _hires_time_end;
 
277
 
 
278
    _current_color = InterpolateColor(_old_color,
 
279
                                      _new_color,
 
280
                                      timediff);
 
281
 
 
282
    DoUbusColorEmit ();
 
283
 
 
284
    if (current_time > _hires_time_start + _hires_time_end)
 
285
    {
 
286
      _transition_handler = 0;
 
287
      return FALSE;
 
288
    }
 
289
    else
 
290
    {
 
291
      return TRUE;
 
292
    }
 
293
  }
 
294
 
 
295
  void BGHash::DoUbusColorEmit()
 
296
  {
 
297
    ubus_server_send_message(ubus_server_get_default(),
 
298
                             UBUS_BACKGROUND_COLOR_CHANGED,
 
299
                             g_variant_new ("(dddd)",
 
300
                                            0.0,
 
301
                                            0.0,
 
302
                                            0.0,
 
303
                                            0.95)
 
304
                                            //FIXME - using default colours for now
 
305
                                            //_current_color.red,
 
306
                                            //_current_color.green,
 
307
                                            //_current_color.blue,
 
308
                                            //_current_color.alpha)
 
309
                            );
 
310
  }
 
311
 
 
312
  GdkPixbuf *BGHash::GetPixbufFromBG ()
 
313
  {
 
314
    GnomeDesktopThumbnailFactory *factory;
 
315
    GdkPixbuf *pixbuf;
 
316
    GdkScreen *screen = gdk_screen_get_default ();
 
317
    int width = 0, height = 0;
 
318
 
 
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),
 
323
                             &width, &height);
 
324
 
 
325
    pixbuf = gnome_bg_create_thumbnail (background_monitor, factory,
 
326
                                        gdk_screen_get_default (),
 
327
                                        width, height);
 
328
 
 
329
    return pixbuf;
 
330
  }
 
331
 
 
332
  void BGHash::LoadPixbufToHash (GdkPixbuf *pixbuf)
 
333
  {
 
334
    if (pixbuf == NULL)
 
335
      return;
 
336
 
 
337
    nux::Color new_color = HashColor (pixbuf);
 
338
    TransitionToNewColor (new_color);
 
339
  }
 
340
 
 
341
  void BGHash::LoadFileToHash(const std::string path)
 
342
  {
 
343
    GError *error = NULL;
 
344
    GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (path.c_str (), &error);
 
345
 
 
346
    if (error != NULL)
 
347
    {
 
348
      _current_color = nux::Color(0.2, 0.2, 0.2, 0.9);
 
349
      g_error_free (error);
 
350
      return;
 
351
    }
 
352
 
 
353
 
 
354
    LoadPixbufToHash (pixbuf);
 
355
 
 
356
  }
 
357
 
 
358
  inline nux::Color GetPixbufSample (GdkPixbuf *pixbuf, int x, int y)
 
359
  {
 
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)));
 
363
  }
 
364
 
 
365
  inline bool is_color_different (const nux::Color color_a, const nux::Color color_b)
 
366
  {
 
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)
 
369
      return true;
 
370
 
 
371
    return false;
 
372
  }
 
373
 
 
374
  nux::Color GetQuadAverage (int x, int y, int width, int height, GdkPixbuf *pixbuf)
 
375
  {
 
376
    level_of_recursion++;
 
377
    // samples four corners
 
378
    // c1-----c2
 
379
    // |       |
 
380
    // c3-----c4
 
381
 
 
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);
 
386
 
 
387
    nux::Color centre = GetPixbufSample(pixbuf, x + (width/2), y + (height / 2));
 
388
 
 
389
    // corner 1
 
390
    if ((is_color_different(corner1, centre) ||
 
391
         level_of_recursion < MIN_LEVEL_OF_RECURSION) &&
 
392
        level_of_recursion < MAX_LEVEL_OF_RECURSION)
 
393
    {
 
394
      corner1 = GetQuadAverage(x, y, width/2, height/2, pixbuf);
 
395
    }
 
396
 
 
397
    // corner 2
 
398
    if ((is_color_different(corner2, centre) ||
 
399
         level_of_recursion < MIN_LEVEL_OF_RECURSION) &&
 
400
        level_of_recursion < MAX_LEVEL_OF_RECURSION)
 
401
    {
 
402
      corner2 = GetQuadAverage(x + width/2, y, width/2, height/2, pixbuf);
 
403
    }
 
404
 
 
405
    // corner 3
 
406
    if ((is_color_different(corner3, centre) ||
 
407
         level_of_recursion < MIN_LEVEL_OF_RECURSION) &&
 
408
        level_of_recursion < MAX_LEVEL_OF_RECURSION)
 
409
    {
 
410
      corner3 = GetQuadAverage(x, y + height/2, width/2, height/2, pixbuf);
 
411
    }
 
412
 
 
413
    // corner 4
 
414
    if ((is_color_different(corner4, centre) ||
 
415
         level_of_recursion < MIN_LEVEL_OF_RECURSION) &&
 
416
        level_of_recursion < MAX_LEVEL_OF_RECURSION)
 
417
    {
 
418
      corner4 = GetQuadAverage(x + width/2, y + height/2, width/2, height/2, pixbuf);
 
419
    }
 
420
 
 
421
    nux::Color average = (  (corner1 * 3)
 
422
                          + (corner3 * 3)
 
423
                          + (centre  * 2)
 
424
                          + corner2
 
425
                          + corner4) * 0.1;
 
426
    level_of_recursion--;
 
427
    return average;
 
428
  }
 
429
 
 
430
  nux::Color BGHash::HashColor(GdkPixbuf *pixbuf)
 
431
  {
 
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,
 
436
                                         pixbuf);
 
437
 
 
438
 
 
439
    nux::Color matched_color = MatchColor (average);
 
440
    return matched_color;
 
441
  }
 
442
 
 
443
  nux::Color BGHash::MatchColor (const nux::Color base_color)
 
444
  {
 
445
    nux::Color colors[12];
 
446
 
 
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);
 
459
 
 
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
 
464
 
 
465
    float closest_diff = 200.0f;
 
466
    nux::Color chosen_color;
 
467
    nux::color::HueSaturationValue base_hsv (base_color);
 
468
 
 
469
    if (base_hsv.saturation < 0.08)
 
470
    {
 
471
      // grayscale image
 
472
      for (int i = 0; i < 3; i++)
 
473
      {
 
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)
 
477
        {
 
478
          chosen_color = bw_colors[i];
 
479
          closest_diff = color_diff;
 
480
        }
 
481
      }
 
482
    }
 
483
    else
 
484
    {
 
485
      // full colour image
 
486
      for (int i = 0; i < 11; i++)
 
487
      {
 
488
        nux::color::HueSaturationValue comparison_hsv (colors[i]);
 
489
        float color_diff = fabs(base_hsv.hue - comparison_hsv.hue);
 
490
 
 
491
        if (color_diff < closest_diff)
 
492
        {
 
493
          chosen_color = colors[i];
 
494
          closest_diff = color_diff;
 
495
        }
 
496
      }
 
497
 
 
498
      nux::color::HueLightnessSaturation hsv_color (chosen_color);
 
499
 
 
500
      hsv_color.saturation = base_hsv.saturation;
 
501
      hsv_color.lightness = 0.2;
 
502
      chosen_color = nux::Color (nux::color::RedGreenBlue(hsv_color));
 
503
    }
 
504
 
 
505
    // apply design to the colour
 
506
    chosen_color.alpha = 0.5f;
 
507
 
 
508
 
 
509
 
 
510
    return chosen_color;
 
511
  }
 
512
 
 
513
  nux::Color BGHash::CurrentColor ()
 
514
  {
 
515
    return _current_color;
 
516
  }
 
517
}