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