083e900f1070157d6f61ec4ede693f6a6f26699c
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / desktop_screen_x11.cc
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 gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
116   TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()");
117
118   XDisplay* display = gfx::GetXDisplay();
119
120   ::Window root, child;
121   int root_x, root_y, win_x, win_y;
122   unsigned int mask;
123   XQueryPointer(display,
124                 DefaultRootWindow(display),
125                 &root,
126                 &child,
127                 &root_x,
128                 &root_y,
129                 &win_x,
130                 &win_y,
131                 &mask);
132
133   return gfx::Point(root_x, root_y);
134 }
135
136 gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
137   return GetWindowAtScreenPoint(GetCursorScreenPoint());
138 }
139
140 gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
141     const gfx::Point& point) {
142   X11TopmostWindowFinder finder;
143   return finder.FindLocalProcessWindowAt(point, std::set<aura::Window*>());
144 }
145
146 int DesktopScreenX11::GetNumDisplays() const {
147   return displays_.size();
148 }
149
150 std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
151   return displays_;
152 }
153
154 gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
155     gfx::NativeView window) const {
156   if (!window)
157     return GetPrimaryDisplay();
158
159   // Getting screen bounds here safely is hard.
160   //
161   // You'd think we'd be able to just call window->GetBoundsInScreen(), but we
162   // can't because |window| (and the associated WindowEventDispatcher*) can be
163   // partially initialized at this point; WindowEventDispatcher initializations
164   // call through into GetDisplayNearestWindow(). But the X11 resources are
165   // created before we create the aura::WindowEventDispatcher. So we ask what
166   // the DRWHX11 believes the window bounds are instead of going through the
167   // aura::Window's screen bounds.
168   aura::WindowTreeHost* host = window->GetHost();
169   if (host) {
170     DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID(
171         host->GetAcceleratedWidget());
172     if (rwh)
173       return GetDisplayMatching(rwh->GetX11RootWindowBounds());
174   }
175
176   return GetPrimaryDisplay();
177 }
178
179 gfx::Display DesktopScreenX11::GetDisplayNearestPoint(
180     const gfx::Point& point) const {
181   for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
182        it != displays_.end(); ++it) {
183     if (it->bounds().Contains(point))
184       return *it;
185   }
186
187   return GetPrimaryDisplay();
188 }
189
190 gfx::Display DesktopScreenX11::GetDisplayMatching(
191     const gfx::Rect& match_rect) const {
192   int max_area = 0;
193   const gfx::Display* matching = NULL;
194   for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
195        it != displays_.end(); ++it) {
196     gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect);
197     int area = intersect.width() * intersect.height();
198     if (area > max_area) {
199       max_area = area;
200       matching = &*it;
201     }
202   }
203   // Fallback to the primary display if there is no matching display.
204   return matching ? *matching : GetPrimaryDisplay();
205 }
206
207 gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
208   return displays_.front();
209 }
210
211 void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
212   change_notifier_.AddObserver(observer);
213 }
214
215 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
216   change_notifier_.RemoveObserver(observer);
217 }
218
219 bool DesktopScreenX11::CanDispatchEvent(const ui::PlatformEvent& event) {
220   return event->type - xrandr_event_base_ == RRScreenChangeNotify ||
221          event->type - xrandr_event_base_ == RRNotify;
222 }
223
224 uint32_t DesktopScreenX11::DispatchEvent(const ui::PlatformEvent& event) {
225   if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
226     // Pass the event through to xlib.
227     XRRUpdateConfiguration(event);
228   } else if (event->type - xrandr_event_base_ == RRNotify) {
229     // There's some sort of observer dispatch going on here, but I don't think
230     // it's the screen's?
231     if (configure_timer_.get() && configure_timer_->IsRunning()) {
232       configure_timer_->Reset();
233     } else {
234       configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>());
235       configure_timer_->Start(
236           FROM_HERE,
237           base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
238           this,
239           &DesktopScreenX11::ConfigureTimerFired);
240     }
241   } else {
242     NOTREACHED();
243   }
244
245   return ui::POST_DISPATCH_NONE;
246 }
247
248 ////////////////////////////////////////////////////////////////////////////////
249 // DesktopScreenX11, private:
250
251 DesktopScreenX11::DesktopScreenX11(
252     const std::vector<gfx::Display>& test_displays)
253     : xdisplay_(gfx::GetXDisplay()),
254       x_root_window_(DefaultRootWindow(xdisplay_)),
255       has_xrandr_(false),
256       xrandr_event_base_(0),
257       displays_(test_displays) {
258 }
259
260 std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
261   std::vector<gfx::Display> displays;
262   XRRScreenResources* resources =
263       XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_);
264   if (!resources) {
265     LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
266     return GetFallbackDisplayList();
267   }
268
269   bool has_work_area = false;
270   gfx::Rect work_area;
271   std::vector<int> value;
272   if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
273       value.size() >= 4) {
274     work_area = gfx::Rect(value[0], value[1], value[2], value[3]);
275     has_work_area = true;
276   }
277
278   float device_scale_factor = 1.0f;
279   for (int i = 0; i < resources->noutput; ++i) {
280     RROutput output_id = resources->outputs[i];
281     XRROutputInfo* output_info =
282         XRRGetOutputInfo(xdisplay_, resources, output_id);
283
284     bool is_connected = (output_info->connection == RR_Connected);
285     if (!is_connected) {
286       XRRFreeOutputInfo(output_info);
287       continue;
288     }
289
290     if (output_info->crtc) {
291       XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_,
292                                          resources,
293                                          output_info->crtc);
294
295       int64 display_id = -1;
296       if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) {
297         // It isn't ideal, but if we can't parse the EDID data, fallback on the
298         // display number.
299         display_id = i;
300       }
301
302       gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
303       gfx::Display display(display_id, crtc_bounds);
304
305       if (!gfx::Display::HasForceDeviceScaleFactor()) {
306         if (i == 0 && !ui::IsDisplaySizeBlackListed(
307             gfx::Size(output_info->mm_width, output_info->mm_height))) {
308           // As per display scale factor is not supported right now,
309           // the primary display's scale factor is always used.
310           device_scale_factor = GetDeviceScaleFactor(crtc->width,
311                                                      output_info->mm_width);
312           DCHECK_LE(1.0f, device_scale_factor);
313         }
314         display.SetScaleAndBounds(device_scale_factor, crtc_bounds);
315       }
316
317       if (has_work_area) {
318         gfx::Rect intersection = crtc_bounds;
319         intersection.Intersect(work_area);
320         display.set_work_area(intersection);
321       }
322
323       switch (crtc->rotation) {
324         case RR_Rotate_0:
325           display.set_rotation(gfx::Display::ROTATE_0);
326           break;
327         case RR_Rotate_90:
328           display.set_rotation(gfx::Display::ROTATE_90);
329           break;
330         case RR_Rotate_180:
331           display.set_rotation(gfx::Display::ROTATE_180);
332           break;
333         case RR_Rotate_270:
334           display.set_rotation(gfx::Display::ROTATE_270);
335           break;
336       }
337
338       displays.push_back(display);
339
340       XRRFreeCrtcInfo(crtc);
341     }
342
343     XRRFreeOutputInfo(output_info);
344   }
345
346   XRRFreeScreenResources(resources);
347
348   if (displays.empty())
349     return GetFallbackDisplayList();
350
351   return displays;
352 }
353
354 void DesktopScreenX11::ConfigureTimerFired() {
355   std::vector<gfx::Display> old_displays = displays_;
356   displays_ = BuildDisplaysFromXRandRInfo();
357
358   change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
359 }
360
361 ////////////////////////////////////////////////////////////////////////////////
362
363 gfx::Screen* CreateDesktopScreen() {
364   return new DesktopScreenX11;
365 }
366
367 }  // namespace views