Chromium Code Reviews| Index: ui/base/cursor/cursor_loader_x11.cc |
| diff --git a/ui/base/cursor/cursor_loader_x11.cc b/ui/base/cursor/cursor_loader_x11.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..65ba578d2736a11c81c7959a36c426417602a367 |
| --- /dev/null |
| +++ b/ui/base/cursor/cursor_loader_x11.cc |
| @@ -0,0 +1,227 @@ |
| +// 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/base/cursor/cursor_loader_x11.h" |
| + |
| +#include <X11/Xlib.h> |
| +#include <X11/cursorfont.h> |
| + |
| +#include "base/logging.h" |
| +#include "grit/ui_resources.h" |
| +#include "ui/base/cursor/cursor.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| +#include "ui/base/x/x11_util.h" |
| +#include "ui/gfx/image/image.h" |
| +#include "ui/gfx/image/image_skia.h" |
| + |
| +namespace { |
| + |
| +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.
|
| + |
| +// Returns X font cursor shape from an Aura cursor. |
| +int CursorShapeFromNative(gfx::NativeCursor native_cursor) { |
| + switch (native_cursor.native_type()) { |
| + case ui::kCursorMiddlePanning: |
| + return XC_fleur; |
| + case ui::kCursorEastPanning: |
| + return XC_sb_right_arrow; |
| + case ui::kCursorNorthPanning: |
| + return XC_sb_up_arrow; |
| + case ui::kCursorNorthEastPanning: |
| + return XC_top_right_corner; |
| + case ui::kCursorNorthWestPanning: |
| + return XC_top_left_corner; |
| + case ui::kCursorSouthPanning: |
| + return XC_sb_down_arrow; |
| + case ui::kCursorSouthEastPanning: |
| + return XC_bottom_right_corner; |
| + case ui::kCursorSouthWestPanning: |
| + return XC_bottom_left_corner; |
| + case ui::kCursorWestPanning: |
| + return XC_sb_left_arrow; |
| + case ui::kCursorNone: |
| + case ui::kCursorGrab: |
| + case ui::kCursorGrabbing: |
| + // TODO(jamescook): Need cursors for these. crbug.com/111650 |
| + return XC_left_ptr; |
| + |
| + case ui::kCursorNull: |
| + case ui::kCursorPointer: |
| + case ui::kCursorNoDrop: |
| + case ui::kCursorNotAllowed: |
| + case ui::kCursorCopy: |
| + case ui::kCursorMove: |
| + case ui::kCursorEastResize: |
| + case ui::kCursorNorthResize: |
| + case ui::kCursorSouthResize: |
| + case ui::kCursorWestResize: |
| + case ui::kCursorNorthEastResize: |
| + case ui::kCursorNorthWestResize: |
| + case ui::kCursorSouthWestResize: |
| + case ui::kCursorSouthEastResize: |
| + case ui::kCursorIBeam: |
| + case ui::kCursorAlias: |
| + case ui::kCursorCell: |
| + case ui::kCursorContextMenu: |
| + case ui::kCursorCross: |
| + case ui::kCursorHelp: |
| + case ui::kCursorWait: |
| + case ui::kCursorNorthSouthResize: |
| + case ui::kCursorEastWestResize: |
| + case ui::kCursorNorthEastSouthWestResize: |
| + case ui::kCursorNorthWestSouthEastResize: |
| + case ui::kCursorProgress: |
| + case ui::kCursorColumnResize: |
| + case ui::kCursorRowResize: |
| + case ui::kCursorVerticalText: |
| + case ui::kCursorZoomIn: |
| + case ui::kCursorZoomOut: |
| + NOTREACHED() << "Cursor (" << native_cursor.native_type() << ") should " |
| + << "have an image asset."; |
| + return XC_left_ptr; |
| + case ui::kCursorCustom: |
| + NOTREACHED(); |
| + return XC_left_ptr; |
| + } |
| + NOTREACHED(); |
| + return XC_left_ptr; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace ui { |
| + |
| +CursorLoader* CursorLoader::Create() { |
| + return new CursorLoaderX11; |
| +} |
| + |
| +CursorLoaderX11::CursorLoaderX11() |
| + : invisible_cursor_(0U) { |
| + Display* xdisplay = ui::GetXDisplay(); |
| + // Initialize invisible cursor. |
| + char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; |
| + XColor black; |
| + black.red = black.green = black.blue = 0; |
| + Pixmap blank = XCreateBitmapFromData(xdisplay, DefaultRootWindow(xdisplay), |
| + nodata, 8, 8); |
| + invisible_cursor_ = XCreatePixmapCursor(xdisplay, blank, blank, |
| + &black, &black, 0, 0); |
| + XFreePixmap(xdisplay, blank); |
| +} |
| + |
| +CursorLoaderX11::~CursorLoaderX11() { |
| + UnloadAll(); |
| + // Clears XCursorCache. |
| + ui::GetXCursor(ui::kCursorClearXCursorCache); |
| + |
| + XFreeCursor(ui::GetXDisplay(), invisible_cursor_); |
| +} |
| + |
| +void CursorLoaderX11::LoadImageCursor( |
| + int id, int resource_id, int hot_x, int hot_y) { |
| + const gfx::ImageSkia* image = |
| + ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); |
| + const gfx::ImageSkiaRep& image_rep = image->GetRepresentation( |
| + ui::GetScaleFactorFromScale(device_scale_factor())); |
| + gfx::Point hot(hot_x * device_scale_factor(), hot_y * device_scale_factor()); |
| + XcursorImage* x_image = |
| + ui::SkBitmapToXcursorImage(&image_rep.sk_bitmap(), hot); |
| + cursors_[id] = ui::CreateReffedCustomXCursor(x_image); |
| + // |bitmap| is owned by the resource bundle. So we do not need to free it. |
| +} |
| + |
| +// Creates an animated X Cursor from an image resource and puts it in the |
| +// cursor map. The image is assumed to be a concatenation of animation frames. |
| +// Also, each frame is assumed to be square (width == height) |
| +void CursorLoaderX11::LoadAnimatedCursor( |
| + int id, int resource_id, int hot_x, int hot_y) { |
| + const gfx::ImageSkia* image = |
| + ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); |
| + const gfx::ImageSkiaRep& image_rep = image->GetRepresentation( |
| + ui::GetScaleFactorFromScale(device_scale_factor())); |
| + const SkBitmap bitmap = image_rep.sk_bitmap(); |
| + DCHECK_EQ(bitmap.config(), SkBitmap::kARGB_8888_Config); |
| + int frame_width = bitmap.height(); |
| + int frame_height = frame_width; |
| + int total_width = bitmap.width(); |
| + DCHECK_EQ(total_width % frame_width, 0); |
| + int frame_count = total_width / frame_width; |
| + DCHECK_GT(frame_count, 0); |
| + XcursorImages* x_images = XcursorImagesCreate(frame_count); |
| + x_images->nimage = frame_count; |
| + bitmap.lockPixels(); |
| + unsigned int* pixels = bitmap.getAddr32(0, 0); |
| + // Create each frame. |
| + for (int i = 0; i < frame_count; ++i) { |
| + XcursorImage* x_image = XcursorImageCreate(frame_width, frame_height); |
| + for (int j = 0; j < frame_height; ++j) { |
| + // Copy j'th row of i'th frame. |
| + memcpy(x_image->pixels + j * frame_width, |
| + pixels + i * frame_width + j * total_width, |
| + frame_width * 4); |
| + } |
| + x_image->xhot = hot_x * device_scale_factor(); |
| + x_image->yhot = hot_y * device_scale_factor(); |
| + x_image->delay = kAnimatedCursorFrameDelayMs; |
| + x_images->images[i] = x_image; |
| + } |
| + bitmap.unlockPixels(); |
| + |
| + animated_cursors_[id] = std::make_pair( |
| + XcursorImagesLoadCursor(ui::GetXDisplay(), x_images), x_images); |
| + // |bitmap| is owned by the resource bundle. So we do not need to free it. |
| +} |
| + |
| +void CursorLoaderX11::UnloadAll() { |
| + for (ImageCursorMap::const_iterator it = cursors_.begin(); |
| + it != cursors_.end(); ++it) |
| + ui::UnrefCustomXCursor(it->second); |
| + |
| + // Free animated cursors and images. |
| + for (AnimatedCursorMap::iterator it = animated_cursors_.begin(); |
| + it != animated_cursors_.end(); ++it) { |
| + XcursorImagesDestroy(it->second.second); // also frees individual frames. |
| + XFreeCursor(ui::GetXDisplay(), it->second.first); |
| + } |
| +} |
| + |
| +void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) { |
| + DCHECK(cursor); |
| + |
| + ::Cursor xcursor; |
| + if (IsImageCursor(*cursor)) |
| + xcursor = ImageCursorFromNative(*cursor); |
| + else if (*cursor == ui::kCursorNone) |
| + xcursor = invisible_cursor_; |
|
Daniel Erat
2012/09/06 23:47:15
nit: delete extra space after '='
mazda
2012/09/07 02:20:57
Done.
|
| + else if (*cursor == ui::kCursorCustom) |
| + xcursor = cursor->platform(); |
| + else if (device_scale_factor() == 1.0f) |
| + xcursor = ui::GetXCursor(CursorShapeFromNative(*cursor)); |
| + else |
| + xcursor = ImageCursorFromNative(ui::kCursorPointer); |
| + |
| + cursor->SetPlatformCursor(xcursor); |
| +} |
| + |
| +void CursorLoaderX11::HideHostCursor() { |
| + XDefineCursor(ui::GetXDisplay(), DefaultRootWindow(ui::GetXDisplay()), |
| + invisible_cursor_); |
| +} |
| + |
| +bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) { |
| + int type = native_cursor.native_type(); |
| + return cursors_.find(type) != cursors_.end() || |
| + animated_cursors_.find(type) != animated_cursors_.end(); |
| +} |
| + |
| +::Cursor CursorLoaderX11::ImageCursorFromNative( |
| + gfx::NativeCursor native_cursor) { |
| + int type = native_cursor.native_type(); |
| + 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.
|
| + return animated_cursors_[type].first; |
| + DCHECK(cursors_.find(type) != cursors_.end()); |
| + return cursors_[type]; |
| +} |
| + |
| +} |