desktop_screen_x11.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 "ui/views/widget/desktop_aura/desktop_screen_x11.h" 6 7#include <X11/extensions/Xrandr.h> 8#include <X11/Xlib.h> 9 10// It clashes with out RootWindow. 11#undef RootWindow 12 13#include "base/debug/trace_event.h" 14#include "base/logging.h" 15#include "ui/aura/window.h" 16#include "ui/aura/window_event_dispatcher.h" 17#include "ui/aura/window_tree_host.h" 18#include "ui/base/layout.h" 19#include "ui/display/util/display_util.h" 20#include "ui/display/util/x11/edid_parser_x11.h" 21#include "ui/events/platform/platform_event_source.h" 22#include "ui/gfx/display.h" 23#include "ui/gfx/display_observer.h" 24#include "ui/gfx/native_widget_types.h" 25#include "ui/gfx/screen.h" 26#include "ui/gfx/x/x11_types.h" 27#include "ui/views/widget/desktop_aura/desktop_screen.h" 28#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" 29#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" 30 31namespace { 32 33// The delay to perform configuration after RRNotify. See the comment 34// in |Dispatch()|. 35const int64 kConfigureDelayMs = 500; 36 37float GetDeviceScaleFactor(int screen_pixels, int screen_mm) { 38 const int kCSSDefaultDPI = 96; 39 const float kInchInMm = 25.4f; 40 41 float screen_inches = screen_mm / kInchInMm; 42 float screen_dpi = screen_pixels / screen_inches; 43 float scale = screen_dpi / kCSSDefaultDPI; 44 45 return ui::GetImageScale(ui::GetSupportedScaleFactor(scale)); 46} 47 48std::vector<gfx::Display> GetFallbackDisplayList() { 49 ::XDisplay* display = gfx::GetXDisplay(); 50 ::Screen* screen = DefaultScreenOfDisplay(display); 51 int width = WidthOfScreen(screen); 52 int height = HeightOfScreen(screen); 53 gfx::Size physical_size(WidthMMOfScreen(screen), HeightMMOfScreen(screen)); 54 55 gfx::Rect bounds_in_pixels(0, 0, width, height); 56 gfx::Display gfx_display(0, bounds_in_pixels); 57 if (!gfx::Display::HasForceDeviceScaleFactor() && 58 !ui::IsDisplaySizeBlackListed(physical_size)) { 59 float device_scale_factor = GetDeviceScaleFactor( 60 width, physical_size.width()); 61 DCHECK_LE(1.0f, device_scale_factor); 62 gfx_display.SetScaleAndBounds(device_scale_factor, bounds_in_pixels); 63 } 64 65 return std::vector<gfx::Display>(1, gfx_display); 66} 67 68} // namespace 69 70namespace views { 71 72//////////////////////////////////////////////////////////////////////////////// 73// DesktopScreenX11, public: 74 75DesktopScreenX11::DesktopScreenX11() 76 : xdisplay_(gfx::GetXDisplay()), 77 x_root_window_(DefaultRootWindow(xdisplay_)), 78 has_xrandr_(false), 79 xrandr_event_base_(0) { 80 // We only support 1.3+. There were library changes before this and we should 81 // use the new interface instead of the 1.2 one. 82 int randr_version_major = 0; 83 int randr_version_minor = 0; 84 has_xrandr_ = XRRQueryVersion( 85 xdisplay_, &randr_version_major, &randr_version_minor) && 86 randr_version_major == 1 && 87 randr_version_minor >= 3; 88 89 if (has_xrandr_) { 90 int error_base_ignored = 0; 91 XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored); 92 93 if (ui::PlatformEventSource::GetInstance()) 94 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); 95 XRRSelectInput(xdisplay_, 96 x_root_window_, 97 RRScreenChangeNotifyMask | 98 RROutputChangeNotifyMask | 99 RRCrtcChangeNotifyMask); 100 101 displays_ = BuildDisplaysFromXRandRInfo(); 102 } else { 103 displays_ = GetFallbackDisplayList(); 104 } 105} 106 107DesktopScreenX11::~DesktopScreenX11() { 108 if (has_xrandr_ && ui::PlatformEventSource::GetInstance()) 109 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); 110} 111 112void DesktopScreenX11::ProcessDisplayChange( 113 const std::vector<gfx::Display>& incoming) { 114 std::vector<gfx::Display> old_displays = displays_; 115 displays_ = incoming; 116 117 typedef std::vector<gfx::Display>::const_iterator DisplayIt; 118 std::vector<gfx::Display>::const_iterator old_it = old_displays.begin(); 119 for (; old_it != old_displays.end(); ++old_it) { 120 bool found = false; 121 for (std::vector<gfx::Display>::const_iterator new_it = 122 displays_.begin(); new_it != displays_.end(); ++new_it) { 123 if (old_it->id() == new_it->id()) { 124 found = true; 125 break; 126 } 127 } 128 129 if (!found) { 130 FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, 131 OnDisplayRemoved(*old_it)); 132 } 133 } 134 135 std::vector<gfx::Display>::const_iterator new_it = displays_.begin(); 136 for (; new_it != displays_.end(); ++new_it) { 137 bool found = false; 138 for (std::vector<gfx::Display>::const_iterator old_it = 139 old_displays.begin(); old_it != old_displays.end(); ++old_it) { 140 if (new_it->id() != old_it->id()) 141 continue; 142 143 uint32_t metrics = gfx::DisplayObserver::DISPLAY_METRIC_NONE; 144 145 if (new_it->bounds() != old_it->bounds()) 146 metrics |= gfx::DisplayObserver::DISPLAY_METRIC_BOUNDS; 147 148 if (new_it->rotation() != old_it->rotation()) 149 metrics |= gfx::DisplayObserver::DISPLAY_METRIC_ROTATION; 150 151 if (new_it->work_area() != old_it->work_area()) 152 metrics |= gfx::DisplayObserver::DISPLAY_METRIC_WORK_AREA; 153 154 if (new_it->device_scale_factor() != old_it->device_scale_factor()) 155 metrics |= gfx::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; 156 157 if (metrics != gfx::DisplayObserver::DISPLAY_METRIC_NONE) { 158 FOR_EACH_OBSERVER(gfx::DisplayObserver, 159 observer_list_, 160 OnDisplayMetricsChanged(*new_it, metrics)); 161 } 162 163 found = true; 164 break; 165 } 166 167 if (!found) { 168 FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, 169 OnDisplayAdded(*new_it)); 170 } 171 } 172} 173 174//////////////////////////////////////////////////////////////////////////////// 175// DesktopScreenX11, gfx::Screen implementation: 176 177bool DesktopScreenX11::IsDIPEnabled() { 178 return true; 179} 180 181gfx::Point DesktopScreenX11::GetCursorScreenPoint() { 182 TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()"); 183 184 XDisplay* display = gfx::GetXDisplay(); 185 186 ::Window root, child; 187 int root_x, root_y, win_x, win_y; 188 unsigned int mask; 189 XQueryPointer(display, 190 DefaultRootWindow(display), 191 &root, 192 &child, 193 &root_x, 194 &root_y, 195 &win_x, 196 &win_y, 197 &mask); 198 199 return gfx::Point(root_x, root_y); 200} 201 202gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() { 203 return GetWindowAtScreenPoint(GetCursorScreenPoint()); 204} 205 206gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint( 207 const gfx::Point& point) { 208 X11TopmostWindowFinder finder; 209 return finder.FindLocalProcessWindowAt(point, std::set<aura::Window*>()); 210} 211 212int DesktopScreenX11::GetNumDisplays() const { 213 return displays_.size(); 214} 215 216std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const { 217 return displays_; 218} 219 220gfx::Display DesktopScreenX11::GetDisplayNearestWindow( 221 gfx::NativeView window) const { 222 // Getting screen bounds here safely is hard. 223 // 224 // You'd think we'd be able to just call window->GetBoundsInScreen(), but we 225 // can't because |window| (and the associated WindowEventDispatcher*) can be 226 // partially initialized at this point; WindowEventDispatcher initializations 227 // call through into GetDisplayNearestWindow(). But the X11 resources are 228 // created before we create the aura::WindowEventDispatcher. So we ask what 229 // the DRWHX11 believes the window bounds are instead of going through the 230 // aura::Window's screen bounds. 231 aura::WindowTreeHost* host = window->GetHost(); 232 if (host) { 233 DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID( 234 host->GetAcceleratedWidget()); 235 if (rwh) 236 return GetDisplayMatching(rwh->GetX11RootWindowBounds()); 237 } 238 239 return GetPrimaryDisplay(); 240} 241 242gfx::Display DesktopScreenX11::GetDisplayNearestPoint( 243 const gfx::Point& point) const { 244 for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); 245 it != displays_.end(); ++it) { 246 if (it->bounds().Contains(point)) 247 return *it; 248 } 249 250 return GetPrimaryDisplay(); 251} 252 253gfx::Display DesktopScreenX11::GetDisplayMatching( 254 const gfx::Rect& match_rect) const { 255 int max_area = 0; 256 const gfx::Display* matching = NULL; 257 for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); 258 it != displays_.end(); ++it) { 259 gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect); 260 int area = intersect.width() * intersect.height(); 261 if (area > max_area) { 262 max_area = area; 263 matching = &*it; 264 } 265 } 266 // Fallback to the primary display if there is no matching display. 267 return matching ? *matching : GetPrimaryDisplay(); 268} 269 270gfx::Display DesktopScreenX11::GetPrimaryDisplay() const { 271 return displays_.front(); 272} 273 274void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) { 275 observer_list_.AddObserver(observer); 276} 277 278void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) { 279 observer_list_.RemoveObserver(observer); 280} 281 282bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent& event) { 283 return event->type - xrandr_event_base_ == RRScreenChangeNotify || 284 event->type - xrandr_event_base_ == RRNotify; 285} 286 287uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) { 288 if (event->type - xrandr_event_base_ == RRScreenChangeNotify) { 289 // Pass the event through to xlib. 290 XRRUpdateConfiguration(event); 291 } else if (event->type - xrandr_event_base_ == RRNotify) { 292 // There's some sort of observer dispatch going on here, but I don't think 293 // it's the screen's? 294 if (configure_timer_.get() && configure_timer_->IsRunning()) { 295 configure_timer_->Reset(); 296 } else { 297 configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>()); 298 configure_timer_->Start( 299 FROM_HERE, 300 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), 301 this, 302 &DesktopScreenX11::ConfigureTimerFired); 303 } 304 } else { 305 NOTREACHED(); 306 } 307 308 return ui::POST_DISPATCH_NONE; 309} 310 311//////////////////////////////////////////////////////////////////////////////// 312// DesktopScreenX11, private: 313 314DesktopScreenX11::DesktopScreenX11( 315 const std::vector<gfx::Display>& test_displays) 316 : xdisplay_(gfx::GetXDisplay()), 317 x_root_window_(DefaultRootWindow(xdisplay_)), 318 has_xrandr_(false), 319 xrandr_event_base_(0), 320 displays_(test_displays) { 321} 322 323std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { 324 std::vector<gfx::Display> displays; 325 XRRScreenResources* resources = 326 XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_); 327 if (!resources) { 328 LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window."; 329 return GetFallbackDisplayList(); 330 } 331 332 bool has_work_area = false; 333 gfx::Rect work_area; 334 std::vector<int> value; 335 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) && 336 value.size() >= 4) { 337 work_area = gfx::Rect(value[0], value[1], value[2], value[3]); 338 has_work_area = true; 339 } 340 341 float device_scale_factor = 1.0f; 342 for (int i = 0; i < resources->noutput; ++i) { 343 RROutput output_id = resources->outputs[i]; 344 XRROutputInfo* output_info = 345 XRRGetOutputInfo(xdisplay_, resources, output_id); 346 347 bool is_connected = (output_info->connection == RR_Connected); 348 if (!is_connected) { 349 XRRFreeOutputInfo(output_info); 350 continue; 351 } 352 353 if (output_info->crtc) { 354 XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_, 355 resources, 356 output_info->crtc); 357 358 int64 display_id = -1; 359 if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) { 360 // It isn't ideal, but if we can't parse the EDID data, fallback on the 361 // display number. 362 display_id = i; 363 } 364 365 gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height); 366 gfx::Display display(display_id, crtc_bounds); 367 368 if (!gfx::Display::HasForceDeviceScaleFactor()) { 369 if (i == 0 && !ui::IsDisplaySizeBlackListed( 370 gfx::Size(output_info->mm_width, output_info->mm_height))) { 371 // As per display scale factor is not supported right now, 372 // the primary display's scale factor is always used. 373 device_scale_factor = GetDeviceScaleFactor(crtc->width, 374 output_info->mm_width); 375 DCHECK_LE(1.0f, device_scale_factor); 376 } 377 display.SetScaleAndBounds(device_scale_factor, crtc_bounds); 378 } 379 380 if (has_work_area) { 381 gfx::Rect intersection = crtc_bounds; 382 intersection.Intersect(work_area); 383 display.set_work_area(intersection); 384 } 385 386 switch (crtc->rotation) { 387 case RR_Rotate_0: 388 display.set_rotation(gfx::Display::ROTATE_0); 389 break; 390 case RR_Rotate_90: 391 display.set_rotation(gfx::Display::ROTATE_90); 392 break; 393 case RR_Rotate_180: 394 display.set_rotation(gfx::Display::ROTATE_180); 395 break; 396 case RR_Rotate_270: 397 display.set_rotation(gfx::Display::ROTATE_270); 398 break; 399 } 400 401 displays.push_back(display); 402 403 XRRFreeCrtcInfo(crtc); 404 } 405 406 XRRFreeOutputInfo(output_info); 407 } 408 409 XRRFreeScreenResources(resources); 410 411 if (displays.empty()) 412 return GetFallbackDisplayList(); 413 414 return displays; 415} 416 417void DesktopScreenX11::ConfigureTimerFired() { 418 std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo(); 419 ProcessDisplayChange(new_displays); 420} 421 422//////////////////////////////////////////////////////////////////////////////// 423 424gfx::Screen* CreateDesktopScreen() { 425 return new DesktopScreenX11; 426} 427 428} // namespace views 429