blob: 860a5ea4996905c796694331098a2cb8f882b0b3 [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 "ash/display/display_change_observer_x11.h"
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <X11/extensions/Xrandr.h>
#include "ash/ash_switches.h"
#include "ash/display/display_info.h"
#include "ash/display/display_layout_store.h"
#include "ash/display/display_manager.h"
#include "ash/display/display_util_x11.h"
#include "ash/shell.h"
#include "base/command_line.h"
#include "base/message_loop/message_pump_aurax11.h"
#include "chromeos/display/output_util.h"
#include "grit/ash_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/dip_util.h"
#include "ui/gfx/display.h"
namespace ash {
namespace internal {
namespace {
// The DPI threshold to detect high density screen.
// Higher DPI than this will use device_scale_factor=2.
const unsigned int kHighDensityDPIThreshold = 160;
// 1 inch in mm.
const float kInchInMm = 25.4f;
int64 GetDisplayId(XID output, size_t output_index) {
int64 display_id;
if (chromeos::GetDisplayId(output, output_index, &display_id))
return display_id;
return gfx::Display::kInvalidDisplayID;
}
} // namespace
DisplayChangeObserverX11::DisplayChangeObserverX11()
: xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
x_root_window_(DefaultRootWindow(xdisplay_)),
xrandr_event_base_(0) {
int error_base_ignored;
XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
Shell::GetInstance()->AddShellObserver(this);
}
DisplayChangeObserverX11::~DisplayChangeObserverX11() {
Shell::GetInstance()->RemoveShellObserver(this);
}
chromeos::OutputState DisplayChangeObserverX11::GetStateForDisplayIds(
const std::vector<int64>& display_ids) const {
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAshForceMirrorMode)) {
return chromeos::STATE_DUAL_MIRROR;
}
CHECK_EQ(2U, display_ids.size());
DisplayIdPair pair = std::make_pair(display_ids[0], display_ids[1]);
DisplayLayout layout = Shell::GetInstance()->display_manager()->
layout_store()->GetRegisteredDisplayLayout(pair);
return layout.mirrored ?
chromeos::STATE_DUAL_MIRROR : chromeos::STATE_DUAL_EXTENDED;
}
bool DisplayChangeObserverX11::GetResolutionForDisplayId(int64 display_id,
int* width,
int* height) const {
gfx::Size resolution;
if (!Shell::GetInstance()->display_manager()->
GetSelectedResolutionForDisplayId(display_id, &resolution)) {
return false;
}
*width = resolution.width();
*height = resolution.height();
return true;
}
void DisplayChangeObserverX11::OnDisplayModeChanged() {
XRRScreenResources* screen_resources =
XRRGetScreenResources(xdisplay_, x_root_window_);
std::map<XID, XRRCrtcInfo*> crtc_info_map;
for (int c = 0; c < screen_resources->ncrtc; c++) {
XID crtc_id = screen_resources->crtcs[c];
XRRCrtcInfo *crtc_info =
XRRGetCrtcInfo(xdisplay_, screen_resources, crtc_id);
crtc_info_map[crtc_id] = crtc_info;
}
std::vector<DisplayInfo> displays;
std::set<int64> ids;
for (int output_index = 0; output_index < screen_resources->noutput;
output_index++) {
XID output = screen_resources->outputs[output_index];
XRROutputInfo *output_info =
XRRGetOutputInfo(xdisplay_, screen_resources, output);
const bool is_internal = chromeos::IsInternalOutputName(
std::string(output_info->name));
if (is_internal &&
gfx::Display::InternalDisplayId() == gfx::Display::kInvalidDisplayID) {
int64 id = GetDisplayId(output, output_index);
// Fallback to output index. crbug.com/180100
gfx::Display::SetInternalDisplayId(
id == gfx::Display::kInvalidDisplayID ? output_index : id);
}
if (output_info->connection != RR_Connected) {
XRRFreeOutputInfo(output_info);
continue;
}
const XRRCrtcInfo* crtc_info = crtc_info_map[output_info->crtc];
if (!crtc_info) {
LOG(WARNING) << "Crtc not found for output: output_index="
<< output_index;
continue;
}
const XRRModeInfo* mode =
chromeos::FindModeInfo(screen_resources, crtc_info->mode);
if (!mode) {
LOG(WARNING) << "Could not find a mode for the output: output_index="
<< output_index;
continue;
}
float device_scale_factor = 1.0f;
if (!ShouldIgnoreSize(output_info->mm_width, output_info->mm_height) &&
(kInchInMm * mode->width / output_info->mm_width) >
kHighDensityDPIThreshold) {
device_scale_factor = 2.0f;
}
gfx::Rect display_bounds(
crtc_info->x, crtc_info->y, mode->width, mode->height);
std::vector<Resolution> resolutions;
if (!is_internal)
resolutions = GetResolutionList(screen_resources, output_info);
XRRFreeOutputInfo(output_info);
std::string name = is_internal ?
l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME) :
chromeos::GetDisplayName(output);
if (name.empty())
name = l10n_util::GetStringUTF8(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
bool has_overscan = false;
chromeos::GetOutputOverscanFlag(output, &has_overscan);
int64 id = GetDisplayId(output, output_index);
// If ID is invalid or there is an duplicate, just use output index.
if (id == gfx::Display::kInvalidDisplayID || ids.find(id) != ids.end())
id = output_index;
ids.insert(id);
displays.push_back(DisplayInfo(id, name, has_overscan));
displays.back().set_device_scale_factor(device_scale_factor);
displays.back().SetBounds(display_bounds);
displays.back().set_native(true);
displays.back().set_resolutions(resolutions);
}
// Free all allocated resources.
for (std::map<XID, XRRCrtcInfo*>::const_iterator iter = crtc_info_map.begin();
iter != crtc_info_map.end(); ++iter) {
XRRFreeCrtcInfo(iter->second);
}
XRRFreeScreenResources(screen_resources);
// DisplayManager can be null during the boot.
Shell::GetInstance()->display_manager()->OnNativeDisplaysChanged(displays);
}
void DisplayChangeObserverX11::OnAppTerminating() {
#if defined(USE_ASH)
// Stop handling display configuration events once the shutdown
// process starts. crbug.com/177014.
Shell::GetInstance()->output_configurator()->Stop();
#endif
}
} // namespace internal
} // namespace ash