blob: e9fb7a7ee7c341b68fed1c37bc3d73ca769e25d4 [file] [log] [blame]
// 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 <float.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include "base/logging.h"
#include "skia/ext/image_operations.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/cursor/cursor_util.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/point_conversions.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/skbitmap_operations.h"
#include "ui/gfx/skia_util.h"
namespace {
// Returns X font cursor shape from an Aura cursor.
int CursorShapeFromNative(const 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;
#if defined(OS_CHROMEOS)
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:
case ui::kCursorHand:
// In some environments, the image assets are not set (e.g. in
// content-browsertests, content-shell etc.).
return XC_left_ptr;
#else // defined(OS_CHROMEOS)
case ui::kCursorNull:
return XC_left_ptr;
case ui::kCursorPointer:
return XC_left_ptr;
case ui::kCursorMove:
return XC_fleur;
case ui::kCursorCross:
return XC_crosshair;
case ui::kCursorHand:
return XC_hand2;
case ui::kCursorIBeam:
return XC_xterm;
case ui::kCursorProgress:
case ui::kCursorWait:
return XC_watch;
case ui::kCursorHelp:
return XC_question_arrow;
case ui::kCursorEastResize:
return XC_right_side;
case ui::kCursorNorthResize:
return XC_top_side;
case ui::kCursorNorthEastResize:
return XC_top_right_corner;
case ui::kCursorNorthWestResize:
return XC_top_left_corner;
case ui::kCursorSouthResize:
return XC_bottom_side;
case ui::kCursorSouthEastResize:
return XC_bottom_right_corner;
case ui::kCursorSouthWestResize:
return XC_bottom_left_corner;
case ui::kCursorWestResize:
return XC_left_side;
case ui::kCursorNorthSouthResize:
return XC_sb_v_double_arrow;
case ui::kCursorEastWestResize:
return XC_sb_h_double_arrow;
case ui::kCursorColumnResize:
return XC_sb_h_double_arrow;
case ui::kCursorRowResize:
return XC_sb_v_double_arrow;
#endif // defined(OS_CHROMEOS)
case ui::kCursorCustom:
NOTREACHED();
return XC_left_ptr;
}
NOTREACHED() << "Case not handled for " << native_cursor.native_type();
return XC_left_ptr;
}
} // namespace
namespace ui {
CursorLoader* CursorLoader::Create() {
return new CursorLoaderX11;
}
CursorLoaderX11::CursorLoaderX11()
: invisible_cursor_(CreateInvisibleCursor(), gfx::GetXDisplay()) {
}
CursorLoaderX11::~CursorLoaderX11() {
UnloadAll();
}
void CursorLoaderX11::LoadImageCursor(int id,
int resource_id,
const gfx::Point& hot) {
SkBitmap bitmap;
gfx::Point hotspot = hot;
GetImageCursorBitmap(resource_id, scale(), rotation(), &hotspot, &bitmap);
XcursorImage* x_image = SkBitmapToXcursorImage(&bitmap, hotspot);
cursors_[id] = CreateReffedCustomXCursor(x_image);
}
void CursorLoaderX11::LoadAnimatedCursor(int id,
int resource_id,
const gfx::Point& hot,
int frame_delay_ms) {
std::vector<SkBitmap> bitmaps;
gfx::Point hotspot = hot;
GetAnimatedCursorBitmaps(
resource_id, scale(), rotation(), &hotspot, &bitmaps);
XcursorImages* x_images = XcursorImagesCreate(bitmaps.size());
x_images->nimage = bitmaps.size();
for (unsigned int frame = 0; frame < bitmaps.size(); ++frame) {
XcursorImage* x_image = SkBitmapToXcursorImage(&bitmaps[frame], hotspot);
x_image->delay = frame_delay_ms;
x_images->images[frame] = x_image;
}
animated_cursors_[id] = std::make_pair(
XcursorImagesLoadCursor(gfx::GetXDisplay(), x_images), x_images);
}
void CursorLoaderX11::UnloadAll() {
for (ImageCursorMap::const_iterator it = cursors_.begin();
it != cursors_.end(); ++it)
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(gfx::GetXDisplay(), it->second.first);
}
}
void CursorLoaderX11::SetPlatformCursor(gfx::NativeCursor* cursor) {
DCHECK(cursor);
::Cursor xcursor;
if (IsImageCursor(*cursor))
xcursor = ImageCursorFromNative(*cursor);
else if (*cursor == kCursorNone)
xcursor = invisible_cursor_.get();
else if (*cursor == kCursorCustom)
xcursor = cursor->platform();
else if (scale() == 1.0f && rotation() == gfx::Display::ROTATE_0) {
xcursor = GetXCursor(CursorShapeFromNative(*cursor));
} else {
xcursor = ImageCursorFromNative(kCursorPointer);
}
cursor->SetPlatformCursor(xcursor);
}
const XcursorImage* CursorLoaderX11::GetXcursorImageForTest(int id) {
return test::GetCachedXcursorImage(cursors_[id]);
}
bool CursorLoaderX11::IsImageCursor(gfx::NativeCursor native_cursor) {
int type = native_cursor.native_type();
return cursors_.count(type) || animated_cursors_.count(type);
}
::Cursor CursorLoaderX11::ImageCursorFromNative(
gfx::NativeCursor native_cursor) {
int type = native_cursor.native_type();
if (animated_cursors_.count(type))
return animated_cursors_[type].first;
ImageCursorMap::iterator find = cursors_.find(type);
if (find != cursors_.end())
return cursors_[type];
return GetXCursor(CursorShapeFromNative(native_cursor));
}
} // namespace ui