| 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 880b3d05a9891fbe151d6bbbc9189e58174194ce..821ae459fcd6c8cf9ab87d4c020a6832d280c3d7 100644
|
| --- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc
|
| +++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
|
| @@ -2,68 +2,125 @@
|
| // 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/logging.h"
|
| +#include "base/x11/edid_parser_x11.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/gfx/x/x11_types.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;
|
| +
|
| +std::vector<gfx::Display> GetFallbackDisplayList() {
|
| ::XDisplay* display = gfx::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 std::vector<gfx::Display>(
|
| + 1, 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),
|
| + xrandr_event_base_(0) {
|
| + // We only support 1.3+. There were library changes before this and we should
|
| + // use the new interface instead of the 1.2 one.
|
| + int randr_version_major = 0;
|
| + int randr_version_minor = 0;
|
| + has_xrandr_ = XRRQueryVersion(
|
| + xdisplay_, &randr_version_major, &randr_version_minor) &&
|
| + 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();
|
| + } else {
|
| + displays_ = GetFallbackDisplayList();
|
| + }
|
| }
|
|
|
| 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 +150,187 @@ 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;
|
| + 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());
|
| + 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()));
|
| + // 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()));
|
| + 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()));
|
| + 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()));
|
| + return displays_.front();
|
| }
|
|
|
| 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),
|
| + 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) {
|
| + LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
|
| + return GetFallbackDisplayList();
|
| + }
|
| +
|
| + bool has_work_area = false;
|
| + gfx::Rect work_area;
|
| + std::vector<int> value;
|
| + if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
|
| + 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);
|
| +
|
| + if (displays.empty())
|
| + return GetFallbackDisplayList();
|
| +
|
| + return displays;
|
| +}
|
| +
|
| +void DesktopScreenX11::ConfigureTimerFired() {
|
| + std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo();
|
| + ProcessDisplayChange(new_displays);
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
|
|
| gfx::Screen* CreateDesktopScreen() {
|
| return new DesktopScreenX11;
|
|
|