// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gfx/gtk_util.h" #include #include #include #include "base/basictypes.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkUnPreMultiply.h" #include "ui/gfx/rect.h" namespace { // A process wide singleton that manages our usage of gdk cursors. // gdk_cursor_new() hits the disk in several places and GdkCursor instances can // be reused throughout the process. class GdkCursorCache { public: GdkCursorCache() {} ~GdkCursorCache() { for (GdkCursorMap::iterator i(cursors_.begin()); i != cursors_.end(); ++i) { gdk_cursor_unref(i->second); } cursors_.clear(); } GdkCursor* GetCursorImpl(GdkCursorType type) { GdkCursorMap::iterator it = cursors_.find(type); GdkCursor* cursor = NULL; if (it == cursors_.end()) { cursor = gdk_cursor_new(type); cursors_.insert(std::make_pair(type, cursor)); } else { cursor = it->second; } // It is not necessary to add a reference here. The callers can ref the // cursor if they need it for something. return cursor; } private: typedef std::map GdkCursorMap; GdkCursorMap cursors_; DISALLOW_COPY_AND_ASSIGN(GdkCursorCache); }; } // namespace namespace gfx { static void CommonInitFromCommandLine(const CommandLine& command_line, void (*init_func)(gint*, gchar***)) { const std::vector& args = command_line.argv(); int argc = args.size(); scoped_ptr argv(new char *[argc + 1]); for (size_t i = 0; i < args.size(); ++i) { // TODO(piman@google.com): can gtk_init modify argv? Just being safe // here. argv[i] = strdup(args[i].c_str()); } argv[argc] = NULL; char **argv_pointer = argv.get(); init_func(&argc, &argv_pointer); for (size_t i = 0; i < args.size(); ++i) { free(argv[i]); } } void GtkInitFromCommandLine(const CommandLine& command_line) { CommonInitFromCommandLine(command_line, gtk_init); } void GdkInitFromCommandLine(const CommandLine& command_line) { CommonInitFromCommandLine(command_line, gdk_init); } GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) { if (bitmap.isNull()) return NULL; SkAutoLockPixels lock_pixels(bitmap); int width = bitmap.width(); int height = bitmap.height(); GdkPixbuf* pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, // The only colorspace gtk supports. TRUE, // There is an alpha channel. 8, width, height); // SkBitmaps are premultiplied, we need to unpremultiply them. const int kBytesPerPixel = 4; uint8* divided = gdk_pixbuf_get_pixels(pixbuf); for (int y = 0, i = 0; y < height; y++) { for (int x = 0; x < width; x++) { uint32 pixel = bitmap.getAddr32(0, y)[x]; int alpha = SkColorGetA(pixel); if (alpha != 0 && alpha != 255) { SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel); divided[i + 0] = SkColorGetR(unmultiplied); divided[i + 1] = SkColorGetG(unmultiplied); divided[i + 2] = SkColorGetB(unmultiplied); divided[i + 3] = alpha; } else { divided[i + 0] = SkColorGetR(pixel); divided[i + 1] = SkColorGetG(pixel); divided[i + 2] = SkColorGetB(pixel); divided[i + 3] = alpha; } i += kBytesPerPixel; } } return pixbuf; } void SubtractRectanglesFromRegion(GdkRegion* region, const std::vector& cutouts) { for (size_t i = 0; i < cutouts.size(); ++i) { GdkRectangle rect = cutouts[i].ToGdkRectangle(); GdkRegion* rect_region = gdk_region_rectangle(&rect); gdk_region_subtract(region, rect_region); // TODO(deanm): It would be nice to be able to reuse the GdkRegion here. gdk_region_destroy(rect_region); } } GdkCursor* GetCursor(int type) { CR_DEFINE_STATIC_LOCAL(GdkCursorCache, impl, ()); return impl.GetCursorImpl(static_cast(type)); } void InitRCStyles() { static const char kRCText[] = // Make our dialogs styled like the GNOME HIG. // // TODO(evanm): content-area-spacing was introduced in a later // version of GTK, so we need to set that manually on all dialogs. // Perhaps it would make sense to have a shared FixupDialog() function. "style \"gnome-dialog\" {\n" " xthickness = 12\n" " GtkDialog::action-area-border = 0\n" " GtkDialog::button-spacing = 6\n" " GtkDialog::content-area-spacing = 18\n" " GtkDialog::content-area-border = 12\n" "}\n" // Note we set it at the "application" priority, so users can override. "widget \"GtkDialog\" style : application \"gnome-dialog\"\n" // Make our about dialog special, so the image is flush with the edge. "style \"about-dialog\" {\n" " GtkDialog::action-area-border = 12\n" " GtkDialog::button-spacing = 6\n" " GtkDialog::content-area-spacing = 18\n" " GtkDialog::content-area-border = 0\n" "}\n" "widget \"about-dialog\" style : application \"about-dialog\"\n"; gtk_rc_parse_string(kRCText); } base::TimeDelta GetCursorBlinkCycle() { // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is // the default value for gtk-cursor-blink-time. static const gint kGtkDefaultCursorBlinkTime = 1200; gint cursor_blink_time = kGtkDefaultCursorBlinkTime; gboolean cursor_blink = TRUE; g_object_get(gtk_settings_get_default(), "gtk-cursor-blink-time", &cursor_blink_time, "gtk-cursor-blink", &cursor_blink, NULL); return cursor_blink ? base::TimeDelta::FromMilliseconds(cursor_blink_time) : base::TimeDelta(); } } // namespace gfx