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/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 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" 29 30 namespace { 31 32 // The delay to perform configuration after RRNotify. See the comment 33 // in |Dispatch()|. 34 const int64 kConfigureDelayMs = 500; 35 36 // TODO(oshima): Consider using gtk-xft-dpi instead. 37 float 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::GetScaleForScaleFactor(ui::GetSupportedScaleFactor(scale)); 46 } 47 48 std::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 70 namespace views { 71 72 //////////////////////////////////////////////////////////////////////////////// 73 // DesktopScreenX11, public: 74 75 DesktopScreenX11::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 107 DesktopScreenX11::~DesktopScreenX11() { 108 if (has_xrandr_ && ui::PlatformEventSource::GetInstance()) 109 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); 110 } 111 112 //////////////////////////////////////////////////////////////////////////////// 113 // DesktopScreenX11, gfx::Screen implementation: 114 115 bool DesktopScreenX11::IsDIPEnabled() { 116 return true; 117 } 118 119 gfx::Point DesktopScreenX11::GetCursorScreenPoint() { 120 TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()"); 121 122 XDisplay* display = gfx::GetXDisplay(); 123 124 ::Window root, child; 125 int root_x, root_y, win_x, win_y; 126 unsigned int mask; 127 XQueryPointer(display, 128 DefaultRootWindow(display), 129 &root, 130 &child, 131 &root_x, 132 &root_y, 133 &win_x, 134 &win_y, 135 &mask); 136 137 return gfx::Point(root_x, root_y); 138 } 139 140 gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() { 141 return GetWindowAtScreenPoint(GetCursorScreenPoint()); 142 } 143 144 gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint( 145 const gfx::Point& point) { 146 X11TopmostWindowFinder finder; 147 return finder.FindLocalProcessWindowAt(point, std::set<aura::Window*>()); 148 } 149 150 int DesktopScreenX11::GetNumDisplays() const { 151 return displays_.size(); 152 } 153 154 std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const { 155 return displays_; 156 } 157 158 gfx::Display DesktopScreenX11::GetDisplayNearestWindow( 159 gfx::NativeView window) const { 160 // Getting screen bounds here safely is hard. 161 // 162 // You'd think we'd be able to just call window->GetBoundsInScreen(), but we 163 // can't because |window| (and the associated WindowEventDispatcher*) can be 164 // partially initialized at this point; WindowEventDispatcher initializations 165 // call through into GetDisplayNearestWindow(). But the X11 resources are 166 // created before we create the aura::WindowEventDispatcher. So we ask what 167 // the DRWHX11 believes the window bounds are instead of going through the 168 // aura::Window's screen bounds. 169 aura::WindowTreeHost* host = window->GetHost(); 170 if (host) { 171 DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID( 172 host->GetAcceleratedWidget()); 173 if (rwh) 174 return GetDisplayMatching(rwh->GetX11RootWindowBounds()); 175 } 176 177 return GetPrimaryDisplay(); 178 } 179 180 gfx::Display DesktopScreenX11::GetDisplayNearestPoint( 181 const gfx::Point& point) const { 182 for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); 183 it != displays_.end(); ++it) { 184 if (it->bounds().Contains(point)) 185 return *it; 186 } 187 188 return GetPrimaryDisplay(); 189 } 190 191 gfx::Display DesktopScreenX11::GetDisplayMatching( 192 const gfx::Rect& match_rect) const { 193 int max_area = 0; 194 const gfx::Display* matching = NULL; 195 for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); 196 it != displays_.end(); ++it) { 197 gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect); 198 int area = intersect.width() * intersect.height(); 199 if (area > max_area) { 200 max_area = area; 201 matching = &*it; 202 } 203 } 204 // Fallback to the primary display if there is no matching display. 205 return matching ? *matching : GetPrimaryDisplay(); 206 } 207 208 gfx::Display DesktopScreenX11::GetPrimaryDisplay() const { 209 return displays_.front(); 210 } 211 212 void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) { 213 change_notifier_.AddObserver(observer); 214 } 215 216 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) { 217 change_notifier_.RemoveObserver(observer); 218 } 219 220 bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent& event) { 221 return event->type - xrandr_event_base_ == RRScreenChangeNotify || 222 event->type - xrandr_event_base_ == RRNotify; 223 } 224 225 uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) { 226 if (event->type - xrandr_event_base_ == RRScreenChangeNotify) { 227 // Pass the event through to xlib. 228 XRRUpdateConfiguration(event); 229 } else if (event->type - xrandr_event_base_ == RRNotify) { 230 // There's some sort of observer dispatch going on here, but I don't think 231 // it's the screen's? 232 if (configure_timer_.get() && configure_timer_->IsRunning()) { 233 configure_timer_->Reset(); 234 } else { 235 configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>()); 236 configure_timer_->Start( 237 FROM_HERE, 238 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), 239 this, 240 &DesktopScreenX11::ConfigureTimerFired); 241 } 242 } else { 243 NOTREACHED(); 244 } 245 246 return ui::POST_DISPATCH_NONE; 247 } 248 249 //////////////////////////////////////////////////////////////////////////////// 250 // DesktopScreenX11, private: 251 252 DesktopScreenX11::DesktopScreenX11( 253 const std::vector<gfx::Display>& test_displays) 254 : xdisplay_(gfx::GetXDisplay()), 255 x_root_window_(DefaultRootWindow(xdisplay_)), 256 has_xrandr_(false), 257 xrandr_event_base_(0), 258 displays_(test_displays) { 259 } 260 261 std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { 262 std::vector<gfx::Display> displays; 263 XRRScreenResources* resources = 264 XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_); 265 if (!resources) { 266 LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window."; 267 return GetFallbackDisplayList(); 268 } 269 270 bool has_work_area = false; 271 gfx::Rect work_area; 272 std::vector<int> value; 273 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) && 274 value.size() >= 4) { 275 work_area = gfx::Rect(value[0], value[1], value[2], value[3]); 276 has_work_area = true; 277 } 278 279 float device_scale_factor = 1.0f; 280 for (int i = 0; i < resources->noutput; ++i) { 281 RROutput output_id = resources->outputs[i]; 282 XRROutputInfo* output_info = 283 XRRGetOutputInfo(xdisplay_, resources, output_id); 284 285 bool is_connected = (output_info->connection == RR_Connected); 286 if (!is_connected) { 287 XRRFreeOutputInfo(output_info); 288 continue; 289 } 290 291 if (output_info->crtc) { 292 XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_, 293 resources, 294 output_info->crtc); 295 296 int64 display_id = -1; 297 if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) { 298 // It isn't ideal, but if we can't parse the EDID data, fallback on the 299 // display number. 300 display_id = i; 301 } 302 303 gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height); 304 gfx::Display display(display_id, crtc_bounds); 305 306 if (!gfx::Display::HasForceDeviceScaleFactor()) { 307 if (i == 0 && !ui::IsDisplaySizeBlackListed( 308 gfx::Size(output_info->mm_width, output_info->mm_height))) { 309 // As per display scale factor is not supported right now, 310 // the primary display's scale factor is always used. 311 device_scale_factor = GetDeviceScaleFactor(crtc->width, 312 output_info->mm_width); 313 DCHECK_LE(1.0f, device_scale_factor); 314 } 315 display.SetScaleAndBounds(device_scale_factor, crtc_bounds); 316 } 317 318 if (has_work_area) { 319 gfx::Rect intersection = crtc_bounds; 320 intersection.Intersect(work_area); 321 display.set_work_area(intersection); 322 } 323 324 switch (crtc->rotation) { 325 case RR_Rotate_0: 326 display.set_rotation(gfx::Display::ROTATE_0); 327 break; 328 case RR_Rotate_90: 329 display.set_rotation(gfx::Display::ROTATE_90); 330 break; 331 case RR_Rotate_180: 332 display.set_rotation(gfx::Display::ROTATE_180); 333 break; 334 case RR_Rotate_270: 335 display.set_rotation(gfx::Display::ROTATE_270); 336 break; 337 } 338 339 displays.push_back(display); 340 341 XRRFreeCrtcInfo(crtc); 342 } 343 344 XRRFreeOutputInfo(output_info); 345 } 346 347 XRRFreeScreenResources(resources); 348 349 if (displays.empty()) 350 return GetFallbackDisplayList(); 351 352 return displays; 353 } 354 355 void DesktopScreenX11::ConfigureTimerFired() { 356 std::vector<gfx::Display> old_displays = displays_; 357 displays_ = BuildDisplaysFromXRandRInfo(); 358 359 change_notifier_.NotifyDisplaysChanged(old_displays, displays_); 360 } 361 362 //////////////////////////////////////////////////////////////////////////////// 363 364 gfx::Screen* CreateDesktopScreen() { 365 return new DesktopScreenX11; 366 } 367 368 } // namespace views 369