Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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/base/cursor/cursor_loader_x11.h" | |
| 6 | |
| 7 #include <X11/Xlib.h> | |
| 8 #include <X11/cursorfont.h> | |
| 9 | |
| 10 #include "base/logging.h" | |
| 11 #include "grit/ui_resources.h" | |
| 12 #include "ui/base/cursor/cursor.h" | |
| 13 #include "ui/base/resource/resource_bundle.h" | |
| 14 #include "ui/base/x/x11_util.h" | |
| 15 #include "ui/gfx/image/image.h" | |
| 16 #include "ui/gfx/image/image_skia.h" | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 const int kAnimatedCursorFrameDelayMs = 25; | |
|
Daniel Erat
2012/09/06 23:47:15
This seems like it should be a parameter to LoadAn
mazda
2012/09/07 02:20:57
Done.
| |
| 21 | |
| 22 // Returns X font cursor shape from an Aura cursor. | |
| 23 int CursorShapeFromNative(gfx::NativeCursor native_cursor) { | |
| 24 switch (native_cursor.native_type()) { | |
| 25 case ui::kCursorMiddlePanning: | |
| 26 return XC_fleur; | |
| 27 case ui::kCursorEastPanning: | |
| 28 return XC_sb_right_arrow; | |
| 29 case ui::kCursorNorthPanning: | |
| 30 return XC_sb_up_arrow; | |
| 31 case ui::kCursorNorthEastPanning: | |
| 32 return XC_top_right_corner; | |
| 33 case ui::kCursorNorthWestPanning: | |
| 34 return XC_top_left_corner; | |
| 35 case ui::kCursorSouthPanning: | |
| 36 return XC_sb_down_arrow; | |
| 37 case ui::kCursorSouthEastPanning: | |
| 38 return XC_bottom_right_corner; | |
| 39 case ui::kCursorSouthWestPanning: | |
| 40 return XC_bottom_left_corner; | |
| 41 case ui::kCursorWestPanning: | |
| 42 return XC_sb_left_arrow; | |
| 43 case ui::kCursorNone: | |
| 44 case ui::kCursorGrab: | |
| 45 case ui::kCursorGrabbing: | |
| 46 // TODO(jamescook): Need cursors for these. crbug.com/111650 | |
| 47 return XC_left_ptr; | |
| 48 | |
| 49 case ui::kCursorNull: | |
| 50 case ui::kCursorPointer: | |
| 51 case ui::kCursorNoDrop: | |
| 52 case ui::kCursorNotAllowed: | |
| 53 case ui::kCursorCopy: | |
| 54 case ui::kCursorMove: | |
| 55 case ui::kCursorEastResize: | |
| 56 case ui::kCursorNorthResize: | |
| 57 case ui::kCursorSouthResize: | |
| 58 case ui::kCursorWestResize: | |
| 59 case ui::kCursorNorthEastResize: | |
| 60 case ui::kCursorNorthWestResize: | |
| 61 case ui::kCursorSouthWestResize: | |
| 62 case ui::kCursorSouthEastResize: | |
| 63 case ui::kCursorIBeam: | |
| 64 case ui::kCursorAlias: | |
| 65 case ui::kCursorCell: | |
| 66 case ui::kCursorContextMenu: | |
| 67 case ui::kCursorCross: | |
| 68 case ui::kCursorHelp: | |
| 69 case ui::kCursorWait: | |
| 70 case ui::kCursorNorthSouthResize: | |
| 71 case ui::kCursorEastWestResize: | |
| 72 case ui::kCursorNorthEastSouthWestResize: | |
| 73 case ui::kCursorNorthWestSouthEastResize: | |
| 74 case ui::kCursorProgress: | |
| 75 case ui::kCursorColumnResize: | |
| 76 case ui::kCursorRowResize: | |
| 77 case ui::kCursorVerticalText: | |
| 78 case ui::kCursorZoomIn: | |
| 79 case ui::kCursorZoomOut: | |
| 80 NOTREACHED() << "Cursor (" << native_cursor.native_type() << ") should " | |
| 81 << "have an image asset."; | |
| 82 return XC_left_ptr; | |
| 83 case ui::kCursorCustom: | |
| 84 NOTREACHED(); | |
| 85 return XC_left_ptr; | |
| 86 } | |
| 87 NOTREACHED(); | |
| 88 return XC_left_ptr; | |
| 89 } | |
| 90 | |
| 91 } // namespace | |
| 92 | |
| 93 namespace ui { | |
| 94 | |
| 95 CursorLoader* CursorLoader::Create() { | |
| 96 return new CursorLoaderX11; | |
| 97 } | |
| 98 | |
| 99 CursorLoaderX11::CursorLoaderX11() | |
| 100 : invisible_cursor_(0U) { | |
| 101 Display* xdisplay = ui::GetXDisplay(); | |
| 102 // Initialize invisible cursor. | |
| 103 char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; | |
| 104 XColor black; | |
| 105 black.red = black.green = black.blue = 0; | |
| 106 Pixmap blank = XCreateBitmapFromData(xdisplay, DefaultRootWindow(xdisplay), | |
| 107 nodata, 8, 8); | |
| 108 invisible_cursor_ = XCreatePixmapCursor(xdisplay, blank, blank, | |
| 109 &black, &black, 0, 0); | |
| 110 XFreePixmap(xdisplay, blank); | |
| 111 } | |
| 112 | |
| 113 CursorLoaderX11::~CursorLoaderX11() { | |
| 114 UnloadAll(); | |
| 115 // Clears XCursorCache. | |
| 116 ui::GetXCursor(ui::kCursorClearXCursorCache); | |
| 117 | |
| 118 XFreeCursor(ui::GetXDisplay(), invisible_cursor_); | |
| 119 } | |
| 120 | |
| 121 void CursorLoaderX11::LoadImageCursor( | |
| 122 int id, int resource_id, int hot_x, int hot_y) { | |
| 123 const gfx::ImageSkia* image = | |
| 124 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); | |
| 125 const gfx::ImageSkiaRep& image_rep = image->GetRepresentation( | |
| 126 ui::GetScaleFactorFromScale(device_scale_factor())); | |
| 127 gfx::Point hot(hot_x * device_scale_factor(), hot_y * device_scale_factor()); | |
| 128 XcursorImage* x_image = | |
| 129 ui::SkBitmapToXcursorImage(&image_rep.sk_bitmap(), hot); | |
| 130 cursors_[id] = ui::CreateReffedCustomXCursor(x_image); | |
| 131 // |bitmap| is owned by the resource bundle. So we do not need to free it. | |
| 132 } | |
| 133 | |
| 134 // Creates an animated X Cursor from an image resource and puts it in the | |
| 135 // cursor map. The image is assumed to be a concatenation of animation frames. | |
| 136 // Also, each frame is assumed to be square (width == height) | |
| 137 void CursorLoaderX11::LoadAnimatedCursor( | |
| 138 int id, int resource_id, int hot_x, int hot_y) { | |
| 139 const gfx::ImageSkia* image = | |
| 140 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); | |
| 141 const gfx::ImageSkiaRep& image_rep = image->GetRepresentation( | |
| 142 ui::GetScaleFactorFromScale(device_scale_factor())); | |
| 143 const SkBitmap bitmap = image_rep.sk_bitmap(); | |
| 144 DCHECK_EQ(bitmap.config(), SkBitmap::kARGB_8888_Config); | |
| 145 int frame_width = bitmap.height(); | |
| 146 int frame_height = frame_width; | |
| 147 int total_width = bitmap.width(); | |
| 148 DCHECK_EQ(total_width % frame_width, 0); | |
| 149 int frame_count = total_width / frame_width; | |
| 150 DCHECK_GT(frame_count, 0); | |
| 151 XcursorImages* x_images = XcursorImagesCreate(frame_count); | |
| 152 x_images->nimage = frame_count; | |
| 153 bitmap.lockPixels(); | |
| 154 unsigned int* pixels = bitmap.getAddr32(0, 0); | |
| 155 // Create each frame. | |
| 156 for (int i = 0; i < frame_count; ++i) { | |
| 157 XcursorImage* x_image = XcursorImageCreate(frame_width, frame_height); | |
| 158 for (int j = 0; j < frame_height; ++j) { | |
| 159 // Copy j'th row of i'th frame. | |
| 160 memcpy(x_image->pixels + j * frame_width, | |
| 161 pixels + i * frame_width + j * total_width, | |
| 162 frame_width * 4); | |
| 163 } | |
| 164 x_image->xhot = hot_x * device_scale_factor(); | |
| 165 x_image->yhot = hot_y * device_scale_factor(); | |
| 166 x_image->delay = kAnimatedCursorFrameDelayMs; | |
| 167 x_images->images[i] = x_image; | |
| 168 } | |
| 169 bitmap.unlockPixels(); | |
| 170 | |
| 171 animated_cursors_[id] = std::make_pair( | |
| 172 XcursorImagesLoadCursor(ui::GetXDisplay(), x_images), x_images); | |
| 173 // |bitmap| is owned by the resource bundle. So we do not need to free it. | |
| 174 } | |
| 175 | |
| 176 void CursorLoaderX11::UnloadAll() { | |
| 177 for (ImageCursorMap::const_iterator it = cursors_.begin(); | |
| 178 it != cursors_.end(); ++it) | |
| 179 ui::UnrefCustomXCursor(it->second); | |
| 180 | |
| 181 // Free animated cursors and images. | |
| 182 for (AnimatedCursorMap::iterator it = animated_cursors_.begin(); | |
| 183 it != animated_cursors_.end(); ++it) { | |
| 184 XcursorImagesDestroy(it->second.second); // also frees individual frames. | |
| 185 XFreeCursor(ui::GetXDisplay(), it->second.first); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) { | |
| 190 DCHECK(cursor); | |
| 191 | |
| 192 ::Cursor xcursor; | |
| 193 if (IsImageCursor(*cursor)) | |
| 194 xcursor = ImageCursorFromNative(*cursor); | |
| 195 else if (*cursor == ui::kCursorNone) | |
| 196 xcursor = invisible_cursor_; | |
|
Daniel Erat
2012/09/06 23:47:15
nit: delete extra space after '='
mazda
2012/09/07 02:20:57
Done.
| |
| 197 else if (*cursor == ui::kCursorCustom) | |
| 198 xcursor = cursor->platform(); | |
| 199 else if (device_scale_factor() == 1.0f) | |
| 200 xcursor = ui::GetXCursor(CursorShapeFromNative(*cursor)); | |
| 201 else | |
| 202 xcursor = ImageCursorFromNative(ui::kCursorPointer); | |
| 203 | |
| 204 cursor->SetPlatformCursor(xcursor); | |
| 205 } | |
| 206 | |
| 207 void CursorLoaderX11::HideHostCursor() { | |
| 208 XDefineCursor(ui::GetXDisplay(), DefaultRootWindow(ui::GetXDisplay()), | |
| 209 invisible_cursor_); | |
| 210 } | |
| 211 | |
| 212 bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) { | |
| 213 int type = native_cursor.native_type(); | |
| 214 return cursors_.find(type) != cursors_.end() || | |
| 215 animated_cursors_.find(type) != animated_cursors_.end(); | |
| 216 } | |
| 217 | |
| 218 ::Cursor CursorLoaderX11::ImageCursorFromNative( | |
| 219 gfx::NativeCursor native_cursor) { | |
| 220 int type = native_cursor.native_type(); | |
| 221 if (animated_cursors_.find(type) != animated_cursors_.end()) | |
|
Daniel Erat
2012/09/06 23:47:15
nit: if you're not going to do anything with the i
mazda
2012/09/07 02:20:57
Done.
| |
| 222 return animated_cursors_[type].first; | |
| 223 DCHECK(cursors_.find(type) != cursors_.end()); | |
| 224 return cursors_[type]; | |
| 225 } | |
| 226 | |
| 227 } | |
| OLD | NEW |