1// Copyright 2014 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/ozone/platform/dri/dri_surface_factory.h"
6
7#include <errno.h>
8
9#include "base/debug/trace_event.h"
10#include "third_party/skia/include/core/SkBitmap.h"
11#include "third_party/skia/include/core/SkDevice.h"
12#include "third_party/skia/include/core/SkSurface.h"
13#include "ui/gfx/native_widget_types.h"
14#include "ui/ozone/platform/dri/dri_buffer.h"
15#include "ui/ozone/platform/dri/dri_surface.h"
16#include "ui/ozone/platform/dri/dri_util.h"
17#include "ui/ozone/platform/dri/dri_window_delegate_impl.h"
18#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
19#include "ui/ozone/platform/dri/dri_wrapper.h"
20#include "ui/ozone/platform/dri/hardware_display_controller.h"
21#include "ui/ozone/platform/dri/screen_manager.h"
22#include "ui/ozone/public/surface_ozone_canvas.h"
23
24namespace ui {
25
26namespace {
27
28// TODO(dnicoara) Read the cursor plane size from the hardware.
29const gfx::Size kCursorSize(64, 64);
30
31void UpdateCursorImage(DriBuffer* cursor, const SkBitmap& image) {
32  SkRect damage;
33  image.getBounds(&damage);
34
35  // Clear to transparent in case |image| is smaller than the canvas.
36  SkCanvas* canvas = cursor->GetCanvas();
37  canvas->clear(SK_ColorTRANSPARENT);
38
39  SkRect clip;
40  clip.set(
41      0, 0, canvas->getDeviceSize().width(), canvas->getDeviceSize().height());
42  canvas->clipRect(clip, SkRegion::kReplace_Op);
43  canvas->drawBitmapRectToRect(image, &damage, damage);
44}
45
46}  // namespace
47
48// static
49const gfx::AcceleratedWidget DriSurfaceFactory::kDefaultWidgetHandle = 1;
50
51DriSurfaceFactory::DriSurfaceFactory(DriWrapper* drm,
52                                     ScreenManager* screen_manager,
53                                     DriWindowDelegateManager* window_manager)
54    : drm_(drm),
55      screen_manager_(screen_manager),
56      window_manager_(window_manager),
57      state_(UNINITIALIZED),
58      cursor_frontbuffer_(0),
59      cursor_widget_(0),
60      cursor_frame_(0),
61      cursor_frame_delay_ms_(0) {
62}
63
64DriSurfaceFactory::~DriSurfaceFactory() {
65  if (state_ == INITIALIZED)
66    ShutdownHardware();
67}
68
69DriSurfaceFactory::HardwareState DriSurfaceFactory::InitializeHardware() {
70  if (state_ != UNINITIALIZED)
71    return state_;
72
73  if (drm_->get_fd() < 0) {
74    LOG(ERROR) << "Failed to create DRI connection";
75    state_ = FAILED;
76    return state_;
77  }
78
79  SkImageInfo info = SkImageInfo::MakeN32Premul(kCursorSize.width(),
80                                                kCursorSize.height());
81  for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) {
82    cursor_buffers_[i] = new DriBuffer(drm_);
83    if (!cursor_buffers_[i]->Initialize(info)) {
84      LOG(ERROR) << "Failed to initialize cursor buffer";
85      state_ = FAILED;
86      return state_;
87    }
88  }
89
90  state_ = INITIALIZED;
91  return state_;
92}
93
94void DriSurfaceFactory::ShutdownHardware() {
95  DCHECK(state_ == INITIALIZED);
96  state_ = UNINITIALIZED;
97}
98
99scoped_ptr<ui::SurfaceOzoneCanvas> DriSurfaceFactory::CreateCanvasForWidget(
100    gfx::AcceleratedWidget widget) {
101  DCHECK(state_ == INITIALIZED);
102
103  return scoped_ptr<ui::SurfaceOzoneCanvas>(
104      new DriSurface(window_manager_->GetWindowDelegate(widget), drm_));
105}
106
107bool DriSurfaceFactory::LoadEGLGLES2Bindings(
108      AddGLLibraryCallback add_gl_library,
109      SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
110  return false;
111}
112
113void DriSurfaceFactory::SetHardwareCursor(gfx::AcceleratedWidget widget,
114                                          const std::vector<SkBitmap>& bitmaps,
115                                          const gfx::Point& location,
116                                          int frame_delay_ms) {
117  cursor_widget_ = widget;
118  cursor_bitmaps_ = bitmaps;
119  cursor_location_ = location;
120  cursor_frame_ = 0;
121  cursor_frame_delay_ms_ = frame_delay_ms;
122  cursor_timer_.Stop();
123
124  if (cursor_frame_delay_ms_)
125    cursor_timer_.Start(
126        FROM_HERE,
127        base::TimeDelta::FromMilliseconds(cursor_frame_delay_ms_),
128        this,
129        &DriSurfaceFactory::OnCursorAnimationTimeout);
130
131  if (state_ != INITIALIZED)
132    return;
133
134  ResetCursor();
135}
136
137void DriSurfaceFactory::MoveHardwareCursor(gfx::AcceleratedWidget widget,
138                                           const gfx::Point& location) {
139  cursor_location_ = location;
140
141  if (state_ != INITIALIZED)
142    return;
143
144  HardwareDisplayController* controller =
145      window_manager_->GetWindowDelegate(widget)->GetController();
146  if (controller)
147    controller->MoveCursor(location);
148}
149
150////////////////////////////////////////////////////////////////////////////////
151// DriSurfaceFactory private
152
153void DriSurfaceFactory::ResetCursor() {
154  if (!cursor_widget_)
155    return;
156
157  HardwareDisplayController* controller =
158      window_manager_->GetWindowDelegate(cursor_widget_)->GetController();
159  if (cursor_bitmaps_.size()) {
160    // Draw new cursor into backbuffer.
161    UpdateCursorImage(cursor_buffers_[cursor_frontbuffer_ ^ 1].get(),
162                      cursor_bitmaps_[cursor_frame_]);
163
164    // Reset location & buffer.
165    if (controller) {
166      controller->MoveCursor(cursor_location_);
167      controller->SetCursor(cursor_buffers_[cursor_frontbuffer_ ^ 1]);
168      cursor_frontbuffer_ ^= 1;
169    }
170  } else {
171    // No cursor set.
172    if (controller)
173      controller->UnsetCursor();
174  }
175}
176
177void DriSurfaceFactory::OnCursorAnimationTimeout() {
178  cursor_frame_++;
179  cursor_frame_ %= cursor_bitmaps_.size();
180
181  ResetCursor();
182}
183
184}  // namespace ui
185