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