Chromium Code Reviews| Index: ui/views/widget/desktop_aura/desktop_screen_x11.cc |
| diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc |
| index e581b834f8151c2a280d3642aa6566c889c9ff4b..e16377000e6004a8f351967cffc0be3423209be4 100644 |
| --- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc |
| +++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc |
| @@ -2,68 +2,121 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| -#include "ui/views/widget/desktop_aura/desktop_screen.h" |
| +#include "ui/views/widget/desktop_aura/desktop_screen_x11.h" |
| +#include <X11/extensions/Xrandr.h> |
| #include <X11/Xlib.h> |
| // It clashes with out RootWindow. |
| #undef RootWindow |
| +#include "base/edid_parser_x11.h" |
| #include "base/logging.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/aura/root_window_host.h" |
| #include "ui/base/x/x11_util.h" |
| #include "ui/gfx/display.h" |
| +#include "ui/gfx/display_observer.h" |
| #include "ui/gfx/native_widget_types.h" |
| -#include "ui/gfx/screen.h" |
| +#include "ui/views/widget/desktop_aura/desktop_screen.h" |
| namespace { |
| -// TODO(erg): This method is a temporary hack, until we can reliably extract |
| -// location data out of XRandR. |
| -gfx::Size GetPrimaryDisplaySize() { |
| +// The delay to perform configuration after RRNotify. See the comment |
| +// in |Dispatch()|. |
| +const int64 kConfigureDelayMs = 500; |
| + |
| +gfx::Display GetFallbackDisplay() { |
| ::Display* display = ui::GetXDisplay(); |
| ::Screen* screen = DefaultScreenOfDisplay(display); |
| int width = WidthOfScreen(screen); |
| int height = HeightOfScreen(screen); |
| - return gfx::Size(width, height); |
| -} |
| - |
| -class DesktopScreenX11 : public gfx::Screen { |
| - public: |
| - DesktopScreenX11(); |
| - virtual ~DesktopScreenX11(); |
| - |
| - // Overridden from gfx::Screen: |
| - virtual bool IsDIPEnabled() OVERRIDE; |
| - virtual gfx::Point GetCursorScreenPoint() OVERRIDE; |
| - virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE; |
| - virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) |
| - OVERRIDE; |
| - virtual int GetNumDisplays() const OVERRIDE; |
| - virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE; |
| - virtual gfx::Display GetDisplayNearestWindow( |
| - gfx::NativeView window) const OVERRIDE; |
| - virtual gfx::Display GetDisplayNearestPoint( |
| - const gfx::Point& point) const OVERRIDE; |
| - virtual gfx::Display GetDisplayMatching( |
| - const gfx::Rect& match_rect) const OVERRIDE; |
| - virtual gfx::Display GetPrimaryDisplay() const OVERRIDE; |
| - virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE; |
| - virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE; |
| - |
| - private: |
| - DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11); |
| -}; |
| + return gfx::Display(0, gfx::Rect(0, 0, width, height)); |
| +} |
| + |
| +} // namespace |
| + |
| +namespace views { |
| //////////////////////////////////////////////////////////////////////////////// |
| // DesktopScreenX11, public: |
| -DesktopScreenX11::DesktopScreenX11() { |
| +DesktopScreenX11::DesktopScreenX11() |
| + : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()), |
| + x_root_window_(DefaultRootWindow(xdisplay_)), |
| + has_xrandr_(false), |
| + randr_version_major_(0), |
| + randr_version_minor_(0), |
| + xrandr_event_base_(0) { |
| + has_xrandr_ = XRRQueryVersion( |
| + xdisplay_, &randr_version_major_, &randr_version_minor_); |
|
Daniel Erat
2013/09/19 22:13:04
i don't see any reason why the major and minor ver
|
| + |
| + // We only support 1.3+. There were library changes before this and we should |
| + // use the new interface instead of the 1.2 one. |
| + has_xrandr_ = has_xrandr_ && randr_version_major_ == 1 && |
| + randr_version_minor_ >= 3; |
| + |
| + if (has_xrandr_) { |
| + int error_base_ignored = 0; |
| + XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored); |
| + |
| + base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this); |
| + XRRSelectInput(xdisplay_, |
| + x_root_window_, |
| + RRScreenChangeNotifyMask | RROutputChangeNotifyMask); |
| + |
| + displays_ = BuildDisplaysFromXRandRInfo(); |
| + } |
| } |
| DesktopScreenX11::~DesktopScreenX11() { |
| + if (has_xrandr_) |
| + base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this); |
| +} |
| + |
| +void DesktopScreenX11::ProcessDisplayChange( |
| + const std::vector<gfx::Display>& incoming) { |
| + std::vector<gfx::Display>::const_iterator cur_it = displays_.begin(); |
| + for (; cur_it != displays_.end(); ++cur_it) { |
| + bool found = false; |
| + for (std::vector<gfx::Display>::const_iterator incoming_it = |
| + incoming.begin(); incoming_it != incoming.end(); ++incoming_it) { |
| + if (cur_it->id() == incoming_it->id()) { |
| + found = true; |
| + break; |
| + } |
| + } |
| + |
| + if (!found) { |
| + FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, |
| + OnDisplayRemoved(*cur_it)); |
| + } |
| + } |
| + |
| + std::vector<gfx::Display>::const_iterator incoming_it = incoming.begin(); |
| + for (; incoming_it != incoming.end(); ++incoming_it) { |
| + bool found = false; |
| + for (std::vector<gfx::Display>::const_iterator cur_it = displays_.begin(); |
| + cur_it != displays_.end(); ++cur_it) { |
| + if (incoming_it->id() == cur_it->id()) { |
| + if (incoming_it->bounds() != cur_it->bounds()) { |
| + FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, |
| + OnDisplayBoundsChanged(*incoming_it)); |
| + } |
| + |
| + found = true; |
| + break; |
| + } |
| + } |
| + |
| + if (!found) { |
| + FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_, |
| + OnDisplayAdded(*incoming_it)); |
| + } |
| + } |
| + |
| + displays_ = incoming; |
| } |
| //////////////////////////////////////////////////////////////////////////////// |
| @@ -93,65 +146,204 @@ gfx::Point DesktopScreenX11::GetCursorScreenPoint() { |
| } |
| gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() { |
| - // TODO(erg): Implement using the discussion at |
| - // http://codereview.chromium.org/10279005/ |
| - return NULL; |
| + return GetWindowAtScreenPoint(GetCursorScreenPoint()); |
| } |
| gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint( |
| const gfx::Point& point) { |
| + // TODO(erg): Implement using the discussion at |
| + // http://codereview.chromium.org/10279005/ |
| NOTIMPLEMENTED(); |
| return NULL; |
| } |
| int DesktopScreenX11::GetNumDisplays() const { |
| - // TODO(erg): Figure this out with oshima or piman because I have no clue |
| - // about the XRandR implications here. |
| - return 1; |
| + if (!has_xrandr_) |
|
Daniel Erat
2013/09/19 22:13:04
can you just insert the fallback display in the c'
Elliot Glaysher
2013/09/19 23:08:34
That's a really good idea, and lets me unit test a
|
| + return 1; |
| + |
| + return displays_.size(); |
| } |
| std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const { |
| - // TODO(erg): Do the right thing once we know what that is. |
| - return std::vector<gfx::Display>(1, GetPrimaryDisplay()); |
| + if (!has_xrandr_) |
| + return std::vector<gfx::Display>(1, GetFallbackDisplay()); |
| + |
| + return displays_; |
| } |
| gfx::Display DesktopScreenX11::GetDisplayNearestWindow( |
| gfx::NativeView window) const { |
| - // TODO(erg): Do the right thing once we know what that is. |
| - return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize())); |
| + if (!has_xrandr_) |
| + return GetFallbackDisplay(); |
| + |
| + // TODO(erg): This should theoretically be easy, but it isn't. At the time we |
| + // get called here, our aura::Window has not been Init()ed, because this |
| + // method is called to get the device scale factor as part of |
| + // RootWindow::Init(), before Window::Init(). This seems very confused; we're |
| + // trying to get a display nearest window even before we've allocated the |
| + // root window. Once fixed, the correct implementation should probably be: |
| + // |
| + // return GetDisplayMatching(window->GetBoundsInScreen()); |
| + // |
| + // But at least for now, we'll just fallback: |
| + return GetPrimaryDisplay(); |
| } |
| gfx::Display DesktopScreenX11::GetDisplayNearestPoint( |
| const gfx::Point& point) const { |
| - // TODO(erg): Do the right thing once we know what that is. |
| - return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize())); |
| + if (!has_xrandr_) |
| + return GetFallbackDisplay(); |
| + |
| + for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); |
| + it != displays_.end(); ++it) { |
| + if (it->bounds().Contains(point)) |
| + return *it; |
| + } |
| + |
| + return GetPrimaryDisplay(); |
| } |
| gfx::Display DesktopScreenX11::GetDisplayMatching( |
| const gfx::Rect& match_rect) const { |
| - // TODO(erg): Do the right thing once we know what that is. |
| - return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize())); |
| + if (!has_xrandr_) |
| + return GetFallbackDisplay(); |
| + |
| + int max_area = 0; |
| + const gfx::Display* matching = NULL; |
| + for (std::vector<gfx::Display>::const_iterator it = displays_.begin(); |
| + it != displays_.end(); ++it) { |
| + gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect); |
| + int area = intersect.width() * intersect.height(); |
| + if (area > max_area) { |
| + max_area = area; |
| + matching = &*it; |
| + } |
| + } |
| + // Fallback to the primary display if there is no matching display. |
| + return matching ? *matching : GetPrimaryDisplay(); |
| } |
| gfx::Display DesktopScreenX11::GetPrimaryDisplay() const { |
| - // TODO(erg): Do the right thing once we know what that is. |
| - return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize())); |
| + if (has_xrandr_ && !displays_.empty()) |
| + return displays_.front(); |
| + |
| + return GetFallbackDisplay(); |
| } |
| void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) { |
| - // TODO(erg|oshima): Do the right thing once we know what that is. |
| - // crbug.com/122863 |
| + observer_list_.AddObserver(observer); |
| } |
| + |
| void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) { |
| - // TODO(erg|oshima): Do the right thing once we know what that is. |
| - // crbug.com/122863 |
| + observer_list_.RemoveObserver(observer); |
| } |
| -} // namespace |
| +bool DesktopScreenX11::Dispatch(const base::NativeEvent& event) { |
| + if (event->type - xrandr_event_base_ == RRScreenChangeNotify) { |
| + // Pass the event through to xlib. |
| + XRRUpdateConfiguration(event); |
| + } else if (event->type - xrandr_event_base_ == RRNotify) { |
| + // There's some sort of observer dispatch going on here, but I don't think |
| + // it's the screen's? |
| + DLOG(ERROR) << "DesktopScreenX11::Dispatch() -> RRNotify"; |
| + |
| + if (configure_timer_.get()) { |
| + configure_timer_->Reset(); |
| + } else { |
| + configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>()); |
| + configure_timer_->Start( |
| + FROM_HERE, |
| + base::TimeDelta::FromMilliseconds(kConfigureDelayMs), |
| + this, |
| + &DesktopScreenX11::ConfigureTimerFired); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| //////////////////////////////////////////////////////////////////////////////// |
| +// DesktopScreenX11, private: |
| + |
| +DesktopScreenX11::DesktopScreenX11( |
| + const std::vector<gfx::Display>& test_displays) |
| + : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()), |
| + x_root_window_(DefaultRootWindow(xdisplay_)), |
| + has_xrandr_(false), |
| + randr_version_major_(0), |
| + randr_version_minor_(0), |
| + xrandr_event_base_(0), |
| + displays_(test_displays) { |
| +} |
| -namespace views { |
| +std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { |
| + std::vector<gfx::Display> displays; |
| + XRRScreenResources* resources = |
| + XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_); |
| + if (!resources) { |
| + NOTIMPLEMENTED(); |
|
Daniel Erat
2013/09/19 22:13:04
maybe log an error instead? notimplemented seems s
|
| + return displays; |
| + } |
| + |
| + bool has_work_area = false; |
| + gfx::Rect work_area; |
| + std::vector<int> value; |
| + if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) && |
|
Daniel Erat
2013/09/19 22:13:04
there should be a separate 4-tuple for each deskto
Elliot Glaysher
2013/09/19 23:08:34
All the documentation that I can find says that _N
Daniel Erat
2013/09/19 23:32:37
the spec (http://standards.freedesktop.org/wm-spec
|
| + value.size() >= 4) { |
| + work_area = gfx::Rect(value[0], value[1], value[2], value[3]); |
| + has_work_area = true; |
| + } |
| + |
| + for (int i = 0; i < resources->noutput; ++i) { |
| + RROutput output_id = resources->outputs[i]; |
| + XRROutputInfo* output_info = |
| + XRRGetOutputInfo(xdisplay_, resources, output_id); |
| + |
| + bool is_connected = (output_info->connection == RR_Connected); |
| + if (!is_connected) { |
| + XRRFreeOutputInfo(output_info); |
| + continue; |
| + } |
| + |
| + if (output_info->crtc) { |
| + XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_, |
| + resources, |
| + output_info->crtc); |
| + |
| + int64 display_id = -1; |
| + if (!base::GetDisplayId(output_id, i, &display_id)) { |
| + // It isn't ideal, but if we can't parse the EDID data, fallback on the |
| + // display number. |
| + display_id = i; |
| + } |
| + |
| + gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height); |
| + gfx::Display display(display_id, crtc_bounds); |
| + if (has_work_area) { |
| + gfx::Rect intersection = crtc_bounds; |
| + intersection.Intersect(work_area); |
| + display.set_work_area(intersection); |
| + } |
| + |
| + displays.push_back(display); |
| + |
| + XRRFreeCrtcInfo(crtc); |
| + } |
| + |
| + XRRFreeOutputInfo(output_info); |
| + } |
| + |
| + XRRFreeScreenResources(resources); |
| + |
| + return displays; |
| +} |
| + |
| +void DesktopScreenX11::ConfigureTimerFired() { |
| + std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo(); |
| + ProcessDisplayChange(new_displays); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| gfx::Screen* CreateDesktopScreen() { |
| return new DesktopScreenX11; |