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