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