display_controller.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 "ash/display/display_controller.h"
6
7#include <algorithm>
8
9#include "ash/ash_switches.h"
10#include "ash/display/multi_display_manager.h"
11#include "ash/root_window_controller.h"
12#include "ash/shell.h"
13#include "ash/wm/coordinate_conversion.h"
14#include "ash/wm/property_util.h"
15#include "ash/wm/window_util.h"
16#include "base/command_line.h"
17#include "base/json/json_value_converter.h"
18#include "base/string_piece.h"
19#include "base/stringprintf.h"
20#include "base/values.h"
21#include "ui/aura/client/screen_position_client.h"
22#include "ui/aura/env.h"
23#include "ui/aura/root_window.h"
24#include "ui/aura/window.h"
25#include "ui/compositor/dip_util.h"
26#include "ui/gfx/display.h"
27#include "ui/gfx/screen.h"
28
29#if defined(OS_CHROMEOS)
30#include "base/chromeos/chromeos_version.h"
31#endif
32
33namespace ash {
34namespace {
35
36// Primary display stored in global object as it can be
37// accessed after Shell is deleted.
38int64 primary_display_id = gfx::Display::kInvalidDisplayID;
39
40// The maximum value for 'offset' in DisplayLayout in case of outliers.  Need
41// to change this value in case to support even larger displays.
42const int kMaxValidOffset = 10000;
43
44// The number of pixels to overlap between the primary and secondary displays,
45// in case that the offset value is too large.
46const int kMinimumOverlapForInvalidOffset = 100;
47
48bool GetPositionFromString(const base::StringPiece& position,
49                           DisplayLayout::Position* field) {
50  if (position == "top") {
51    *field = DisplayLayout::TOP;
52    return true;
53  } else if (position == "bottom") {
54    *field = DisplayLayout::BOTTOM;
55    return true;
56  } else if (position == "right") {
57    *field = DisplayLayout::RIGHT;
58    return true;
59  } else if (position == "left") {
60    *field = DisplayLayout::LEFT;
61    return true;
62  }
63  LOG(ERROR) << "Invalid position value: " << position;
64  return false;
65}
66
67std::string GetStringFromPosition(DisplayLayout::Position position) {
68  switch (position) {
69    case DisplayLayout::TOP:
70      return std::string("top");
71    case DisplayLayout::BOTTOM:
72      return std::string("bottom");
73    case DisplayLayout::RIGHT:
74      return std::string("right");
75    case DisplayLayout::LEFT:
76      return std::string("left");
77  }
78  return std::string("unknown");
79}
80
81internal::MultiDisplayManager* GetDisplayManager() {
82  return static_cast<internal::MultiDisplayManager*>(
83      aura::Env::GetInstance()->display_manager());
84}
85
86}  // namespace
87
88DisplayLayout::DisplayLayout()
89    : position(RIGHT),
90      offset(0) {}
91
92DisplayLayout::DisplayLayout(DisplayLayout::Position position, int offset)
93    : position(position),
94      offset(offset) {
95  DCHECK_LE(TOP, position);
96  DCHECK_GE(LEFT, position);
97
98  // Set the default value to |position| in case position is invalid.  DCHECKs
99  // above doesn't stop in Release builds.
100  if (TOP > position || LEFT < position)
101    this->position = RIGHT;
102
103  DCHECK_GE(kMaxValidOffset, abs(offset));
104}
105
106DisplayLayout DisplayLayout::Invert() const {
107  Position inverted_position = RIGHT;
108  switch (position) {
109    case TOP:
110      inverted_position = BOTTOM;
111      break;
112    case BOTTOM:
113      inverted_position = TOP;
114      break;
115    case RIGHT:
116      inverted_position = LEFT;
117      break;
118    case LEFT:
119      inverted_position = RIGHT;
120      break;
121  }
122  return DisplayLayout(inverted_position, -offset);
123}
124
125// static
126bool DisplayLayout::ConvertFromValue(const base::Value& value,
127                                     DisplayLayout* layout) {
128  base::JSONValueConverter<DisplayLayout> converter;
129  return converter.Convert(value, layout);
130}
131
132// static
133bool DisplayLayout::ConvertToValue(const DisplayLayout& layout,
134                                   base::Value* value) {
135  base::DictionaryValue* dict_value = NULL;
136  if (!value->GetAsDictionary(&dict_value) || dict_value == NULL)
137    return false;
138
139  const std::string position_str = GetStringFromPosition(layout.position);
140  dict_value->SetString("position", position_str);
141  dict_value->SetInteger("offset", layout.offset);
142  return true;
143}
144
145std::string DisplayLayout::ToString() const {
146  const std::string position_str = GetStringFromPosition(position);
147  return StringPrintf("%s, %d", position_str.c_str(), offset);
148}
149
150// static
151void DisplayLayout::RegisterJSONConverter(
152    base::JSONValueConverter<DisplayLayout>* converter) {
153  converter->RegisterCustomField<Position>(
154      "position", &DisplayLayout::position, &GetPositionFromString);
155  converter->RegisterIntField("offset", &DisplayLayout::offset);
156}
157
158DisplayController::DisplayController()
159    : desired_primary_display_id_(gfx::Display::kInvalidDisplayID) {
160  // Reset primary display to make sure that tests don't use
161  // stale display info from previous tests.
162  primary_display_id = gfx::Display::kInvalidDisplayID;
163
164  GetDisplayManager()->AddObserver(this);
165}
166
167DisplayController::~DisplayController() {
168  GetDisplayManager()->RemoveObserver(this);
169  // Delete all root window controllers, which deletes root window
170  // from the last so that the primary root window gets deleted last.
171  for (std::map<int64, aura::RootWindow*>::const_reverse_iterator it =
172           root_windows_.rbegin(); it != root_windows_.rend(); ++it) {
173    internal::RootWindowController* controller =
174        GetRootWindowController(it->second);
175    DCHECK(controller);
176    delete controller;
177  }
178}
179// static
180const gfx::Display& DisplayController::GetPrimaryDisplay() {
181  DCHECK_NE(primary_display_id, gfx::Display::kInvalidDisplayID);
182  return GetDisplayManager()->GetDisplayForId(primary_display_id);
183}
184
185void DisplayController::InitPrimaryDisplay() {
186  const gfx::Display* primary_candidate = GetDisplayManager()->GetDisplayAt(0);
187#if defined(OS_CHROMEOS)
188  if (base::chromeos::IsRunningOnChromeOS()) {
189    internal::MultiDisplayManager* display_manager = GetDisplayManager();
190    // On ChromeOS device, root windows are stacked vertically, and
191    // default primary is the one on top.
192    int count = display_manager->GetNumDisplays();
193    int y = primary_candidate->bounds_in_pixel().y();
194    for (int i = 1; i < count; ++i) {
195      const gfx::Display* display = display_manager->GetDisplayAt(i);
196      if (display_manager->IsInternalDisplayId(display->id())) {
197        primary_candidate = display;
198        break;
199      } else if (display->bounds_in_pixel().y() < y) {
200        primary_candidate = display;
201        y = display->bounds_in_pixel().y();
202      }
203    }
204  }
205#endif
206  primary_display_id = primary_candidate->id();
207  aura::RootWindow* root = AddRootWindowForDisplay(*primary_candidate);
208  root->SetHostBounds(primary_candidate->bounds_in_pixel());
209  UpdateDisplayBoundsForLayout();
210}
211
212void DisplayController::InitSecondaryDisplays() {
213  internal::MultiDisplayManager* display_manager = GetDisplayManager();
214  for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
215    const gfx::Display* display = display_manager->GetDisplayAt(i);
216    if (primary_display_id != display->id()) {
217      aura::RootWindow* root = AddRootWindowForDisplay(*display);
218      Shell::GetInstance()->InitRootWindowForSecondaryDisplay(root);
219    }
220  }
221  CommandLine* command_line = CommandLine::ForCurrentProcess();
222  if (command_line->HasSwitch(switches::kAshSecondaryDisplayLayout)) {
223    std::string value = command_line->GetSwitchValueASCII(
224        switches::kAshSecondaryDisplayLayout);
225    char layout;
226    int offset;
227    if (sscanf(value.c_str(), "%c,%d", &layout, &offset) == 2) {
228      if (layout == 't')
229        default_display_layout_.position = DisplayLayout::TOP;
230      else if (layout == 'b')
231        default_display_layout_.position = DisplayLayout::BOTTOM;
232      else if (layout == 'r')
233        default_display_layout_.position = DisplayLayout::RIGHT;
234      else if (layout == 'l')
235        default_display_layout_.position = DisplayLayout::LEFT;
236      default_display_layout_.offset = offset;
237    }
238  }
239  UpdateDisplayBoundsForLayout();
240}
241
242void DisplayController::AddObserver(Observer* observer) {
243  observers_.AddObserver(observer);
244}
245
246void DisplayController::RemoveObserver(Observer* observer) {
247  observers_.RemoveObserver(observer);
248}
249
250aura::RootWindow* DisplayController::GetPrimaryRootWindow() {
251  DCHECK(!root_windows_.empty());
252  return root_windows_[primary_display_id];
253}
254
255aura::RootWindow* DisplayController::GetRootWindowForDisplayId(int64 id) {
256  return root_windows_[id];
257}
258
259void DisplayController::CloseChildWindows() {
260  for (std::map<int64, aura::RootWindow*>::const_iterator it =
261           root_windows_.begin(); it != root_windows_.end(); ++it) {
262    aura::RootWindow* root_window = it->second;
263    internal::RootWindowController* controller =
264        GetRootWindowController(root_window);
265    if (controller) {
266      controller->CloseChildWindows();
267    } else {
268      while (!root_window->children().empty()) {
269        aura::Window* child = root_window->children()[0];
270        delete child;
271      }
272    }
273  }
274}
275
276std::vector<aura::RootWindow*> DisplayController::GetAllRootWindows() {
277  std::vector<aura::RootWindow*> windows;
278  for (std::map<int64, aura::RootWindow*>::const_iterator it =
279           root_windows_.begin(); it != root_windows_.end(); ++it) {
280    DCHECK(it->second);
281    if (GetRootWindowController(it->second))
282      windows.push_back(it->second);
283  }
284  return windows;
285}
286
287gfx::Insets DisplayController::GetOverscanInsets(int64 display_id) const {
288  return GetDisplayManager()->GetOverscanInsets(display_id);
289}
290
291std::vector<internal::RootWindowController*>
292DisplayController::GetAllRootWindowControllers() {
293  std::vector<internal::RootWindowController*> controllers;
294  for (std::map<int64, aura::RootWindow*>::const_iterator it =
295           root_windows_.begin(); it != root_windows_.end(); ++it) {
296    internal::RootWindowController* controller =
297        GetRootWindowController(it->second);
298    if (controller)
299      controllers.push_back(controller);
300  }
301  return controllers;
302}
303
304void DisplayController::SetDefaultDisplayLayout(const DisplayLayout& layout) {
305  CommandLine* command_line = CommandLine::ForCurrentProcess();
306  if (!command_line->HasSwitch(switches::kAshSecondaryDisplayLayout) &&
307      (default_display_layout_.position != layout.position ||
308       default_display_layout_.offset != layout.offset)) {
309    default_display_layout_ = layout;
310    NotifyDisplayConfigurationChanging();
311    UpdateDisplayBoundsForLayout();
312  }
313}
314
315void DisplayController::SetLayoutForDisplayName(const std::string& name,
316                                                const DisplayLayout& layout) {
317  DisplayLayout& display_for_name = secondary_layouts_[name];
318  if (display_for_name.position != layout.position ||
319      display_for_name.offset != layout.offset) {
320    secondary_layouts_[name] = layout;
321    NotifyDisplayConfigurationChanging();
322    UpdateDisplayBoundsForLayout();
323  }
324}
325
326const DisplayLayout& DisplayController::GetLayoutForDisplay(
327    const gfx::Display& display) const {
328  const std::string& name = GetDisplayManager()->GetDisplayNameFor(display);
329  std::map<std::string, DisplayLayout>::const_iterator it =
330      secondary_layouts_.find(name);
331
332  if (it != secondary_layouts_.end())
333    return it->second;
334  return default_display_layout_;
335}
336
337const DisplayLayout& DisplayController::GetCurrentDisplayLayout() const {
338  DCHECK_EQ(2U, GetDisplayManager()->GetNumDisplays());
339  if (GetDisplayManager()->GetNumDisplays() > 1) {
340    DisplayController* non_const = const_cast<DisplayController*>(this);
341    return GetLayoutForDisplay(*(non_const->GetSecondaryDisplay()));
342  }
343  // On release build, just fallback to default instead of blowing up.
344  return default_display_layout_;
345}
346
347void DisplayController::SetPrimaryDisplayId(int64 id) {
348  desired_primary_display_id_ = id;
349
350  if (desired_primary_display_id_ == primary_display_id)
351    return;
352
353  aura::DisplayManager* display_manager =
354      aura::Env::GetInstance()->display_manager();
355  for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
356    gfx::Display* display = display_manager->GetDisplayAt(i);
357    if (display->id() == id) {
358      SetPrimaryDisplay(*display);
359      break;
360    }
361  }
362}
363
364void DisplayController::SetPrimaryDisplay(
365    const gfx::Display& new_primary_display) {
366  internal::MultiDisplayManager* display_manager = GetDisplayManager();
367  DCHECK(new_primary_display.is_valid());
368  DCHECK(display_manager->IsActiveDisplay(new_primary_display));
369
370  if (!new_primary_display.is_valid() ||
371      !display_manager->IsActiveDisplay(new_primary_display)) {
372    LOG(ERROR) << "Invalid or non-existent display is requested:"
373               << new_primary_display.ToString();
374    return;
375  }
376
377  if (primary_display_id == new_primary_display.id() ||
378      root_windows_.size() < 2) {
379    return;
380  }
381
382  aura::RootWindow* non_primary_root = root_windows_[new_primary_display.id()];
383  LOG_IF(ERROR, !non_primary_root)
384      << "Unknown display is requested in SetPrimaryDisplay: id="
385      << new_primary_display.id();
386  if (!non_primary_root)
387    return;
388
389  gfx::Display old_primary_display = GetPrimaryDisplay();
390
391  // Swap root windows between current and new primary display.
392  aura::RootWindow* primary_root = root_windows_[primary_display_id];
393  DCHECK(primary_root);
394  DCHECK_NE(primary_root, non_primary_root);
395
396  root_windows_[new_primary_display.id()] = primary_root;
397  primary_root->SetProperty(internal::kDisplayIdKey, new_primary_display.id());
398
399  root_windows_[old_primary_display.id()] = non_primary_root;
400  non_primary_root->SetProperty(internal::kDisplayIdKey,
401                                old_primary_display.id());
402
403  primary_display_id = new_primary_display.id();
404  desired_primary_display_id_ = primary_display_id;
405
406  display_manager->UpdateWorkAreaOfDisplayNearestWindow(
407      primary_root, old_primary_display.GetWorkAreaInsets());
408  display_manager->UpdateWorkAreaOfDisplayNearestWindow(
409      non_primary_root, new_primary_display.GetWorkAreaInsets());
410
411  // Update the layout.
412  SetLayoutForDisplayName(
413      display_manager->GetDisplayNameFor(old_primary_display),
414      GetLayoutForDisplay(new_primary_display).Invert());
415
416  // Update the dispay manager with new display info.
417  std::vector<gfx::Display> displays;
418  displays.push_back(display_manager->GetDisplayForId(primary_display_id));
419  displays.push_back(*GetSecondaryDisplay());
420  GetDisplayManager()->set_force_bounds_changed(true);
421  GetDisplayManager()->OnNativeDisplaysChanged(displays);
422  GetDisplayManager()->set_force_bounds_changed(false);
423}
424
425gfx::Display* DisplayController::GetSecondaryDisplay() {
426  internal::MultiDisplayManager* display_manager = GetDisplayManager();
427  CHECK_EQ(2U, display_manager->GetNumDisplays());
428  return display_manager->GetDisplayAt(0)->id() == primary_display_id ?
429      display_manager->GetDisplayAt(1) : display_manager->GetDisplayAt(0);
430}
431
432void DisplayController::OnDisplayBoundsChanged(const gfx::Display& display) {
433  NotifyDisplayConfigurationChanging();
434  UpdateDisplayBoundsForLayout();
435  root_windows_[display.id()]->SetHostBounds(display.bounds_in_pixel());
436}
437
438void DisplayController::OnDisplayAdded(const gfx::Display& display) {
439  DCHECK(!root_windows_.empty());
440  NotifyDisplayConfigurationChanging();
441  aura::RootWindow* root = AddRootWindowForDisplay(display);
442  Shell::GetInstance()->InitRootWindowForSecondaryDisplay(root);
443  UpdateDisplayBoundsForLayout();
444
445  if (desired_primary_display_id_ == display.id())
446    SetPrimaryDisplay(display);
447}
448
449void DisplayController::OnDisplayRemoved(const gfx::Display& display) {
450  aura::RootWindow* root_to_delete = root_windows_[display.id()];
451  DCHECK(root_to_delete) << display.ToString();
452  NotifyDisplayConfigurationChanging();
453
454  // Display for root window will be deleted when the Primary RootWindow
455  // is deleted by the Shell.
456  root_windows_.erase(display.id());
457
458  // When the primary root window's display is removed, move the primary
459  // root to the other display.
460  if (primary_display_id == display.id()) {
461    DCHECK_EQ(1U, root_windows_.size());
462    primary_display_id = GetSecondaryDisplay()->id();
463    aura::RootWindow* primary_root = root_to_delete;
464
465    // Delete the other root instead.
466    root_to_delete = root_windows_[primary_display_id];
467    root_to_delete->SetProperty(internal::kDisplayIdKey, display.id());
468
469    // Setup primary root.
470    root_windows_[primary_display_id] = primary_root;
471    primary_root->SetProperty(internal::kDisplayIdKey, primary_display_id);
472
473    OnDisplayBoundsChanged(
474        GetDisplayManager()->GetDisplayForId(primary_display_id));
475  }
476  internal::RootWindowController* controller =
477      GetRootWindowController(root_to_delete);
478  DCHECK(controller);
479  controller->MoveWindowsTo(GetPrimaryRootWindow());
480  // Delete most of root window related objects, but don't delete
481  // root window itself yet because the stack may be using it.
482  controller->Shutdown();
483  MessageLoop::current()->DeleteSoon(FROM_HERE, controller);
484}
485
486aura::RootWindow* DisplayController::AddRootWindowForDisplay(
487    const gfx::Display& display) {
488  aura::RootWindow* root =
489      GetDisplayManager()->CreateRootWindowForDisplay(display);
490  root_windows_[display.id()] = root;
491
492#if defined(OS_CHROMEOS)
493  static bool force_constrain_pointer_to_root =
494      CommandLine::ForCurrentProcess()->HasSwitch(
495          switches::kAshConstrainPointerToRoot);
496  if (base::chromeos::IsRunningOnChromeOS() || force_constrain_pointer_to_root)
497    root->ConfineCursorToWindow();
498#endif
499  return root;
500}
501
502void DisplayController::UpdateDisplayBoundsForLayout() {
503  if (Shell::GetScreen()->GetNumDisplays() <= 1)
504    return;
505
506  DCHECK_EQ(2, Shell::GetScreen()->GetNumDisplays());
507  const gfx::Rect& primary_bounds = GetPrimaryDisplay().bounds();
508
509  gfx::Display* secondary_display = GetSecondaryDisplay();
510  const gfx::Rect& secondary_bounds = secondary_display->bounds();
511  gfx::Point new_secondary_origin = primary_bounds.origin();
512
513  const DisplayLayout& layout = GetLayoutForDisplay(*secondary_display);
514  DisplayLayout::Position position = layout.position;
515
516  // Ignore the offset in case the secondary display doesn't share edges with
517  // the primary display.
518  int offset = layout.offset;
519  if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) {
520    offset = std::min(
521        offset, primary_bounds.width() - kMinimumOverlapForInvalidOffset);
522    offset = std::max(
523        offset, -secondary_bounds.width() + kMinimumOverlapForInvalidOffset);
524  } else {
525    offset = std::min(
526        offset, primary_bounds.height() - kMinimumOverlapForInvalidOffset);
527    offset = std::max(
528        offset, -secondary_bounds.height() + kMinimumOverlapForInvalidOffset);
529  }
530  switch (position) {
531    case DisplayLayout::TOP:
532      new_secondary_origin.Offset(offset, -secondary_bounds.height());
533      break;
534    case DisplayLayout::RIGHT:
535      new_secondary_origin.Offset(primary_bounds.width(), offset);
536      break;
537    case DisplayLayout::BOTTOM:
538      new_secondary_origin.Offset(offset, primary_bounds.height());
539      break;
540    case DisplayLayout::LEFT:
541      new_secondary_origin.Offset(-secondary_bounds.width(), offset);
542      break;
543  }
544  gfx::Insets insets = secondary_display->GetWorkAreaInsets();
545  secondary_display->set_bounds(
546      gfx::Rect(new_secondary_origin, secondary_bounds.size()));
547  secondary_display->UpdateWorkAreaFromInsets(insets);
548}
549
550void DisplayController::NotifyDisplayConfigurationChanging() {
551  FOR_EACH_OBSERVER(Observer, observers_, OnDisplayConfigurationChanging());
552}
553
554}  // namespace ash
555