| // Copyright 2014 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/drm/gpu/drm_window.h" | 
 |  | 
 | #include "base/trace_event/trace_event.h" | 
 | #include "third_party/skia/include/core/SkBitmap.h" | 
 | #include "third_party/skia/include/core/SkDevice.h" | 
 | #include "third_party/skia/include/core/SkSurface.h" | 
 | #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" | 
 | #include "ui/ozone/platform/drm/gpu/drm_buffer.h" | 
 | #include "ui/ozone/platform/drm/gpu/drm_device.h" | 
 | #include "ui/ozone/platform/drm/gpu/drm_device_manager.h" | 
 | #include "ui/ozone/platform/drm/gpu/scanout_buffer.h" | 
 | #include "ui/ozone/platform/drm/gpu/screen_manager.h" | 
 |  | 
 | namespace ui { | 
 |  | 
 | namespace { | 
 |  | 
 | #ifndef DRM_CAP_CURSOR_WIDTH | 
 | #define DRM_CAP_CURSOR_WIDTH 0x8 | 
 | #endif | 
 |  | 
 | #ifndef DRM_CAP_CURSOR_HEIGHT | 
 | #define DRM_CAP_CURSOR_HEIGHT 0x9 | 
 | #endif | 
 |  | 
 | void EmptyFlipCallback(gfx::SwapResult) { | 
 | } | 
 |  | 
 | void UpdateCursorImage(DrmBuffer* cursor, const SkBitmap& image) { | 
 |   SkRect damage; | 
 |   image.getBounds(&damage); | 
 |  | 
 |   // Clear to transparent in case |image| is smaller than the canvas. | 
 |   SkCanvas* canvas = cursor->GetCanvas(); | 
 |   canvas->clear(SK_ColorTRANSPARENT); | 
 |  | 
 |   SkRect clip; | 
 |   clip.set(0, 0, canvas->getDeviceSize().width(), | 
 |            canvas->getDeviceSize().height()); | 
 |   canvas->clipRect(clip, SkRegion::kReplace_Op); | 
 |   canvas->drawBitmapRect(image, damage, damage, nullptr); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | DrmWindow::DrmWindow(gfx::AcceleratedWidget widget, | 
 |                      DrmDeviceManager* device_manager, | 
 |                      ScreenManager* screen_manager) | 
 |     : widget_(widget), | 
 |       device_manager_(device_manager), | 
 |       screen_manager_(screen_manager) { | 
 | } | 
 |  | 
 | DrmWindow::~DrmWindow() { | 
 | } | 
 |  | 
 | void DrmWindow::Initialize() { | 
 |   TRACE_EVENT1("drm", "DrmWindow::Initialize", "widget", widget_); | 
 |  | 
 |   device_manager_->UpdateDrmDevice(widget_, nullptr); | 
 | } | 
 |  | 
 | void DrmWindow::Shutdown() { | 
 |   TRACE_EVENT1("drm", "DrmWindow::Shutdown", "widget", widget_); | 
 |   device_manager_->RemoveDrmDevice(widget_); | 
 | } | 
 |  | 
 | gfx::AcceleratedWidget DrmWindow::GetAcceleratedWidget() { | 
 |   return widget_; | 
 | } | 
 |  | 
 | HardwareDisplayController* DrmWindow::GetController() { | 
 |   return controller_; | 
 | } | 
 |  | 
 | void DrmWindow::OnBoundsChanged(const gfx::Rect& bounds) { | 
 |   TRACE_EVENT2("drm", "DrmWindow::OnBoundsChanged", "widget", widget_, "bounds", | 
 |                bounds.ToString()); | 
 |   bounds_ = bounds; | 
 |   if (bounds_.size() != bounds.size()) | 
 |     last_submitted_planes_.clear(); | 
 |  | 
 |   screen_manager_->UpdateControllerToWindowMapping(); | 
 | } | 
 |  | 
 | void DrmWindow::SetCursor(const std::vector<SkBitmap>& bitmaps, | 
 |                           const gfx::Point& location, | 
 |                           int frame_delay_ms) { | 
 |   cursor_bitmaps_ = bitmaps; | 
 |   cursor_location_ = location; | 
 |   cursor_frame_ = 0; | 
 |   cursor_frame_delay_ms_ = frame_delay_ms; | 
 |   cursor_timer_.Stop(); | 
 |  | 
 |   if (cursor_frame_delay_ms_) | 
 |     cursor_timer_.Start( | 
 |         FROM_HERE, base::TimeDelta::FromMilliseconds(cursor_frame_delay_ms_), | 
 |         this, &DrmWindow::OnCursorAnimationTimeout); | 
 |  | 
 |   ResetCursor(false); | 
 | } | 
 |  | 
 | void DrmWindow::SetCursorWithoutAnimations(const std::vector<SkBitmap>& bitmaps, | 
 |                                            const gfx::Point& location) { | 
 |   cursor_bitmaps_ = bitmaps; | 
 |   cursor_location_ = location; | 
 |   cursor_frame_ = 0; | 
 |   cursor_frame_delay_ms_ = 0; | 
 |   ResetCursor(false); | 
 | } | 
 |  | 
 | void DrmWindow::MoveCursor(const gfx::Point& location) { | 
 |   cursor_location_ = location; | 
 |  | 
 |   if (controller_) | 
 |     controller_->MoveCursor(location); | 
 | } | 
 |  | 
 | void DrmWindow::QueueOverlayPlane(const OverlayPlane& plane) { | 
 |   pending_planes_.push_back(plane); | 
 | } | 
 |  | 
 | bool DrmWindow::SchedulePageFlip(bool is_sync, | 
 |                                  const SwapCompletionCallback& callback) { | 
 |   last_submitted_planes_.clear(); | 
 |   last_submitted_planes_.swap(pending_planes_); | 
 |   last_swap_sync_ = is_sync; | 
 |  | 
 |   if (controller_) { | 
 |     return controller_->SchedulePageFlip(last_submitted_planes_, is_sync, false, | 
 |                                          callback); | 
 |   } | 
 |  | 
 |   callback.Run(gfx::SwapResult::SWAP_ACK); | 
 |   return true; | 
 | } | 
 |  | 
 | bool DrmWindow::TestPageFlip(const std::vector<OverlayCheck_Params>& overlays, | 
 |                              ScanoutBufferGenerator* buffer_generator) { | 
 |   if (!controller_) | 
 |     return true; | 
 |   for (const auto& overlay : overlays) { | 
 |     // It is possible that the cc rect we get actually falls off the edge of | 
 |     // the screen. Usually this is prevented via things like status bars | 
 |     // blocking overlaying or cc clipping it, but in case it wasn't properly | 
 |     // clipped (since GL will render this situation fine) just ignore it here. | 
 |     // This should be an extremely rare occurrance. | 
 |     if (overlay.plane_z_order != 0 && !bounds().Contains(overlay.display_rect)) | 
 |       return false; | 
 |   } | 
 |  | 
 |   scoped_refptr<DrmDevice> drm = controller_->GetAllocationDrmDevice(); | 
 |   OverlayPlaneList planes; | 
 |   for (const auto& overlay : overlays) { | 
 |     gfx::Size size = | 
 |         (overlay.plane_z_order == 0) ? bounds().size() : overlay.buffer_size; | 
 |     scoped_refptr<ScanoutBuffer> buffer = buffer_generator->Create(drm, size); | 
 |     if (!buffer) | 
 |       return false; | 
 |     planes.push_back(OverlayPlane(buffer, overlay.plane_z_order, | 
 |                                   overlay.transform, overlay.display_rect, | 
 |                                   gfx::RectF(gfx::Size(1, 1)))); | 
 |   } | 
 |   return controller_->SchedulePageFlip(planes, true, true, | 
 |                                        base::Bind(&EmptyFlipCallback)); | 
 | } | 
 |  | 
 | const OverlayPlane* DrmWindow::GetLastModesetBuffer() { | 
 |   return OverlayPlane::GetPrimaryPlane(last_submitted_planes_); | 
 | } | 
 |  | 
 | void DrmWindow::ResetCursor(bool bitmap_only) { | 
 |   if (!controller_) | 
 |     return; | 
 |  | 
 |   if (cursor_bitmaps_.size()) { | 
 |     // Draw new cursor into backbuffer. | 
 |     UpdateCursorImage(cursor_buffers_[cursor_frontbuffer_ ^ 1].get(), | 
 |                       cursor_bitmaps_[cursor_frame_]); | 
 |  | 
 |     // Reset location & buffer. | 
 |     if (!bitmap_only) | 
 |       controller_->MoveCursor(cursor_location_); | 
 |     controller_->SetCursor(cursor_buffers_[cursor_frontbuffer_ ^ 1]); | 
 |     cursor_frontbuffer_ ^= 1; | 
 |   } else { | 
 |     // No cursor set. | 
 |     controller_->UnsetCursor(); | 
 |   } | 
 | } | 
 |  | 
 | void DrmWindow::OnCursorAnimationTimeout() { | 
 |   cursor_frame_++; | 
 |   cursor_frame_ %= cursor_bitmaps_.size(); | 
 |  | 
 |   ResetCursor(true); | 
 | } | 
 |  | 
 | void DrmWindow::SetController(HardwareDisplayController* controller) { | 
 |   if (controller_ == controller) | 
 |     return; | 
 |  | 
 |   controller_ = controller; | 
 |   device_manager_->UpdateDrmDevice( | 
 |       widget_, controller ? controller->GetAllocationDrmDevice() : nullptr); | 
 |  | 
 |   UpdateCursorBuffers(); | 
 |   // We changed displays, so we want to update the cursor as well. | 
 |   ResetCursor(false /* bitmap_only */); | 
 | } | 
 |  | 
 | void DrmWindow::UpdateCursorBuffers() { | 
 |   if (!controller_) { | 
 |     for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) { | 
 |       cursor_buffers_[i] = nullptr; | 
 |     } | 
 |   } else { | 
 |     scoped_refptr<DrmDevice> drm = controller_->GetAllocationDrmDevice(); | 
 |  | 
 |     uint64_t cursor_width = 64; | 
 |     uint64_t cursor_height = 64; | 
 |     drm->GetCapability(DRM_CAP_CURSOR_WIDTH, &cursor_width); | 
 |     drm->GetCapability(DRM_CAP_CURSOR_HEIGHT, &cursor_height); | 
 |  | 
 |     SkImageInfo info = SkImageInfo::MakeN32Premul(cursor_width, cursor_height); | 
 |     for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) { | 
 |       cursor_buffers_[i] = new DrmBuffer(drm); | 
 |       // Don't register a framebuffer for cursors since they are special (they | 
 |       // aren't modesetting buffers and drivers may fail to register them due to | 
 |       // their small sizes). | 
 |       if (!cursor_buffers_[i]->Initialize( | 
 |               info, false /* should_register_framebuffer */)) { | 
 |         LOG(FATAL) << "Failed to initialize cursor buffer"; | 
 |         return; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace ui |