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