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 "webkit/common/cursors/webcursor.h" 6 7 #include <gdk/gdk.h> 8 #include <gtk/gtk.h> 9 10 #include "base/logging.h" 11 #include "third_party/WebKit/public/platform/WebCursorInfo.h" 12 #include "ui/gfx/gtk_util.h" 13 14 using blink::WebCursorInfo; 15 16 namespace { 17 18 // webcursor_gtk_data.h is taken directly from WebKit's CursorGtk.h. 19 #include "webkit/common/cursors/webcursor_gtk_data.h" 20 21 // This helper function is taken directly from WebKit's CursorGtk.cpp. 22 // It attempts to create a custom cursor from the data inlined in 23 // webcursor_gtk_data.h. 24 GdkCursor* GetInlineCustomCursor(CustomCursorType type) { 25 static GdkCursor* CustomCursorsGdk[G_N_ELEMENTS(CustomCursors)]; 26 GdkCursor* cursor = CustomCursorsGdk[type]; 27 if (cursor) 28 return cursor; 29 const CustomCursor& custom = CustomCursors[type]; 30 cursor = gdk_cursor_new_from_name(gdk_display_get_default(), custom.name); 31 if (!cursor) { 32 const GdkColor fg = { 0, 0, 0, 0 }; 33 const GdkColor bg = { 65535, 65535, 65535, 65535 }; 34 GdkPixmap* source = gdk_bitmap_create_from_data( 35 NULL, reinterpret_cast<const gchar*>(custom.bits), 32, 32); 36 GdkPixmap* mask = gdk_bitmap_create_from_data( 37 NULL, reinterpret_cast<const gchar*>(custom.mask_bits), 32, 32); 38 cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 39 custom.hot_x, custom.hot_y); 40 g_object_unref(source); 41 g_object_unref(mask); 42 } 43 CustomCursorsGdk[type] = cursor; 44 return cursor; 45 } 46 47 } // end anonymous namespace 48 49 int WebCursor::GetCursorType() const { 50 // http://library.gnome.org/devel/gdk/2.12/gdk-Cursors.html has images 51 // of the default X theme, but beware that the user's cursor theme can 52 // change everything. 53 switch (type_) { 54 case WebCursorInfo::TypePointer: 55 return GDK_LAST_CURSOR; 56 case WebCursorInfo::TypeCross: 57 return GDK_CROSS; 58 case WebCursorInfo::TypeHand: 59 return GDK_HAND2; 60 case WebCursorInfo::TypeIBeam: 61 return GDK_XTERM; 62 case WebCursorInfo::TypeWait: 63 return GDK_WATCH; 64 case WebCursorInfo::TypeHelp: 65 return GDK_QUESTION_ARROW; 66 case WebCursorInfo::TypeEastResize: 67 return GDK_RIGHT_SIDE; 68 case WebCursorInfo::TypeNorthResize: 69 return GDK_TOP_SIDE; 70 case WebCursorInfo::TypeNorthEastResize: 71 return GDK_TOP_RIGHT_CORNER; 72 case WebCursorInfo::TypeNorthWestResize: 73 return GDK_TOP_LEFT_CORNER; 74 case WebCursorInfo::TypeSouthResize: 75 return GDK_BOTTOM_SIDE; 76 case WebCursorInfo::TypeSouthEastResize: 77 return GDK_BOTTOM_RIGHT_CORNER; 78 case WebCursorInfo::TypeSouthWestResize: 79 return GDK_BOTTOM_LEFT_CORNER; 80 case WebCursorInfo::TypeWestResize: 81 return GDK_LEFT_SIDE; 82 case WebCursorInfo::TypeNorthSouthResize: 83 return GDK_SB_V_DOUBLE_ARROW; 84 case WebCursorInfo::TypeEastWestResize: 85 return GDK_SB_H_DOUBLE_ARROW; 86 case WebCursorInfo::TypeNorthEastSouthWestResize: 87 case WebCursorInfo::TypeNorthWestSouthEastResize: 88 // There isn't really a useful cursor available for these. 89 return GDK_LAST_CURSOR; 90 case WebCursorInfo::TypeColumnResize: 91 return GDK_SB_H_DOUBLE_ARROW; // TODO(evanm): is this correct? 92 case WebCursorInfo::TypeRowResize: 93 return GDK_SB_V_DOUBLE_ARROW; // TODO(evanm): is this correct? 94 case WebCursorInfo::TypeMiddlePanning: 95 return GDK_FLEUR; 96 case WebCursorInfo::TypeEastPanning: 97 return GDK_SB_RIGHT_ARROW; 98 case WebCursorInfo::TypeNorthPanning: 99 return GDK_SB_UP_ARROW; 100 case WebCursorInfo::TypeNorthEastPanning: 101 return GDK_TOP_RIGHT_CORNER; 102 case WebCursorInfo::TypeNorthWestPanning: 103 return GDK_TOP_LEFT_CORNER; 104 case WebCursorInfo::TypeSouthPanning: 105 return GDK_SB_DOWN_ARROW; 106 case WebCursorInfo::TypeSouthEastPanning: 107 return GDK_BOTTOM_RIGHT_CORNER; 108 case WebCursorInfo::TypeSouthWestPanning: 109 return GDK_BOTTOM_LEFT_CORNER; 110 case WebCursorInfo::TypeWestPanning: 111 return GDK_SB_LEFT_ARROW; 112 case WebCursorInfo::TypeMove: 113 return GDK_FLEUR; 114 case WebCursorInfo::TypeVerticalText: 115 return GDK_LAST_CURSOR; 116 case WebCursorInfo::TypeCell: 117 return GDK_LAST_CURSOR; 118 case WebCursorInfo::TypeContextMenu: 119 return GDK_LAST_CURSOR; 120 case WebCursorInfo::TypeAlias: 121 return GDK_LAST_CURSOR; 122 case WebCursorInfo::TypeProgress: 123 return GDK_WATCH; 124 case WebCursorInfo::TypeNoDrop: 125 return GDK_LAST_CURSOR; 126 case WebCursorInfo::TypeCopy: 127 return GDK_LAST_CURSOR; 128 case WebCursorInfo::TypeNone: 129 return GDK_BLANK_CURSOR; 130 case WebCursorInfo::TypeNotAllowed: 131 return GDK_LAST_CURSOR; 132 case WebCursorInfo::TypeZoomIn: 133 case WebCursorInfo::TypeZoomOut: 134 case WebCursorInfo::TypeGrab: 135 case WebCursorInfo::TypeGrabbing: 136 case WebCursorInfo::TypeCustom: 137 return GDK_CURSOR_IS_PIXMAP; 138 } 139 NOTREACHED(); 140 return GDK_LAST_CURSOR; 141 } 142 143 gfx::NativeCursor WebCursor::GetNativeCursor() { 144 int type = GetCursorType(); 145 if (type == GDK_CURSOR_IS_PIXMAP) 146 return GetCustomCursor(); 147 return gfx::GetCursor(type); 148 } 149 150 GdkCursor* WebCursor::GetCustomCursor() { 151 switch (type_) { 152 case WebCursorInfo::TypeZoomIn: 153 return GetInlineCustomCursor(CustomCursorZoomIn); 154 case WebCursorInfo::TypeZoomOut: 155 return GetInlineCustomCursor(CustomCursorZoomOut); 156 case WebCursorInfo::TypeGrab: 157 return GetInlineCustomCursor(CustomCursorGrab); 158 case WebCursorInfo::TypeGrabbing: 159 return GetInlineCustomCursor(CustomCursorGrabbing); 160 } 161 162 if (type_ != WebCursorInfo::TypeCustom) { 163 NOTREACHED(); 164 return NULL; 165 } 166 167 if (custom_size_.width() == 0 || custom_size_.height() == 0) { 168 // Some websites specify cursor images that are 0 sized, such as Bing Maps. 169 // Don't crash on this; just use the default cursor. 170 return NULL; 171 } 172 173 SkBitmap bitmap; 174 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 175 custom_size_.width(), custom_size_.height()); 176 bitmap.allocPixels(); 177 memcpy(bitmap.getAddr32(0, 0), custom_data_.data(), custom_data_.size()); 178 179 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(bitmap); 180 GdkCursor* cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), 181 pixbuf, 182 hotspot_.x(), 183 hotspot_.y()); 184 185 g_object_unref(pixbuf); 186 187 if (unref_) 188 gdk_cursor_unref(unref_); 189 unref_ = cursor; 190 return cursor; 191 } 192 193 void WebCursor::InitPlatformData() { 194 unref_ = NULL; 195 return; 196 } 197 198 bool WebCursor::SerializePlatformData(Pickle* pickle) const { 199 return true; 200 } 201 202 bool WebCursor::DeserializePlatformData(PickleIterator* iter) { 203 return true; 204 } 205 206 bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const { 207 return true; 208 } 209 210 void WebCursor::CleanupPlatformData() { 211 if (unref_) { 212 gdk_cursor_unref(unref_); 213 unref_ = NULL; 214 } 215 return; 216 } 217 218 void WebCursor::CopyPlatformData(const WebCursor& other) { 219 if (other.unref_) 220 unref_ = gdk_cursor_ref(other.unref_); 221 return; 222 } 223