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