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