Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(384)

Side by Side Diff: ash/display/display_controller.cc

Issue 12505005: Store rotation/ui scale to local state. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ash/display/display_controller.h" 5 #include "ash/display/display_controller.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "ash/ash_switches.h" 9 #include "ash/ash_switches.h"
10 #include "ash/display/display_manager.h" 10 #include "ash/display/display_manager.h"
(...skipping 30 matching lines...) Expand all
41 // potential conflict with chrome headers. 41 // potential conflict with chrome headers.
42 #include <X11/extensions/Xrandr.h> 42 #include <X11/extensions/Xrandr.h>
43 #undef RootWindow 43 #undef RootWindow
44 #endif // defined(OS_CHROMEOS) 44 #endif // defined(OS_CHROMEOS)
45 45
46 DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation); 46 DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation);
47 47
48 namespace ash { 48 namespace ash {
49 namespace { 49 namespace {
50 50
51 DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationKey, 51 DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropKey,
James Cook 2013/03/22 16:53:14 kRotationPropertyKey please
oshima 2013/03/22 18:20:42 Done.
52 gfx::Display::ROTATE_0); 52 gfx::Display::ROTATE_0);
53 53
54 // Primary display stored in global object as it can be 54 // Primary display stored in global object as it can be
55 // accessed after Shell is deleted. A separate display instance is created 55 // accessed after Shell is deleted. A separate display instance is created
56 // during the shutdown instead of always keeping two display instances 56 // during the shutdown instead of always keeping two display instances
57 // (one here and another one in display_manager) in sync, which is error prone. 57 // (one here and another one in display_manager) in sync, which is error prone.
58 int64 primary_display_id = gfx::Display::kInvalidDisplayID; 58 int64 primary_display_id = gfx::Display::kInvalidDisplayID;
59 gfx::Display* primary_display_for_shutdown = NULL; 59 gfx::Display* primary_display_for_shutdown = NULL;
60 // Keeps the number of displays during the shutdown after 60 // Keeps the number of displays during the shutdown after
61 // ash::Shell:: is deleted. 61 // ash::Shell:: is deleted.
(...skipping 10 matching lines...) Expand all
72 // Specifies how long the display change should have been disabled 72 // Specifies how long the display change should have been disabled
73 // after each display change operations. 73 // after each display change operations.
74 // |kCycleDisplayThrottleTimeoutMs| is set to be longer to avoid 74 // |kCycleDisplayThrottleTimeoutMs| is set to be longer to avoid
75 // changing the settings while the system is still configurating 75 // changing the settings while the system is still configurating
76 // displays. It will be overriden by |kAfterDisplayChangeThrottleTimeoutMs| 76 // displays. It will be overriden by |kAfterDisplayChangeThrottleTimeoutMs|
77 // when the display change happens, so the actual timeout is much shorter. 77 // when the display change happens, so the actual timeout is much shorter.
78 const int64 kAfterDisplayChangeThrottleTimeoutMs = 500; 78 const int64 kAfterDisplayChangeThrottleTimeoutMs = 500;
79 const int64 kCycleDisplayThrottleTimeoutMs = 4000; 79 const int64 kCycleDisplayThrottleTimeoutMs = 4000;
80 const int64 kSwapDisplayThrottleTimeoutMs = 500; 80 const int64 kSwapDisplayThrottleTimeoutMs = 500;
81 81
82 // Persistent key names
83 const char kPositionKey[] = "position";
84 const char kOffsetKey[] = "offset";
85
82 bool GetPositionFromString(const base::StringPiece& position, 86 bool GetPositionFromString(const base::StringPiece& position,
83 DisplayLayout::Position* field) { 87 DisplayLayout::Position* field) {
84 if (position == "top") { 88 if (position == "top") {
85 *field = DisplayLayout::TOP; 89 *field = DisplayLayout::TOP;
86 return true; 90 return true;
87 } else if (position == "bottom") { 91 } else if (position == "bottom") {
88 *field = DisplayLayout::BOTTOM; 92 *field = DisplayLayout::BOTTOM;
89 return true; 93 return true;
90 } else if (position == "right") { 94 } else if (position == "right") {
91 *field = DisplayLayout::RIGHT; 95 *field = DisplayLayout::RIGHT;
(...skipping 26 matching lines...) Expand all
118 122
119 void RotateRootWindow(aura::RootWindow* root_window, 123 void RotateRootWindow(aura::RootWindow* root_window,
120 const gfx::Display& display, 124 const gfx::Display& display,
121 const internal::DisplayInfo& info) { 125 const internal::DisplayInfo& info) {
122 // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade) 126 // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade)
123 #if defined(OS_WIN) 127 #if defined(OS_WIN)
124 // Windows 8 bots refused to resize the host window, and 128 // Windows 8 bots refused to resize the host window, and
125 // updating the transform results in incorrectly resizing 129 // updating the transform results in incorrectly resizing
126 // the root window. Don't apply the transform unless 130 // the root window. Don't apply the transform unless
127 // necessary so that unit tests pass on win8 bots. 131 // necessary so that unit tests pass on win8 bots.
128 if (info.rotation() == root_window->GetProperty(kRotationKey)) 132 if (info.rotation() == root_window->GetProperty(kRotationPropKey))
129 return; 133 return;
130 root_window->SetProperty(kRotationKey, info.rotation()); 134 root_window->SetProperty(kRotationPropKey, info.rotation());
131 #endif 135 #endif
132 gfx::Transform rotate; 136 gfx::Transform rotate;
133 // The origin is (0, 0), so the translate width/height must be reduced by 1. 137 // The origin is (0, 0), so the translate width/height must be reduced by 1.
134 switch (info.rotation()) { 138 switch (info.rotation()) {
135 case gfx::Display::ROTATE_0: 139 case gfx::Display::ROTATE_0:
136 break; 140 break;
137 case gfx::Display::ROTATE_90: 141 case gfx::Display::ROTATE_90:
138 rotate.Translate(display.bounds().height() - 1, 0); 142 rotate.Translate(display.bounds().height() - 1, 0);
139 rotate.Rotate(90); 143 rotate.Rotate(90);
140 break; 144 break;
141 case gfx::Display::ROTATE_270: 145 case gfx::Display::ROTATE_270:
142 rotate.Translate(0, display.bounds().width() - 1); 146 rotate.Translate(0, display.bounds().width() - 1);
143 rotate.Rotate(270); 147 rotate.Rotate(270);
144 break; 148 break;
145 case gfx::Display::ROTATE_180: 149 case gfx::Display::ROTATE_180:
146 rotate.Translate(display.bounds().width() - 1, 150 rotate.Translate(display.bounds().width() - 1,
147 display.bounds().height() - 1); 151 display.bounds().height() - 1);
148 rotate.Rotate(180); 152 rotate.Rotate(180);
149 break; 153 break;
150 } 154 }
151 root_window->SetTransform(rotate); 155 root_window->SetTransform(rotate);
152 } 156 }
153 157
154 void SetDisplayPropertiesOnHostWindow(aura::RootWindow* root, 158 void SetDisplayPropertiesOnHostWindow(aura::RootWindow* root,
155 const gfx::Display& display) { 159 const gfx::Display& display) {
156 internal::DisplayInfo info = GetDisplayManager()->GetDisplayInfo(display); 160 internal::DisplayInfo info =
161 GetDisplayManager()->GetDisplayInfo(display.id());
157 #if defined(OS_CHROMEOS) 162 #if defined(OS_CHROMEOS)
158 // Native window property (Atom in X11) that specifies the display's 163 // Native window property (Atom in X11) that specifies the display's
159 // rotation, scale factor and if it's internal display. They are 164 // rotation, scale factor and if it's internal display. They are
160 // read and used by touchpad/mouse driver directly on X (contact 165 // read and used by touchpad/mouse driver directly on X (contact
161 // adlr@ for more details on touchpad/mouse driver side). The value 166 // adlr@ for more details on touchpad/mouse driver side). The value
162 // of the rotation is one of 0 (normal), 1 (90 degrees clockwise), 2 167 // of the rotation is one of 0 (normal), 1 (90 degrees clockwise), 2
163 // (180 degree) or 3 (270 degrees clockwise). The value of the 168 // (180 degree) or 3 (270 degrees clockwise). The value of the
164 // scale factor is in percent (100, 140, 200 etc). 169 // scale factor is in percent (100, 140, 200 etc).
165 const char kRotationProp[] = "_CHROME_DISPLAY_ROTATION"; 170 const char kRotationProp[] = "_CHROME_DISPLAY_ROTATION";
166 const char kScaleFactorProp[] = "_CHROME_DISPLAY_SCALE_FACTOR"; 171 const char kScaleFactorProp[] = "_CHROME_DISPLAY_SCALE_FACTOR";
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
249 } 254 }
250 255
251 // static 256 // static
252 bool DisplayLayout::ConvertToValue(const DisplayLayout& layout, 257 bool DisplayLayout::ConvertToValue(const DisplayLayout& layout,
253 base::Value* value) { 258 base::Value* value) {
254 base::DictionaryValue* dict_value = NULL; 259 base::DictionaryValue* dict_value = NULL;
255 if (!value->GetAsDictionary(&dict_value) || dict_value == NULL) 260 if (!value->GetAsDictionary(&dict_value) || dict_value == NULL)
256 return false; 261 return false;
257 262
258 const std::string position_str = GetStringFromPosition(layout.position); 263 const std::string position_str = GetStringFromPosition(layout.position);
259 dict_value->SetString("position", position_str); 264 dict_value->SetString(kPositionKey, position_str);
260 dict_value->SetInteger("offset", layout.offset); 265 dict_value->SetInteger(kOffsetKey, layout.offset);
261 return true; 266 return true;
262 } 267 }
263 268
264 std::string DisplayLayout::ToString() const { 269 std::string DisplayLayout::ToString() const {
265 const std::string position_str = GetStringFromPosition(position); 270 const std::string position_str = GetStringFromPosition(position);
266 return base::StringPrintf("%s, %d", position_str.c_str(), offset); 271 return base::StringPrintf("%s, %d", position_str.c_str(), offset);
267 } 272 }
268 273
269 // static 274 // static
270 void DisplayLayout::RegisterJSONConverter( 275 void DisplayLayout::RegisterJSONConverter(
271 base::JSONValueConverter<DisplayLayout>* converter) { 276 base::JSONValueConverter<DisplayLayout>* converter) {
272 converter->RegisterCustomField<Position>( 277 converter->RegisterCustomField<Position>(
273 "position", &DisplayLayout::position, &GetPositionFromString); 278 kPositionKey, &DisplayLayout::position, &GetPositionFromString);
274 converter->RegisterIntField("offset", &DisplayLayout::offset); 279 converter->RegisterIntField(kOffsetKey, &DisplayLayout::offset);
275 } 280 }
276 281
277 //////////////////////////////////////////////////////////////////////////////// 282 ////////////////////////////////////////////////////////////////////////////////
278 // DisplayChangeLimiter 283 // DisplayChangeLimiter
279 284
280 DisplayController::DisplayChangeLimiter::DisplayChangeLimiter() 285 DisplayController::DisplayChangeLimiter::DisplayChangeLimiter()
281 : throttle_timeout_(base::Time::Now()) { 286 : throttle_timeout_(base::Time::Now()) {
282 } 287 }
283 288
284 void DisplayController::DisplayChangeLimiter::SetThrottleTimeout( 289 void DisplayController::DisplayChangeLimiter::SetThrottleTimeout(
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 default_display_layout_.position = DisplayLayout::LEFT; 324 default_display_layout_.position = DisplayLayout::LEFT;
320 default_display_layout_.offset = offset; 325 default_display_layout_.offset = offset;
321 } 326 }
322 } 327 }
323 // Reset primary display to make sure that tests don't use 328 // Reset primary display to make sure that tests don't use
324 // stale display info from previous tests. 329 // stale display info from previous tests.
325 primary_display_id = gfx::Display::kInvalidDisplayID; 330 primary_display_id = gfx::Display::kInvalidDisplayID;
326 delete primary_display_for_shutdown; 331 delete primary_display_for_shutdown;
327 primary_display_for_shutdown = NULL; 332 primary_display_for_shutdown = NULL;
328 num_displays_for_shutdown = -1; 333 num_displays_for_shutdown = -1;
329
330 Shell::GetScreen()->AddObserver(this);
331 } 334 }
332 335
333 DisplayController::~DisplayController() { 336 DisplayController::~DisplayController() {
334 DCHECK(primary_display_for_shutdown); 337 DCHECK(primary_display_for_shutdown);
335 } 338 }
336 339
340 void DisplayController::Start() {
341 Shell::GetScreen()->AddObserver(this);
342 }
343
337 void DisplayController::Shutdown() { 344 void DisplayController::Shutdown() {
338 DCHECK(!primary_display_for_shutdown); 345 DCHECK(!primary_display_for_shutdown);
339 primary_display_for_shutdown = new gfx::Display( 346 primary_display_for_shutdown = new gfx::Display(
340 GetDisplayManager()->GetDisplayForId(primary_display_id)); 347 GetDisplayManager()->GetDisplayForId(primary_display_id));
341 num_displays_for_shutdown = GetDisplayManager()->GetNumDisplays(); 348 num_displays_for_shutdown = GetDisplayManager()->GetNumDisplays();
342 349
343 Shell::GetScreen()->RemoveObserver(this); 350 Shell::GetScreen()->RemoveObserver(this);
344 // Delete all root window controllers, which deletes root window 351 // Delete all root window controllers, which deletes root window
345 // from the last so that the primary root window gets deleted last. 352 // from the last so that the primary root window gets deleted last.
346 for (std::map<int64, aura::RootWindow*>::const_reverse_iterator it = 353 for (std::map<int64, aura::RootWindow*>::const_reverse_iterator it =
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after
655 desired_primary_display_id_ = primary_display_id; 662 desired_primary_display_id_ = primary_display_id;
656 663
657 display_manager->UpdateWorkAreaOfDisplayNearestWindow( 664 display_manager->UpdateWorkAreaOfDisplayNearestWindow(
658 primary_root, old_primary_display.GetWorkAreaInsets()); 665 primary_root, old_primary_display.GetWorkAreaInsets());
659 display_manager->UpdateWorkAreaOfDisplayNearestWindow( 666 display_manager->UpdateWorkAreaOfDisplayNearestWindow(
660 non_primary_root, new_primary_display.GetWorkAreaInsets()); 667 non_primary_root, new_primary_display.GetWorkAreaInsets());
661 668
662 // Update the dispay manager with new display info. 669 // Update the dispay manager with new display info.
663 std::vector<internal::DisplayInfo> display_info_list; 670 std::vector<internal::DisplayInfo> display_info_list;
664 display_info_list.push_back(display_manager->GetDisplayInfo( 671 display_info_list.push_back(display_manager->GetDisplayInfo(
665 display_manager->GetDisplayForId(primary_display_id))); 672 primary_display_id));
666 display_info_list.push_back(display_manager->GetDisplayInfo( 673 display_info_list.push_back(display_manager->GetDisplayInfo(
667 *GetSecondaryDisplay())); 674 GetSecondaryDisplay()->id()));
668 GetDisplayManager()->set_force_bounds_changed(true); 675 GetDisplayManager()->set_force_bounds_changed(true);
669 GetDisplayManager()->UpdateDisplays(display_info_list); 676 GetDisplayManager()->UpdateDisplays(display_info_list);
670 GetDisplayManager()->set_force_bounds_changed(false); 677 GetDisplayManager()->set_force_bounds_changed(false);
671 } 678 }
672 679
673 gfx::Display* DisplayController::GetSecondaryDisplay() { 680 gfx::Display* DisplayController::GetSecondaryDisplay() {
674 internal::DisplayManager* display_manager = GetDisplayManager(); 681 internal::DisplayManager* display_manager = GetDisplayManager();
675 CHECK_EQ(2U, display_manager->GetNumDisplays()); 682 CHECK_EQ(2U, display_manager->GetNumDisplays());
676 return display_manager->GetDisplayAt(0)->id() == primary_display_id ? 683 return display_manager->GetDisplayAt(0)->id() == primary_display_id ?
677 display_manager->GetDisplayAt(1) : display_manager->GetDisplayAt(0); 684 display_manager->GetDisplayAt(1) : display_manager->GetDisplayAt(0);
678 } 685 }
679 686
680 void DisplayController::OnDisplayBoundsChanged(const gfx::Display& display) { 687 void DisplayController::OnDisplayBoundsChanged(const gfx::Display& display) {
681 if (limiter_.get()) 688 if (limiter_.get())
682 limiter_->SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs); 689 limiter_->SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs);
683 const internal::DisplayInfo& display_info = 690 const internal::DisplayInfo& display_info =
684 GetDisplayManager()->GetDisplayInfo(display); 691 GetDisplayManager()->GetDisplayInfo(display.id());
685 DCHECK(!display_info.bounds_in_pixel().IsEmpty()); 692 DCHECK(!display_info.bounds_in_pixel().IsEmpty());
686 693
687 UpdateDisplayBoundsForLayout(); 694 UpdateDisplayBoundsForLayout();
688 aura::RootWindow* root = root_windows_[display.id()]; 695 aura::RootWindow* root = root_windows_[display.id()];
689 root->SetHostBoundsAndInsetsAndRootWindowScale( 696 root->SetHostBoundsAndInsetsAndRootWindowScale(
690 display_info.bounds_in_pixel(), 697 display_info.bounds_in_pixel(),
691 display_info.GetOverscanInsetsInPixel(), 698 display_info.GetOverscanInsetsInPixel(),
692 display_info.ui_scale()); 699 display_info.ui_scale());
693 SetDisplayPropertiesOnHostWindow(root, display); 700 SetDisplayPropertiesOnHostWindow(root, display);
694 } 701 }
695 702
696 void DisplayController::OnDisplayAdded(const gfx::Display& display) { 703 void DisplayController::OnDisplayAdded(const gfx::Display& display) {
697 if (limiter_.get()) 704 if (limiter_.get())
698 limiter_->SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs); 705 limiter_->SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs);
699 706
700 if (primary_root_window_for_replace_) { 707 if (primary_root_window_for_replace_) {
701 DCHECK(root_windows_.empty()); 708 DCHECK(root_windows_.empty());
702 primary_display_id = display.id(); 709 primary_display_id = display.id();
703 root_windows_[display.id()] = primary_root_window_for_replace_; 710 root_windows_[display.id()] = primary_root_window_for_replace_;
704 primary_root_window_for_replace_->SetProperty( 711 primary_root_window_for_replace_->SetProperty(
705 internal::kDisplayIdKey, display.id()); 712 internal::kDisplayIdKey, display.id());
706 primary_root_window_for_replace_ = NULL; 713 primary_root_window_for_replace_ = NULL;
707 UpdateDisplayBoundsForLayout(); 714 UpdateDisplayBoundsForLayout();
708 const internal::DisplayInfo& display_info = 715 const internal::DisplayInfo& display_info =
709 GetDisplayManager()->GetDisplayInfo(display); 716 GetDisplayManager()->GetDisplayInfo(display.id());
710 root_windows_[display.id()]->SetHostBoundsAndInsetsAndRootWindowScale( 717 root_windows_[display.id()]->SetHostBoundsAndInsetsAndRootWindowScale(
711 display_info.bounds_in_pixel(), 718 display_info.bounds_in_pixel(),
712 display_info.GetOverscanInsetsInPixel(), 719 display_info.GetOverscanInsetsInPixel(),
713 display_info.ui_scale()); 720 display_info.ui_scale());
714 } else { 721 } else {
715 DCHECK(!root_windows_.empty()); 722 DCHECK(!root_windows_.empty());
716 aura::RootWindow* root = AddRootWindowForDisplay(display); 723 aura::RootWindow* root = AddRootWindowForDisplay(display);
717 UpdateDisplayBoundsForLayout(); 724 UpdateDisplayBoundsForLayout();
718 if (desired_primary_display_id_ == display.id()) 725 if (desired_primary_display_id_ == display.id())
719 SetPrimaryDisplay(display); 726 SetPrimaryDisplay(display);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
764 // Delete most of root window related objects, but don't delete 771 // Delete most of root window related objects, but don't delete
765 // root window itself yet because the stack may be using it. 772 // root window itself yet because the stack may be using it.
766 controller->Shutdown(); 773 controller->Shutdown();
767 MessageLoop::current()->DeleteSoon(FROM_HERE, controller); 774 MessageLoop::current()->DeleteSoon(FROM_HERE, controller);
768 } 775 }
769 776
770 aura::RootWindow* DisplayController::CreateRootWindowForDisplay( 777 aura::RootWindow* DisplayController::CreateRootWindowForDisplay(
771 const gfx::Display& display) { 778 const gfx::Display& display) {
772 static int root_window_count = 0; 779 static int root_window_count = 0;
773 const internal::DisplayInfo& display_info = 780 const internal::DisplayInfo& display_info =
774 GetDisplayManager()->GetDisplayInfo(display); 781 GetDisplayManager()->GetDisplayInfo(display.id());
775 const gfx::Rect& bounds_in_pixel = display_info.bounds_in_pixel(); 782 const gfx::Rect& bounds_in_pixel = display_info.bounds_in_pixel();
776 aura::RootWindow::CreateParams params(bounds_in_pixel); 783 aura::RootWindow::CreateParams params(bounds_in_pixel);
777 params.host = Shell::GetInstance()->root_window_host_factory()-> 784 params.host = Shell::GetInstance()->root_window_host_factory()->
778 CreateRootWindowHost(bounds_in_pixel); 785 CreateRootWindowHost(bounds_in_pixel);
779 params.initial_insets = display_info.GetOverscanInsetsInPixel(); 786 params.initial_insets = display_info.GetOverscanInsetsInPixel();
780 params.initial_root_window_scale = display_info.ui_scale(); 787 params.initial_root_window_scale = display_info.ui_scale();
781 aura::RootWindow* root_window = new aura::RootWindow(params); 788 aura::RootWindow* root_window = new aura::RootWindow(params);
782 root_window->SetName( 789 root_window->SetName(
783 base::StringPrintf("RootWindow-%d", root_window_count++)); 790 base::StringPrintf("RootWindow-%d", root_window_count++));
784 791
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
873 } 880 }
874 881
875 void DisplayController::OnFadeOutForSwapDisplayFinished() { 882 void DisplayController::OnFadeOutForSwapDisplayFinished() {
876 #if defined(OS_CHROMEOS) 883 #if defined(OS_CHROMEOS)
877 SetPrimaryDisplay(ScreenAsh::GetSecondaryDisplay()); 884 SetPrimaryDisplay(ScreenAsh::GetSecondaryDisplay());
878 Shell::GetInstance()->output_configurator_animation()->StartFadeInAnimation(); 885 Shell::GetInstance()->output_configurator_animation()->StartFadeInAnimation();
879 #endif 886 #endif
880 } 887 }
881 888
882 } // namespace ash 889 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698