15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/gtk_util.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <gdk/gdk.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <gtk/gtk.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h> 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkUnPreMultiply.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A process wide singleton that manages our usage of gdk cursors. 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// gdk_cursor_new() hits the disk in several places and GdkCursor instances can 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// be reused throughout the process. 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class GdkCursorCache { 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkCursorCache() {} 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ~GdkCursorCache() { 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (GdkCursorMap::iterator i(cursors_.begin()); i != cursors_.end(); ++i) { 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gdk_cursor_unref(i->second); 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cursors_.clear(); 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkCursor* GetCursorImpl(GdkCursorType type) { 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkCursorMap::iterator it = cursors_.find(type); 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkCursor* cursor = NULL; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (it == cursors_.end()) { 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cursor = gdk_cursor_new(type); 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cursors_.insert(std::make_pair(type, cursor)); 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cursor = it->second; 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It is not necessary to add a reference here. The callers can ref the 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // cursor if they need it for something. 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cursor; 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) typedef std::map<GdkCursorType, GdkCursor*> GdkCursorMap; 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkCursorMap cursors_; 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(GdkCursorCache); 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace gfx { 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static void CommonInitFromCommandLine(const CommandLine& command_line, 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) void (*init_func)(gint*, gchar***)) { 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::vector<std::string>& args = command_line.argv(); 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int argc = args.size(); 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_ptr<char *[]> argv(new char *[argc + 1]); 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < args.size(); ++i) { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(piman@google.com): can gtk_init modify argv? Just being safe 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // here. 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv[i] = strdup(args[i].c_str()); 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) argv[argc] = NULL; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) char **argv_pointer = argv.get(); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) init_func(&argc, &argv_pointer); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < args.size(); ++i) { 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) free(argv[i]); 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GtkInitFromCommandLine(const CommandLine& command_line) { 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommonInitFromCommandLine(command_line, gtk_init); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GdkInitFromCommandLine(const CommandLine& command_line) { 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CommonInitFromCommandLine(command_line, gdk_init); 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) { 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (bitmap.isNull()) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkAutoLockPixels lock_pixels(bitmap); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int width = bitmap.width(); 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int height = bitmap.height(); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkPixbuf* pixbuf = gdk_pixbuf_new( 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GDK_COLORSPACE_RGB, // The only colorspace gtk supports. 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TRUE, // There is an alpha channel. 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8, 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) width, height); 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // SkBitmaps are premultiplied, we need to unpremultiply them. 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const int kBytesPerPixel = 4; 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint8* divided = gdk_pixbuf_get_pixels(pixbuf); 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int y = 0, i = 0; y < height; y++) { 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int x = 0; x < width; x++) { 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint32 pixel = bitmap.getAddr32(0, y)[x]; 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int alpha = SkColorGetA(pixel); 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (alpha != 0 && alpha != 255) { 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) divided[i + 0] = SkColorGetR(unmultiplied); 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) divided[i + 1] = SkColorGetG(unmultiplied); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) divided[i + 2] = SkColorGetB(unmultiplied); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) divided[i + 3] = alpha; 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) divided[i + 0] = SkColorGetR(pixel); 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) divided[i + 1] = SkColorGetG(pixel); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) divided[i + 2] = SkColorGetB(pixel); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) divided[i + 3] = alpha; 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i += kBytesPerPixel; 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return pixbuf; 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SubtractRectanglesFromRegion(GdkRegion* region, 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::vector<Rect>& cutouts) { 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < cutouts.size(); ++i) { 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkRectangle rect = cutouts[i].ToGdkRectangle(); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkRegion* rect_region = gdk_region_rectangle(&rect); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gdk_region_subtract(region, rect_region); 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(deanm): It would be nice to be able to reuse the GdkRegion here. 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gdk_region_destroy(rect_region); 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GdkCursor* GetCursor(int type) { 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CR_DEFINE_STATIC_LOCAL(GdkCursorCache, impl, ()); 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return impl.GetCursorImpl(static_cast<GdkCursorType>(type)); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void InitRCStyles() { 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static const char kRCText[] = 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Make our dialogs styled like the GNOME HIG. 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(evanm): content-area-spacing was introduced in a later 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // version of GTK, so we need to set that manually on all dialogs. 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Perhaps it would make sense to have a shared FixupDialog() function. 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "style \"gnome-dialog\" {\n" 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " xthickness = 12\n" 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " GtkDialog::action-area-border = 0\n" 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " GtkDialog::button-spacing = 6\n" 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " GtkDialog::content-area-spacing = 18\n" 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " GtkDialog::content-area-border = 12\n" 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "}\n" 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Note we set it at the "application" priority, so users can override. 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "widget \"GtkDialog\" style : application \"gnome-dialog\"\n" 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Make our about dialog special, so the image is flush with the edge. 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "style \"about-dialog\" {\n" 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " GtkDialog::action-area-border = 12\n" 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " GtkDialog::button-spacing = 6\n" 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " GtkDialog::content-area-spacing = 18\n" 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) " GtkDialog::content-area-border = 0\n" 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "}\n" 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "widget \"about-dialog\" style : application \"about-dialog\"\n"; 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_rc_parse_string(kRCText); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)base::TimeDelta GetCursorBlinkCycle() { 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the default value for gtk-cursor-blink-time. 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static const gint kGtkDefaultCursorBlinkTime = 1200; 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gint cursor_blink_time = kGtkDefaultCursorBlinkTime; 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gboolean cursor_blink = TRUE; 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_get(gtk_settings_get_default(), 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "gtk-cursor-blink-time", &cursor_blink_time, 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "gtk-cursor-blink", &cursor_blink, 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL); 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cursor_blink ? 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::TimeDelta::FromMilliseconds(cursor_blink_time) : 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::TimeDelta(); 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace gfx 191