blob: 74681ed672bd21082c15ceff9aadea1608582961 [file] [log] [blame]
// Copyright 2017 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/ozone/platform/wayland/wayland_cursor.h"
#include <sys/mman.h>
#include <vector>
#include "base/memory/shared_memory.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/skia_util.cc"
#include "ui/ozone/platform/wayland/wayland_connection.h"
#include "ui/ozone/platform/wayland/wayland_pointer.h"
namespace ui {
namespace {
const uint32_t kShmFormat = WL_SHM_FORMAT_ARGB8888;
const SkColorType kColorType = kBGRA_8888_SkColorType;
} // namespace
WaylandCursor::WaylandCursor() : shared_memory_(new base::SharedMemory()) {}
void WaylandCursor::Init(wl_pointer* pointer, WaylandConnection* connection) {
if (input_pointer_ == pointer)
return;
input_pointer_ = pointer;
DCHECK(connection);
shm_ = connection->shm();
pointer_surface_.reset(
wl_compositor_create_surface(connection->compositor()));
}
WaylandCursor::~WaylandCursor() {
pointer_surface_.reset();
buffer_.reset();
if (shared_memory_->handle().GetHandle()) {
shared_memory_->Unmap();
shared_memory_->Close();
}
}
void WaylandCursor::UpdateBitmap(const std::vector<SkBitmap>& cursor_image,
const gfx::Point& location,
uint32_t serial) {
if (!input_pointer_)
return;
if (!cursor_image.size()) {
HideCursor(serial);
return;
}
const SkBitmap& image = cursor_image[0];
SkISize size = image.dimensions();
if (size.isEmpty()) {
HideCursor(serial);
return;
}
if (!CreateSHMBuffer(gfx::SkISizeToSize(size))) {
LOG(ERROR) << "Failed to create SHM buffer for Cursor Bitmap.";
wl_pointer_set_cursor(input_pointer_, serial, nullptr, 0, 0);
return;
}
// The |bitmap| contains ARGB image, so update our wl_buffer, which is
// backed by a SkSurface.
SkRect damage;
image.getBounds(&damage);
// Clear to transparent in case |image| is smaller than the canvas.
SkCanvas* canvas = sk_surface_->getCanvas();
canvas->clear(SK_ColorTRANSPARENT);
canvas->drawBitmapRect(image, damage, nullptr);
wl_pointer_set_cursor(input_pointer_, serial, pointer_surface_.get(),
location.x(), location.y());
wl_surface_attach(pointer_surface_.get(), buffer_.get(), 0, 0);
wl_surface_damage(pointer_surface_.get(), 0, 0, size_.width(),
size_.height());
wl_surface_commit(pointer_surface_.get());
}
bool WaylandCursor::CreateSHMBuffer(const gfx::Size& size) {
if (size == size_)
return true;
size_ = size;
SkImageInfo info = SkImageInfo::MakeN32Premul(size_.width(), size_.height());
int stride = info.minRowBytes();
size_t image_buffer_size = info.computeByteSize(stride);
if (image_buffer_size == SK_MaxSizeT)
return false;
if (shared_memory_->handle().GetHandle()) {
shared_memory_->Unmap();
shared_memory_->Close();
}
if (!shared_memory_->CreateAndMapAnonymous(image_buffer_size)) {
LOG(ERROR) << "Create and mmap failed.";
return false;
}
// TODO(tonikitoo): Use SharedMemory::requested_size instead of
// 'image_buffer_size'?
wl::Object<wl_shm_pool> pool;
pool.reset(wl_shm_create_pool(shm_, shared_memory_->handle().GetHandle(),
image_buffer_size));
buffer_.reset(wl_shm_pool_create_buffer(pool.get(), 0, size_.width(),
size_.height(), stride, kShmFormat));
sk_surface_ = SkSurface::MakeRasterDirect(
SkImageInfo::Make(size_.width(), size_.height(), kColorType,
kOpaque_SkAlphaType),
static_cast<uint8_t*>(shared_memory_->memory()), stride);
return true;
}
void WaylandCursor::HideCursor(uint32_t serial) {
size_ = gfx::Size();
wl_pointer_set_cursor(input_pointer_, serial, nullptr, 0, 0);
buffer_.reset();
if (shared_memory_->handle().GetHandle()) {
shared_memory_->Unmap();
shared_memory_->Close();
}
}
} // namespace ui