970d86cbec3d70aeebd47f97c5d2b9166eb42444
[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/base/x/x11_util.h"
20 #include "ui/display/x11/edid_parser_x11.h"
21 #include "ui/gfx/display.h"
22 #include "ui/gfx/display_observer.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
29 namespace {
30
31 // The delay to perform configuration after RRNotify.  See the comment
32 // in |Dispatch()|.
33 const int64 kConfigureDelayMs = 500;
34
35 float GetDeviceScaleFactor(int screen_pixels, int screen_mm) {
36   const int kCSSDefaultDPI = 96;
37   const float kInchInMm = 25.4f;
38
39   float screen_inches = screen_mm / kInchInMm;
40   float screen_dpi = screen_pixels / screen_inches;
41   float scale = screen_dpi / kCSSDefaultDPI;
42
43   return ui::GetImageScale(ui::GetSupportedScaleFactor(scale));
44 }
45
46 std::vector<gfx::Display> GetFallbackDisplayList() {
47   ::XDisplay* display = gfx::GetXDisplay();
48   ::Screen* screen = DefaultScreenOfDisplay(display);
49   int width = WidthOfScreen(screen);
50   int height = HeightOfScreen(screen);
51   int mm_width = WidthMMOfScreen(screen);
52   int mm_height = HeightMMOfScreen(screen);
53
54   gfx::Rect bounds_in_pixels(0, 0, width, height);
55   gfx::Display gfx_display(0, bounds_in_pixels);
56   if (!gfx::Display::HasForceDeviceScaleFactor() &&
57       !ui::IsXDisplaySizeBlackListed(mm_width, mm_height)) {
58     float device_scale_factor = GetDeviceScaleFactor(width, mm_width);
59     DCHECK_LE(1.0f, device_scale_factor);
60     gfx_display.SetScaleAndBounds(device_scale_factor, bounds_in_pixels);
61   }
62
63   return std::vector<gfx::Display>(1, gfx_display);
64 }
65
66 // Helper class to GetWindowAtScreenPoint() which returns the topmost window at
67 // the location passed to FindAt(). NULL is returned if a window which does not
68 // belong to Chromium is topmost at the passed in location.
69 class ToplevelWindowFinder : public ui::EnumerateWindowsDelegate {
70  public:
71   ToplevelWindowFinder() : toplevel_(NULL) {
72   }
73
74   virtual ~ToplevelWindowFinder() {
75   }
76
77   aura::Window* FindAt(const gfx::Point& screen_loc) {
78     screen_loc_ = screen_loc;
79     ui::EnumerateTopLevelWindows(this);
80     return toplevel_;
81   }
82
83  protected:
84   virtual bool ShouldStopIterating(XID xid) OVERRIDE {
85    if (!ui::IsWindowVisible(xid))
86      return false;
87
88     aura::Window* window =
89         views::DesktopWindowTreeHostX11::GetContentWindowForXID(xid);
90     if (window) {
91       // Currently |window|->IsVisible() always returns true.
92       // TODO(pkotwicz): Fix this. crbug.com/353038
93       if (window->IsVisible() &&
94           window->GetBoundsInScreen().Contains(screen_loc_)) {
95         toplevel_ = window;
96         return true;
97       }
98       return false;
99     }
100
101     if (ui::WindowContainsPoint(xid, screen_loc_)) {
102       // toplevel_ = NULL
103       return true;
104     }
105     return false;
106   }
107
108   gfx::Point screen_loc_;
109   aura::Window* toplevel_;
110
111   DISALLOW_COPY_AND_ASSIGN(ToplevelWindowFinder);
112 };
113
114 }  // namespace
115
116 namespace views {
117
118 ////////////////////////////////////////////////////////////////////////////////
119 // DesktopScreenX11, public:
120
121 DesktopScreenX11::DesktopScreenX11()
122     : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
123       x_root_window_(DefaultRootWindow(xdisplay_)),
124       has_xrandr_(false),
125       xrandr_event_base_(0) {
126   // We only support 1.3+. There were library changes before this and we should
127   // use the new interface instead of the 1.2 one.
128   int randr_version_major = 0;
129   int randr_version_minor = 0;
130   has_xrandr_ = XRRQueryVersion(
131         xdisplay_, &randr_version_major, &randr_version_minor) &&
132       randr_version_major == 1 &&
133       randr_version_minor >= 3;
134
135   if (has_xrandr_) {
136     int error_base_ignored = 0;
137     XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
138
139     base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this);
140     XRRSelectInput(xdisplay_,
141                    x_root_window_,
142                    RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
143
144     displays_ = BuildDisplaysFromXRandRInfo();
145   } else {
146     displays_ = GetFallbackDisplayList();
147   }
148 }
149
150 DesktopScreenX11::~DesktopScreenX11() {
151   if (has_xrandr_)
152     base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
153 }
154
155 void DesktopScreenX11::ProcessDisplayChange(
156     const std::vector<gfx::Display>& incoming) {
157   std::vector<gfx::Display> old_displays = displays_;
158   displays_ = incoming;
159
160   typedef std::vector<gfx::Display>::const_iterator DisplayIt;
161   std::vector<gfx::Display>::const_iterator old_it = old_displays.begin();
162   for (; old_it != old_displays.end(); ++old_it) {
163     bool found = false;
164     for (std::vector<gfx::Display>::const_iterator new_it =
165              displays_.begin(); new_it != displays_.end(); ++new_it) {
166       if (old_it->id() == new_it->id()) {
167         found = true;
168         break;
169       }
170     }
171
172     if (!found) {
173       FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
174                         OnDisplayRemoved(*old_it));
175     }
176   }
177
178   std::vector<gfx::Display>::const_iterator new_it = displays_.begin();
179   for (; new_it != displays_.end(); ++new_it) {
180     bool found = false;
181     for (std::vector<gfx::Display>::const_iterator old_it =
182          old_displays.begin(); old_it != old_displays.end(); ++old_it) {
183       if (new_it->id() == old_it->id()) {
184         if (new_it->bounds() != old_it->bounds()) {
185           FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
186                             OnDisplayBoundsChanged(*new_it));
187         }
188
189         found = true;
190         break;
191       }
192     }
193
194     if (!found) {
195       FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
196                         OnDisplayAdded(*new_it));
197     }
198   }
199 }
200
201 ////////////////////////////////////////////////////////////////////////////////
202 // DesktopScreenX11, gfx::Screen implementation:
203
204 bool DesktopScreenX11::IsDIPEnabled() {
205   return true;
206 }
207
208 gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
209   TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()");
210
211   XDisplay* display = gfx::GetXDisplay();
212
213   ::Window root, child;
214   int root_x, root_y, win_x, win_y;
215   unsigned int mask;
216   XQueryPointer(display,
217                 DefaultRootWindow(display),
218                 &root,
219                 &child,
220                 &root_x,
221                 &root_y,
222                 &win_x,
223                 &win_y,
224                 &mask);
225
226   return gfx::Point(root_x, root_y);
227 }
228
229 gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
230   return GetWindowAtScreenPoint(GetCursorScreenPoint());
231 }
232
233 gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
234     const gfx::Point& point) {
235   ToplevelWindowFinder finder;
236   return finder.FindAt(point);
237 }
238
239 int DesktopScreenX11::GetNumDisplays() const {
240   return displays_.size();
241 }
242
243 std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
244   return displays_;
245 }
246
247 gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
248     gfx::NativeView window) const {
249   // Getting screen bounds here safely is hard.
250   //
251   // You'd think we'd be able to just call window->GetBoundsInScreen(), but we
252   // can't because |window| (and the associated WindowEventDispatcher*) can be
253   // partially initialized at this point; WindowEventDispatcher initializations
254   // call through into GetDisplayNearestWindow(). But the X11 resources are
255   // created before we create the aura::WindowEventDispatcher. So we ask what
256   // the DRWHX11 believes the window bounds are instead of going through the
257   // aura::Window's screen bounds.
258   aura::WindowTreeHost* host = window->GetHost();
259   if (host) {
260     DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID(
261         host->GetAcceleratedWidget());
262     if (rwh)
263       return GetDisplayMatching(rwh->GetX11RootWindowBounds());
264   }
265
266   return GetPrimaryDisplay();
267 }
268
269 gfx::Display DesktopScreenX11::GetDisplayNearestPoint(
270     const gfx::Point& point) const {
271   for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
272        it != displays_.end(); ++it) {
273     if (it->bounds().Contains(point))
274       return *it;
275   }
276
277   return GetPrimaryDisplay();
278 }
279
280 gfx::Display DesktopScreenX11::GetDisplayMatching(
281     const gfx::Rect& match_rect) const {
282   int max_area = 0;
283   const gfx::Display* matching = NULL;
284   for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
285        it != displays_.end(); ++it) {
286     gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect);
287     int area = intersect.width() * intersect.height();
288     if (area > max_area) {
289       max_area = area;
290       matching = &*it;
291     }
292   }
293   // Fallback to the primary display if there is no matching display.
294   return matching ? *matching : GetPrimaryDisplay();
295 }
296
297 gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
298   return displays_.front();
299 }
300
301 void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
302   observer_list_.AddObserver(observer);
303 }
304
305 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
306   observer_list_.RemoveObserver(observer);
307 }
308
309 uint32_t DesktopScreenX11::Dispatch(const base::NativeEvent& event) {
310   if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
311     // Pass the event through to xlib.
312     XRRUpdateConfiguration(event);
313   } else if (event->type - xrandr_event_base_ == RRNotify) {
314     // There's some sort of observer dispatch going on here, but I don't think
315     // it's the screen's?
316     if (configure_timer_.get() && configure_timer_->IsRunning()) {
317       configure_timer_->Reset();
318     } else {
319       configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>());
320       configure_timer_->Start(
321           FROM_HERE,
322           base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
323           this,
324           &DesktopScreenX11::ConfigureTimerFired);
325     }
326   }
327
328   return POST_DISPATCH_NONE;
329 }
330
331 ////////////////////////////////////////////////////////////////////////////////
332 // DesktopScreenX11, private:
333
334 DesktopScreenX11::DesktopScreenX11(
335     const std::vector<gfx::Display>& test_displays)
336     : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
337       x_root_window_(DefaultRootWindow(xdisplay_)),
338       has_xrandr_(false),
339       xrandr_event_base_(0),
340       displays_(test_displays) {
341 }
342
343 std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
344   std::vector<gfx::Display> displays;
345   XRRScreenResources* resources =
346       XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_);
347   if (!resources) {
348     LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
349     return GetFallbackDisplayList();
350   }
351
352   bool has_work_area = false;
353   gfx::Rect work_area;
354   std::vector<int> value;
355   if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
356       value.size() >= 4) {
357     work_area = gfx::Rect(value[0], value[1], value[2], value[3]);
358     has_work_area = true;
359   }
360
361   float device_scale_factor = 1.0f;
362   for (int i = 0; i < resources->noutput; ++i) {
363     RROutput output_id = resources->outputs[i];
364     XRROutputInfo* output_info =
365         XRRGetOutputInfo(xdisplay_, resources, output_id);
366
367     bool is_connected = (output_info->connection == RR_Connected);
368     if (!is_connected) {
369       XRRFreeOutputInfo(output_info);
370       continue;
371     }
372
373     if (output_info->crtc) {
374       XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_,
375                                          resources,
376                                          output_info->crtc);
377
378       int64 display_id = -1;
379       if (!ui::GetDisplayId(output_id, static_cast<uint8>(i), &display_id)) {
380         // It isn't ideal, but if we can't parse the EDID data, fallback on the
381         // display number.
382         display_id = i;
383       }
384
385       gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
386       gfx::Display display(display_id, crtc_bounds);
387
388       if (!gfx::Display::HasForceDeviceScaleFactor()) {
389         if (i == 0 && !ui::IsXDisplaySizeBlackListed(output_info->mm_width,
390                                                      output_info->mm_height)) {
391           // As per display scale factor is not supported right now,
392           // the primary display's scale factor is always used.
393           device_scale_factor = GetDeviceScaleFactor(crtc->width,
394                                                      output_info->mm_width);
395           DCHECK_LE(1.0f, device_scale_factor);
396         }
397         display.SetScaleAndBounds(device_scale_factor, crtc_bounds);
398       }
399
400       if (has_work_area) {
401         gfx::Rect intersection = crtc_bounds;
402         intersection.Intersect(work_area);
403         display.set_work_area(intersection);
404       }
405
406       displays.push_back(display);
407
408       XRRFreeCrtcInfo(crtc);
409     }
410
411     XRRFreeOutputInfo(output_info);
412   }
413
414   XRRFreeScreenResources(resources);
415
416   if (displays.empty())
417     return GetFallbackDisplayList();
418
419   return displays;
420 }
421
422 void DesktopScreenX11::ConfigureTimerFired() {
423   std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo();
424   ProcessDisplayChange(new_displays);
425 }
426
427 ////////////////////////////////////////////////////////////////////////////////
428
429 gfx::Screen* CreateDesktopScreen() {
430   return new DesktopScreenX11;
431 }
432
433 }  // namespace views