Upstream version 5.34.104.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 "base/x11/edid_parser_x11.h"
16 #include "ui/aura/root_window.h"
17 #include "ui/aura/window.h"
18 #include "ui/aura/window_tree_host.h"
19 #include "ui/base/layout.h"
20 #include "ui/base/x/x11_util.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 }  // namespace
67
68 namespace views {
69
70 ////////////////////////////////////////////////////////////////////////////////
71 // DesktopScreenX11, public:
72
73 DesktopScreenX11::DesktopScreenX11()
74     : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
75       x_root_window_(DefaultRootWindow(xdisplay_)),
76       has_xrandr_(false),
77       xrandr_event_base_(0) {
78   // We only support 1.3+. There were library changes before this and we should
79   // use the new interface instead of the 1.2 one.
80   int randr_version_major = 0;
81   int randr_version_minor = 0;
82   has_xrandr_ = XRRQueryVersion(
83         xdisplay_, &randr_version_major, &randr_version_minor) &&
84       randr_version_major == 1 &&
85       randr_version_minor >= 3;
86
87   if (has_xrandr_) {
88     int error_base_ignored = 0;
89     XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
90
91     base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this);
92     XRRSelectInput(xdisplay_,
93                    x_root_window_,
94                    RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
95
96     displays_ = BuildDisplaysFromXRandRInfo();
97   } else {
98     displays_ = GetFallbackDisplayList();
99   }
100 }
101
102 DesktopScreenX11::~DesktopScreenX11() {
103   if (has_xrandr_)
104     base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
105 }
106
107 void DesktopScreenX11::ProcessDisplayChange(
108     const std::vector<gfx::Display>& incoming) {
109   std::vector<gfx::Display>::const_iterator cur_it = displays_.begin();
110   for (; cur_it != displays_.end(); ++cur_it) {
111     bool found = false;
112     for (std::vector<gfx::Display>::const_iterator incoming_it =
113              incoming.begin(); incoming_it != incoming.end(); ++incoming_it) {
114       if (cur_it->id() == incoming_it->id()) {
115         found = true;
116         break;
117       }
118     }
119
120     if (!found) {
121       FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
122                         OnDisplayRemoved(*cur_it));
123     }
124   }
125
126   std::vector<gfx::Display>::const_iterator incoming_it = incoming.begin();
127   for (; incoming_it != incoming.end(); ++incoming_it) {
128     bool found = false;
129     for (std::vector<gfx::Display>::const_iterator cur_it = displays_.begin();
130          cur_it != displays_.end(); ++cur_it) {
131       if (incoming_it->id() == cur_it->id()) {
132         if (incoming_it->bounds() != cur_it->bounds()) {
133           FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
134                             OnDisplayBoundsChanged(*incoming_it));
135         }
136
137         found = true;
138         break;
139       }
140     }
141
142     if (!found) {
143       FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
144                         OnDisplayAdded(*incoming_it));
145     }
146   }
147
148   displays_ = incoming;
149 }
150
151 ////////////////////////////////////////////////////////////////////////////////
152 // DesktopScreenX11, gfx::Screen implementation:
153
154 bool DesktopScreenX11::IsDIPEnabled() {
155   return true;
156 }
157
158 gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
159   TRACE_EVENT0("views", "DesktopScreenX11::GetCursorScreenPoint()");
160
161   XDisplay* display = gfx::GetXDisplay();
162
163   ::Window root, child;
164   int root_x, root_y, win_x, win_y;
165   unsigned int mask;
166   XQueryPointer(display,
167                 DefaultRootWindow(display),
168                 &root,
169                 &child,
170                 &root_x,
171                 &root_y,
172                 &win_x,
173                 &win_y,
174                 &mask);
175
176   return gfx::Point(root_x, root_y);
177 }
178
179 gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
180   return GetWindowAtScreenPoint(GetCursorScreenPoint());
181 }
182
183 gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
184     const gfx::Point& point) {
185   std::vector<aura::Window*> windows =
186       DesktopWindowTreeHostX11::GetAllOpenWindows();
187
188   for (std::vector<aura::Window*>::const_iterator it = windows.begin();
189        it != windows.end(); ++it) {
190     if ((*it)->GetBoundsInScreen().Contains(point))
191       return *it;
192   }
193
194   return NULL;
195 }
196
197 int DesktopScreenX11::GetNumDisplays() const {
198   return displays_.size();
199 }
200
201 std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
202   return displays_;
203 }
204
205 gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
206     gfx::NativeView window) const {
207   // Getting screen bounds here safely is hard.
208   //
209   // You'd think we'd be able to just call window->GetBoundsInScreen(), but we
210   // can't because |window| (and the associated RootWindow*) can be partially
211   // initialized at this point; RootWindow initializations call through into
212   // GetDisplayNearestWindow(). But the X11 resources are created before we
213   // create the aura::RootWindow. So we ask what the DRWHX11 believes the
214   // window bounds are instead of going through the aura::Window's screen
215   // bounds.
216   aura::WindowEventDispatcher* dispatcher = window->GetDispatcher();
217   if (dispatcher) {
218     DesktopWindowTreeHostX11* rwh = DesktopWindowTreeHostX11::GetHostForXID(
219         dispatcher->host()->GetAcceleratedWidget());
220     if (rwh)
221       return GetDisplayMatching(rwh->GetX11RootWindowBounds());
222   }
223
224   return GetPrimaryDisplay();
225 }
226
227 gfx::Display DesktopScreenX11::GetDisplayNearestPoint(
228     const gfx::Point& point) const {
229   for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
230        it != displays_.end(); ++it) {
231     if (it->bounds().Contains(point))
232       return *it;
233   }
234
235   return GetPrimaryDisplay();
236 }
237
238 gfx::Display DesktopScreenX11::GetDisplayMatching(
239     const gfx::Rect& match_rect) const {
240   int max_area = 0;
241   const gfx::Display* matching = NULL;
242   for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
243        it != displays_.end(); ++it) {
244     gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect);
245     int area = intersect.width() * intersect.height();
246     if (area > max_area) {
247       max_area = area;
248       matching = &*it;
249     }
250   }
251   // Fallback to the primary display if there is no matching display.
252   return matching ? *matching : GetPrimaryDisplay();
253 }
254
255 gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
256   return displays_.front();
257 }
258
259 void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
260   observer_list_.AddObserver(observer);
261 }
262
263 void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
264   observer_list_.RemoveObserver(observer);
265 }
266
267 uint32_t DesktopScreenX11::Dispatch(const base::NativeEvent& event) {
268   if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
269     // Pass the event through to xlib.
270     XRRUpdateConfiguration(event);
271   } else if (event->type - xrandr_event_base_ == RRNotify) {
272     // There's some sort of observer dispatch going on here, but I don't think
273     // it's the screen's?
274     if (configure_timer_.get() && configure_timer_->IsRunning()) {
275       configure_timer_->Reset();
276     } else {
277       configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>());
278       configure_timer_->Start(
279           FROM_HERE,
280           base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
281           this,
282           &DesktopScreenX11::ConfigureTimerFired);
283     }
284   }
285
286   return POST_DISPATCH_NONE;
287 }
288
289 ////////////////////////////////////////////////////////////////////////////////
290 // DesktopScreenX11, private:
291
292 DesktopScreenX11::DesktopScreenX11(
293     const std::vector<gfx::Display>& test_displays)
294     : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
295       x_root_window_(DefaultRootWindow(xdisplay_)),
296       has_xrandr_(false),
297       xrandr_event_base_(0),
298       displays_(test_displays) {
299 }
300
301 std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
302   std::vector<gfx::Display> displays;
303   XRRScreenResources* resources =
304       XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_);
305   if (!resources) {
306     LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
307     return GetFallbackDisplayList();
308   }
309
310   bool has_work_area = false;
311   gfx::Rect work_area;
312   std::vector<int> value;
313   if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
314       value.size() >= 4) {
315     work_area = gfx::Rect(value[0], value[1], value[2], value[3]);
316     has_work_area = true;
317   }
318
319   float device_scale_factor = 1.0f;
320   for (int i = 0; i < resources->noutput; ++i) {
321     RROutput output_id = resources->outputs[i];
322     XRROutputInfo* output_info =
323         XRRGetOutputInfo(xdisplay_, resources, output_id);
324
325     bool is_connected = (output_info->connection == RR_Connected);
326     if (!is_connected) {
327       XRRFreeOutputInfo(output_info);
328       continue;
329     }
330
331     if (output_info->crtc) {
332       XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_,
333                                          resources,
334                                          output_info->crtc);
335
336       int64 display_id = -1;
337       if (!base::GetDisplayId(output_id, i, &display_id)) {
338         // It isn't ideal, but if we can't parse the EDID data, fallback on the
339         // display number.
340         display_id = i;
341       }
342
343       gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
344       gfx::Display display(display_id, crtc_bounds);
345
346       if (!gfx::Display::HasForceDeviceScaleFactor()) {
347         if (i == 0 && !ui::IsXDisplaySizeBlackListed(output_info->mm_width,
348                                                      output_info->mm_height)) {
349           // As per display scale factor is not supported right now,
350           // the primary display's scale factor is always used.
351           device_scale_factor = GetDeviceScaleFactor(crtc->width,
352                                                      output_info->mm_width);
353           DCHECK_LE(1.0f, device_scale_factor);
354         }
355         display.SetScaleAndBounds(device_scale_factor, crtc_bounds);
356       }
357
358       if (has_work_area) {
359         gfx::Rect intersection = crtc_bounds;
360         intersection.Intersect(work_area);
361         display.set_work_area(intersection);
362       }
363
364       displays.push_back(display);
365
366       XRRFreeCrtcInfo(crtc);
367     }
368
369     XRRFreeOutputInfo(output_info);
370   }
371
372   XRRFreeScreenResources(resources);
373
374   if (displays.empty())
375     return GetFallbackDisplayList();
376
377   return displays;
378 }
379
380 void DesktopScreenX11::ConfigureTimerFired() {
381   std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo();
382   ProcessDisplayChange(new_displays);
383 }
384
385 ////////////////////////////////////////////////////////////////////////////////
386
387 gfx::Screen* CreateDesktopScreen() {
388   return new DesktopScreenX11;
389 }
390
391 }  // namespace views