Home | History | Annotate | Download | only in gfx
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/gfx/gtk_util.h"
      6 
      7 #include <gdk/gdk.h>
      8 #include <gtk/gtk.h>
      9 #include <stdlib.h>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/command_line.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "third_party/skia/include/core/SkBitmap.h"
     15 #include "third_party/skia/include/core/SkUnPreMultiply.h"
     16 #include "ui/gfx/rect.h"
     17 
     18 namespace {
     19 
     20 // A process wide singleton that manages our usage of gdk cursors.
     21 // gdk_cursor_new() hits the disk in several places and GdkCursor instances can
     22 // be reused throughout the process.
     23 class GdkCursorCache {
     24  public:
     25   GdkCursorCache() {}
     26   ~GdkCursorCache() {
     27     for (GdkCursorMap::iterator i(cursors_.begin()); i != cursors_.end(); ++i) {
     28       gdk_cursor_unref(i->second);
     29     }
     30     cursors_.clear();
     31   }
     32 
     33   GdkCursor* GetCursorImpl(GdkCursorType type) {
     34     GdkCursorMap::iterator it = cursors_.find(type);
     35     GdkCursor* cursor = NULL;
     36     if (it == cursors_.end()) {
     37       cursor = gdk_cursor_new(type);
     38       cursors_.insert(std::make_pair(type, cursor));
     39     } else {
     40       cursor = it->second;
     41     }
     42 
     43     // It is not necessary to add a reference here. The callers can ref the
     44     // cursor if they need it for something.
     45     return cursor;
     46   }
     47 
     48  private:
     49   typedef std::map<GdkCursorType, GdkCursor*> GdkCursorMap;
     50   GdkCursorMap cursors_;
     51 
     52   DISALLOW_COPY_AND_ASSIGN(GdkCursorCache);
     53 };
     54 
     55 }  // namespace
     56 
     57 namespace gfx {
     58 
     59 static void CommonInitFromCommandLine(const CommandLine& command_line,
     60                                       void (*init_func)(gint*, gchar***)) {
     61   const std::vector<std::string>& args = command_line.argv();
     62   int argc = args.size();
     63   scoped_ptr<char *[]> argv(new char *[argc + 1]);
     64   for (size_t i = 0; i < args.size(); ++i) {
     65     // TODO(piman (at) google.com): can gtk_init modify argv? Just being safe
     66     // here.
     67     argv[i] = strdup(args[i].c_str());
     68   }
     69   argv[argc] = NULL;
     70   char **argv_pointer = argv.get();
     71 
     72   init_func(&argc, &argv_pointer);
     73   for (size_t i = 0; i < args.size(); ++i) {
     74     free(argv[i]);
     75   }
     76 }
     77 
     78 void GtkInitFromCommandLine(const CommandLine& command_line) {
     79   CommonInitFromCommandLine(command_line, gtk_init);
     80 }
     81 
     82 void GdkInitFromCommandLine(const CommandLine& command_line) {
     83   CommonInitFromCommandLine(command_line, gdk_init);
     84 }
     85 
     86 GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) {
     87   if (bitmap.isNull())
     88     return NULL;
     89 
     90   SkAutoLockPixels lock_pixels(bitmap);
     91 
     92   int width = bitmap.width();
     93   int height = bitmap.height();
     94 
     95   GdkPixbuf* pixbuf = gdk_pixbuf_new(
     96       GDK_COLORSPACE_RGB,  // The only colorspace gtk supports.
     97       TRUE,  // There is an alpha channel.
     98       8,
     99       width, height);
    100 
    101   // SkBitmaps are premultiplied, we need to unpremultiply them.
    102   const int kBytesPerPixel = 4;
    103   uint8* divided = gdk_pixbuf_get_pixels(pixbuf);
    104 
    105   for (int y = 0, i = 0; y < height; y++) {
    106     for (int x = 0; x < width; x++) {
    107       uint32 pixel = bitmap.getAddr32(0, y)[x];
    108 
    109       int alpha = SkColorGetA(pixel);
    110       if (alpha != 0 && alpha != 255) {
    111         SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel);
    112         divided[i + 0] = SkColorGetR(unmultiplied);
    113         divided[i + 1] = SkColorGetG(unmultiplied);
    114         divided[i + 2] = SkColorGetB(unmultiplied);
    115         divided[i + 3] = alpha;
    116       } else {
    117         divided[i + 0] = SkColorGetR(pixel);
    118         divided[i + 1] = SkColorGetG(pixel);
    119         divided[i + 2] = SkColorGetB(pixel);
    120         divided[i + 3] = alpha;
    121       }
    122       i += kBytesPerPixel;
    123     }
    124   }
    125 
    126   return pixbuf;
    127 }
    128 
    129 void SubtractRectanglesFromRegion(GdkRegion* region,
    130                                   const std::vector<Rect>& cutouts) {
    131   for (size_t i = 0; i < cutouts.size(); ++i) {
    132     GdkRectangle rect = cutouts[i].ToGdkRectangle();
    133     GdkRegion* rect_region = gdk_region_rectangle(&rect);
    134     gdk_region_subtract(region, rect_region);
    135     // TODO(deanm): It would be nice to be able to reuse the GdkRegion here.
    136     gdk_region_destroy(rect_region);
    137   }
    138 }
    139 
    140 GdkCursor* GetCursor(int type) {
    141   CR_DEFINE_STATIC_LOCAL(GdkCursorCache, impl, ());
    142   return impl.GetCursorImpl(static_cast<GdkCursorType>(type));
    143 }
    144 
    145 void InitRCStyles() {
    146   static const char kRCText[] =
    147       // Make our dialogs styled like the GNOME HIG.
    148       //
    149       // TODO(evanm): content-area-spacing was introduced in a later
    150       // version of GTK, so we need to set that manually on all dialogs.
    151       // Perhaps it would make sense to have a shared FixupDialog() function.
    152       "style \"gnome-dialog\" {\n"
    153       "  xthickness = 12\n"
    154       "  GtkDialog::action-area-border = 0\n"
    155       "  GtkDialog::button-spacing = 6\n"
    156       "  GtkDialog::content-area-spacing = 18\n"
    157       "  GtkDialog::content-area-border = 12\n"
    158       "}\n"
    159       // Note we set it at the "application" priority, so users can override.
    160       "widget \"GtkDialog\" style : application \"gnome-dialog\"\n"
    161 
    162       // Make our about dialog special, so the image is flush with the edge.
    163       "style \"about-dialog\" {\n"
    164       "  GtkDialog::action-area-border = 12\n"
    165       "  GtkDialog::button-spacing = 6\n"
    166       "  GtkDialog::content-area-spacing = 18\n"
    167       "  GtkDialog::content-area-border = 0\n"
    168       "}\n"
    169       "widget \"about-dialog\" style : application \"about-dialog\"\n";
    170 
    171   gtk_rc_parse_string(kRCText);
    172 }
    173 
    174 base::TimeDelta GetCursorBlinkCycle() {
    175   // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is
    176   // the default value for gtk-cursor-blink-time.
    177   static const gint kGtkDefaultCursorBlinkTime = 1200;
    178 
    179   gint cursor_blink_time = kGtkDefaultCursorBlinkTime;
    180   gboolean cursor_blink = TRUE;
    181   g_object_get(gtk_settings_get_default(),
    182                "gtk-cursor-blink-time", &cursor_blink_time,
    183                "gtk-cursor-blink", &cursor_blink,
    184                NULL);
    185   return cursor_blink ?
    186          base::TimeDelta::FromMilliseconds(cursor_blink_time) :
    187          base::TimeDelta();
    188 }
    189 
    190 }  // namespace gfx
    191