desktop_screen_x11.cc revision 010d83a9304c5a91596085d917d248abff47903a
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 if (new_it->bounds() != old_it->bounds()) { 142 FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, 143 OnDisplayBoundsChanged(*new_it)); 144 } 145 146 found = true; 147 break; 148 } 149 } 150 151 if (!found) { 152 FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, 153 OnDisplayAdded(*new_it)); 154 } 155 } 156} 157 158//////////////////////////////////////////////////////////////////////////////// 159// DesktopScreenX11, gfx::Screen implementation: 160 161bool DesktopScreenX11::IsDIPEnabled() { 162 return true; 163} 164 165gfx::Point DesktopScreenX11::GetCursorScreenPoint() { 166 TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()"); 167 168 XDisplay* display = gfx::GetXDisplay(); 169 170 ::Window root, child; 171 int root_x, root_y, win_x, win_y; 172 unsigned int mask; 173 XQueryPointer(display, 174 DefaultRootWindow(display), 175 &root, 176 &child, 177 &root_x, 178 &root_y, 179 &win_x, 180 &win_y, 181 &mask); 182 183 return gfx::Point(root_x, root_y); 184} 185 186gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() { 187 return GetWindowAtScreenPoint(GetCursorScreenPoint()); 188} 189 190gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint( 191 const gfx::Point& point) { 192 X11TopmostWindowFinder finder; 193 return finder.FindLocalProcessWindowAt(point, std::set<aura::Window*>()); 194} 195 196int DesktopScreenX11::GetNumDisplays() const { 197 return displays_.size(); 198} 199 200std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const { 201 return displays_; 202} 203 204gfx::Display DesktopScreenX11::GetDisplayNearestWindow( 205 gfx::NativeView window) const { 206 // Getting screen bounds here safely is hard. 207 // 208 // You'd think we'd be able to just call window->GetBoundsInScreen(), but we 209 // can't because |window| (and the associated WindowEventDispatcher*) can be 210 // partially initialized at this point; WindowEventDispatcher initializations 211 // call through into GetDisplayNearestWindow(). But the X11 resources are 212 // created before we create the aura::WindowEventDispatcher. So we ask what 213 // the DRWHX11 believes the window bounds are instead of going through the 214 // aura::Window's screen bounds. 215 aura::WindowTreeHost* host = window->GetHost(); 216 if (host) { 217 DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID( 218 host->GetAcceleratedWidget()); 219 if (rwh) 220 return GetDisplayMatching(rwh->GetX11RootWindowBounds()); 221 } 222 223 return GetPrimaryDisplay(); 224} 225 226gfx::Display DesktopScreenX11::GetDisplayNearestPoint( 227 const gfx::Point& point) const { 228 for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); 229 it != displays_.end(); ++it) { 230 if (it->bounds().Contains(point)) 231 return *it; 232 } 233 234 return GetPrimaryDisplay(); 235} 236 237gfx::Display DesktopScreenX11::GetDisplayMatching( 238 const gfx::Rect& match_rect) const { 239 int max_area = 0; 240 const gfx::Display* matching = NULL; 241 for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); 242 it != displays_.end(); ++it) { 243 gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect); 244 int area = intersect.width() * intersect.height(); 245 if (area > max_area) { 246 max_area = area; 247 matching = &*it; 248 } 249 } 250 // Fallback to the primary display if there is no matching display. 251 return matching ? *matching : GetPrimaryDisplay(); 252} 253 254gfx::Display DesktopScreenX11::GetPrimaryDisplay() const { 255 return displays_.front(); 256} 257 258void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) { 259 observer_list_.AddObserver(observer); 260} 261 262void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) { 263 observer_list_.RemoveObserver(observer); 264} 265 266bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent& event) { 267 return event->type - xrandr_event_base_ == RRScreenChangeNotify || 268 event->type - xrandr_event_base_ == RRNotify; 269} 270 271uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) { 272 if (event->type - xrandr_event_base_ == RRScreenChangeNotify) { 273 // Pass the event through to xlib. 274 XRRUpdateConfiguration(event); 275 } else if (event->type - xrandr_event_base_ == RRNotify) { 276 // There's some sort of observer dispatch going on here, but I don't think 277 // it's the screen's? 278 if (configure_timer_.get() && configure_timer_->IsRunning()) { 279 configure_timer_->Reset(); 280 } else { 281 configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>()); 282 configure_timer_->Start( 283 FROM_HERE, 284 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), 285 this, 286 &DesktopScreenX11::ConfigureTimerFired); 287 } 288 } else { 289 NOTREACHED(); 290 } 291 292 return ui::POST_DISPATCH_NONE; 293} 294 295//////////////////////////////////////////////////////////////////////////////// 296// DesktopScreenX11, private: 297 298DesktopScreenX11::DesktopScreenX11( 299 const std::vector<gfx::Display>& test_displays) 300 : xdisplay_(gfx::GetXDisplay()), 301 x_root_window_(DefaultRootWindow(xdisplay_)), 302 has_xrandr_(false), 303 xrandr_event_base_(0), 304 displays_(test_displays) { 305} 306 307std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { 308 std::vector<gfx::Display> displays; 309 XRRScreenResources* resources = 310 XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_); 311 if (!resources) { 312 LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window."; 313 return GetFallbackDisplayList(); 314 } 315 316 bool has_work_area = false; 317 gfx::Rect work_area; 318 std::vector<int> value; 319 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) && 320 value.size() >= 4) { 321 work_area = gfx::Rect(value[0], value[1], value[2], value[3]); 322 has_work_area = true; 323 } 324 325 float device_scale_factor = 1.0f; 326 for (int i = 0; i < resources->noutput; ++i) { 327 RROutput output_id = resources->outputs[i]; 328 XRROutputInfo* output_info = 329 XRRGetOutputInfo(xdisplay_, resources, output_id); 330 331 bool is_connected = (output_info->connection == RR_Connected); 332 if (!is_connected) { 333 XRRFreeOutputInfo(output_info); 334 continue; 335 } 336 337 if (output_info->crtc) { 338 XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_, 339 resources, 340 output_info->crtc); 341 342 int64 display_id = -1; 343 if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) { 344 // It isn't ideal, but if we can't parse the EDID data, fallback on the 345 // display number. 346 display_id = i; 347 } 348 349 gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height); 350 gfx::Display display(display_id, crtc_bounds); 351 352 if (!gfx::Display::HasForceDeviceScaleFactor()) { 353 if (i == 0 && !ui::IsDisplaySizeBlackListed( 354 gfx::Size(output_info->mm_width, output_info->mm_height))) { 355 // As per display scale factor is not supported right now, 356 // the primary display's scale factor is always used. 357 device_scale_factor = GetDeviceScaleFactor(crtc->width, 358 output_info->mm_width); 359 DCHECK_LE(1.0f, device_scale_factor); 360 } 361 display.SetScaleAndBounds(device_scale_factor, crtc_bounds); 362 } 363 364 if (has_work_area) { 365 gfx::Rect intersection = crtc_bounds; 366 intersection.Intersect(work_area); 367 display.set_work_area(intersection); 368 } 369 370 displays.push_back(display); 371 372 XRRFreeCrtcInfo(crtc); 373 } 374 375 XRRFreeOutputInfo(output_info); 376 } 377 378 XRRFreeScreenResources(resources); 379 380 if (displays.empty()) 381 return GetFallbackDisplayList(); 382 383 return displays; 384} 385 386void DesktopScreenX11::ConfigureTimerFired() { 387 std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo(); 388 ProcessDisplayChange(new_displays); 389} 390 391//////////////////////////////////////////////////////////////////////////////// 392 393gfx::Screen* CreateDesktopScreen() { 394 return new DesktopScreenX11; 395} 396 397} // namespace views 398