blob: 81258c64f7f7d3e68d5b550229a83553f4daebd1 [file] [log] [blame]
// 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/dri/screen_manager.h"
#include <xf86drmMode.h>
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/ozone/platform/dri/crtc_controller.h"
#include "ui/ozone/platform/dri/dri_util.h"
#include "ui/ozone/platform/dri/dri_wrapper.h"
#include "ui/ozone/platform/dri/hardware_display_controller.h"
#include "ui/ozone/platform/dri/scanout_buffer.h"
namespace ui {
ScreenManager::ScreenManager(DriWrapper* dri,
ScanoutBufferGenerator* buffer_generator)
: dri_(dri), buffer_generator_(buffer_generator) {
}
ScreenManager::~ScreenManager() {
}
void ScreenManager::AddDisplayController(DriWrapper* dri,
uint32_t crtc,
uint32_t connector) {
HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
// TODO(dnicoara): Turn this into a DCHECK when async display configuration is
// properly supported. (When there can't be a race between forcing initial
// display configuration in ScreenManager and NativeDisplayDelegate creating
// the display controllers.)
if (it != controllers_.end()) {
LOG(WARNING) << "Display controller (crtc=" << crtc << ") already present.";
return;
}
controllers_.push_back(new HardwareDisplayController(
scoped_ptr<CrtcController>(new CrtcController(dri, crtc, connector))));
}
void ScreenManager::RemoveDisplayController(uint32_t crtc) {
HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
if (it != controllers_.end()) {
bool is_mirrored = (*it)->IsMirrored();
(*it)->RemoveCrtc(crtc);
if (!is_mirrored)
controllers_.erase(it);
}
}
bool ScreenManager::ConfigureDisplayController(uint32_t crtc,
uint32_t connector,
const gfx::Point& origin,
const drmModeModeInfo& mode) {
gfx::Rect modeset_bounds(origin.x(), origin.y(), mode.hdisplay,
mode.vdisplay);
HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
DCHECK(controllers_.end() != it) << "Display controller (crtc=" << crtc
<< ") doesn't exist.";
HardwareDisplayController* controller = *it;
controller = *it;
// If nothing changed just enable the controller. Note, we perform an exact
// comparison on the mode since the refresh rate may have changed.
if (SameMode(mode, controller->get_mode()) &&
origin == controller->origin() && !controller->IsDisabled())
return controller->Enable();
// Either the mode or the location of the display changed, so exit mirror
// mode and configure the display independently. If the caller still wants
// mirror mode, subsequent calls configuring the other controllers will
// restore mirror mode.
if (controller->IsMirrored()) {
controller = new HardwareDisplayController(controller->RemoveCrtc(crtc));
controllers_.push_back(controller);
it = controllers_.end() - 1;
}
HardwareDisplayControllers::iterator mirror =
FindActiveDisplayControllerByLocation(modeset_bounds);
// Handle mirror mode.
if (mirror != controllers_.end() && it != mirror)
return HandleMirrorMode(it, mirror, crtc, connector);
return ModesetDisplayController(controller, origin, mode);
}
bool ScreenManager::DisableDisplayController(uint32_t crtc) {
HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
if (it != controllers_.end()) {
if ((*it)->IsMirrored()) {
HardwareDisplayController* controller =
new HardwareDisplayController((*it)->RemoveCrtc(crtc));
controllers_.push_back(controller);
}
(*it)->Disable();
return true;
}
LOG(ERROR) << "Failed to find display controller crtc=" << crtc;
return false;
}
base::WeakPtr<HardwareDisplayController> ScreenManager::GetDisplayController(
const gfx::Rect& bounds) {
// TODO(dnicoara): Remove hack once TestScreen uses a simple Ozone display
// configuration reader and ScreenManager is called from there to create the
// one display needed by the content_shell target.
if (controllers_.empty())
ForceInitializationOfPrimaryDisplay();
HardwareDisplayControllers::iterator it =
FindActiveDisplayControllerByLocation(bounds);
if (it != controllers_.end())
return (*it)->AsWeakPtr();
return base::WeakPtr<HardwareDisplayController>();
}
ScreenManager::HardwareDisplayControllers::iterator
ScreenManager::FindDisplayController(uint32_t crtc) {
for (HardwareDisplayControllers::iterator it = controllers_.begin();
it != controllers_.end();
++it) {
if ((*it)->HasCrtc(crtc))
return it;
}
return controllers_.end();
}
ScreenManager::HardwareDisplayControllers::iterator
ScreenManager::FindActiveDisplayControllerByLocation(const gfx::Rect& bounds) {
for (HardwareDisplayControllers::iterator it = controllers_.begin();
it != controllers_.end();
++it) {
gfx::Rect controller_bounds((*it)->origin(), (*it)->GetModeSize());
// We don't perform a strict check since content_shell will have windows
// smaller than the display size.
if (controller_bounds.Contains(bounds) && !(*it)->IsDisabled())
return it;
}
return controllers_.end();
}
void ScreenManager::ForceInitializationOfPrimaryDisplay() {
LOG(WARNING) << "Forcing initialization of primary display.";
ScopedVector<HardwareDisplayControllerInfo> displays =
GetAvailableDisplayControllerInfos(dri_->get_fd());
if (displays.empty())
return;
ScopedDrmPropertyPtr dpms(
dri_->GetProperty(displays[0]->connector(), "DPMS"));
if (dpms)
dri_->SetProperty(displays[0]->connector()->connector_id,
dpms->prop_id,
DRM_MODE_DPMS_ON);
AddDisplayController(dri_, displays[0]->crtc()->crtc_id,
displays[0]->connector()->connector_id);
ConfigureDisplayController(displays[0]->crtc()->crtc_id,
displays[0]->connector()->connector_id,
gfx::Point(),
displays[0]->connector()->modes[0]);
}
bool ScreenManager::ModesetDisplayController(
HardwareDisplayController* controller,
const gfx::Point& origin,
const drmModeModeInfo& mode) {
controller->set_origin(origin);
// Create a surface suitable for the current controller.
scoped_refptr<ScanoutBuffer> buffer =
buffer_generator_->Create(gfx::Size(mode.hdisplay, mode.vdisplay));
if (!buffer.get()) {
LOG(ERROR) << "Failed to create scanout buffer";
return false;
}
if (!controller->Modeset(OverlayPlane(buffer), mode)) {
LOG(ERROR) << "Failed to modeset controller";
return false;
}
return true;
}
bool ScreenManager::HandleMirrorMode(
HardwareDisplayControllers::iterator original,
HardwareDisplayControllers::iterator mirror,
uint32_t crtc,
uint32_t connector) {
(*mirror)->AddCrtc((*original)->RemoveCrtc(crtc));
if ((*mirror)->Enable()) {
controllers_.erase(original);
return true;
}
LOG(ERROR) << "Failed to switch to mirror mode";
// When things go wrong revert back to the previous configuration since
// it is expected that the configuration would not have changed if
// things fail.
(*original)->AddCrtc((*mirror)->RemoveCrtc(crtc));
(*original)->Enable();
return false;
}
} // namespace ui