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