Home | History | Annotate | Download | only in display
      1 // Copyright (c) 2012 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 "ash/display/display_change_observer_x11.h"
      6 
      7 #include <algorithm>
      8 #include <map>
      9 #include <set>
     10 #include <vector>
     11 
     12 #include <X11/extensions/Xrandr.h>
     13 
     14 #include "ash/ash_switches.h"
     15 #include "ash/display/display_info.h"
     16 #include "ash/display/display_layout_store.h"
     17 #include "ash/display/display_manager.h"
     18 #include "ash/display/display_util_x11.h"
     19 #include "ash/shell.h"
     20 #include "base/command_line.h"
     21 #include "base/message_loop/message_pump_aurax11.h"
     22 #include "chromeos/display/output_util.h"
     23 #include "grit/ash_strings.h"
     24 #include "ui/base/l10n/l10n_util.h"
     25 #include "ui/compositor/dip_util.h"
     26 #include "ui/gfx/display.h"
     27 
     28 namespace ash {
     29 namespace internal {
     30 namespace {
     31 
     32 // The DPI threshold to detect high density screen.
     33 // Higher DPI than this will use device_scale_factor=2.
     34 const unsigned int kHighDensityDPIThreshold = 160;
     35 
     36 // 1 inch in mm.
     37 const float kInchInMm = 25.4f;
     38 
     39 int64 GetDisplayId(XID output, size_t output_index) {
     40   int64 display_id;
     41   if (chromeos::GetDisplayId(output, output_index, &display_id))
     42     return display_id;
     43   return gfx::Display::kInvalidDisplayID;
     44 }
     45 
     46 }  // namespace
     47 
     48 DisplayChangeObserverX11::DisplayChangeObserverX11()
     49     : xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
     50       x_root_window_(DefaultRootWindow(xdisplay_)),
     51       xrandr_event_base_(0) {
     52   int error_base_ignored;
     53   XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
     54 
     55   Shell::GetInstance()->AddShellObserver(this);
     56 }
     57 
     58 DisplayChangeObserverX11::~DisplayChangeObserverX11() {
     59   Shell::GetInstance()->RemoveShellObserver(this);
     60 }
     61 
     62 chromeos::OutputState DisplayChangeObserverX11::GetStateForDisplayIds(
     63     const std::vector<int64>& display_ids) const {
     64   if (CommandLine::ForCurrentProcess()->HasSwitch(
     65           switches::kAshForceMirrorMode)) {
     66     return chromeos::STATE_DUAL_MIRROR;
     67   }
     68 
     69   CHECK_EQ(2U, display_ids.size());
     70   DisplayIdPair pair = std::make_pair(display_ids[0], display_ids[1]);
     71   DisplayLayout layout = Shell::GetInstance()->display_manager()->
     72       layout_store()->GetRegisteredDisplayLayout(pair);
     73   return layout.mirrored ?
     74       chromeos::STATE_DUAL_MIRROR : chromeos::STATE_DUAL_EXTENDED;
     75 }
     76 
     77 bool DisplayChangeObserverX11::GetResolutionForDisplayId(int64 display_id,
     78                                                          int* width,
     79                                                          int* height) const {
     80 
     81   gfx::Size resolution;
     82   if (!Shell::GetInstance()->display_manager()->
     83       GetSelectedResolutionForDisplayId(display_id, &resolution)) {
     84     return false;
     85   }
     86 
     87   *width = resolution.width();
     88   *height = resolution.height();
     89   return true;
     90 }
     91 
     92 void DisplayChangeObserverX11::OnDisplayModeChanged() {
     93   XRRScreenResources* screen_resources =
     94       XRRGetScreenResources(xdisplay_, x_root_window_);
     95   std::map<XID, XRRCrtcInfo*> crtc_info_map;
     96 
     97   for (int c = 0; c < screen_resources->ncrtc; c++) {
     98     XID crtc_id = screen_resources->crtcs[c];
     99     XRRCrtcInfo *crtc_info =
    100         XRRGetCrtcInfo(xdisplay_, screen_resources, crtc_id);
    101     crtc_info_map[crtc_id] = crtc_info;
    102   }
    103 
    104   std::vector<DisplayInfo> displays;
    105   std::set<int64> ids;
    106   for (int output_index = 0; output_index < screen_resources->noutput;
    107        output_index++) {
    108     XID output = screen_resources->outputs[output_index];
    109     XRROutputInfo *output_info =
    110         XRRGetOutputInfo(xdisplay_, screen_resources, output);
    111 
    112     const bool is_internal = chromeos::IsInternalOutputName(
    113         std::string(output_info->name));
    114 
    115     if (is_internal &&
    116         gfx::Display::InternalDisplayId() == gfx::Display::kInvalidDisplayID) {
    117       int64 id = GetDisplayId(output, output_index);
    118       // Fallback to output index. crbug.com/180100
    119       gfx::Display::SetInternalDisplayId(
    120           id == gfx::Display::kInvalidDisplayID ? output_index : id);
    121     }
    122 
    123     if (output_info->connection != RR_Connected) {
    124       XRRFreeOutputInfo(output_info);
    125       continue;
    126     }
    127     const XRRCrtcInfo* crtc_info = crtc_info_map[output_info->crtc];
    128     if (!crtc_info) {
    129       LOG(WARNING) << "Crtc not found for output: output_index="
    130                    << output_index;
    131       continue;
    132     }
    133     const XRRModeInfo* mode =
    134         chromeos::FindModeInfo(screen_resources, crtc_info->mode);
    135     if (!mode) {
    136       LOG(WARNING) << "Could not find a mode for the output: output_index="
    137                    << output_index;
    138       continue;
    139     }
    140 
    141     float device_scale_factor = 1.0f;
    142     if (!ShouldIgnoreSize(output_info->mm_width, output_info->mm_height) &&
    143         (kInchInMm * mode->width / output_info->mm_width) >
    144         kHighDensityDPIThreshold) {
    145       device_scale_factor = 2.0f;
    146     }
    147     gfx::Rect display_bounds(
    148         crtc_info->x, crtc_info->y, mode->width, mode->height);
    149 
    150     std::vector<Resolution> resolutions;
    151     if (!is_internal)
    152       resolutions = GetResolutionList(screen_resources, output_info);
    153 
    154     XRRFreeOutputInfo(output_info);
    155 
    156     std::string name = is_internal ?
    157         l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME) :
    158         chromeos::GetDisplayName(output);
    159     if (name.empty())
    160       name = l10n_util::GetStringUTF8(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
    161 
    162     bool has_overscan = false;
    163     chromeos::GetOutputOverscanFlag(output, &has_overscan);
    164 
    165     int64 id = GetDisplayId(output, output_index);
    166 
    167     // If ID is invalid or there is an duplicate, just use output index.
    168     if (id == gfx::Display::kInvalidDisplayID || ids.find(id) != ids.end())
    169       id = output_index;
    170     ids.insert(id);
    171 
    172     displays.push_back(DisplayInfo(id, name, has_overscan));
    173     displays.back().set_device_scale_factor(device_scale_factor);
    174     displays.back().SetBounds(display_bounds);
    175     displays.back().set_native(true);
    176     displays.back().set_resolutions(resolutions);
    177   }
    178 
    179   // Free all allocated resources.
    180   for (std::map<XID, XRRCrtcInfo*>::const_iterator iter = crtc_info_map.begin();
    181        iter != crtc_info_map.end(); ++iter) {
    182     XRRFreeCrtcInfo(iter->second);
    183   }
    184   XRRFreeScreenResources(screen_resources);
    185 
    186   // DisplayManager can be null during the boot.
    187   Shell::GetInstance()->display_manager()->OnNativeDisplaysChanged(displays);
    188 }
    189 
    190 void DisplayChangeObserverX11::OnAppTerminating() {
    191 #if defined(USE_ASH)
    192   // Stop handling display configuration events once the shutdown
    193   // process starts. crbug.com/177014.
    194   Shell::GetInstance()->output_configurator()->Stop();
    195 #endif
    196 }
    197 
    198 }  // namespace internal
    199 }  // namespace ash
    200