Home | History | Annotate | Download | only in desktop_aura
      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