1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/display/cros_display_config.h"
7 #include "ash/constants/ash_features.h"
8 #include "ash/display/display_alignment_controller.h"
9 #include "ash/display/display_highlight_controller.h"
10 #include "ash/display/screen_orientation_controller.h"
11 #include "ash/display/screen_orientation_controller_test_api.h"
12 #include "ash/display/touch_calibrator_controller.h"
13 #include "ash/shell.h"
14 #include "ash/test/ash_test_base.h"
15 #include "ash/touch/ash_touch_transform_controller.h"
16 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
17 #include "base/command_line.h"
18 #include "base/containers/contains.h"
19 #include "base/functional/bind.h"
20 #include "base/memory/raw_ptr.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/test/scoped_feature_list.h"
23 #include "chromeos/crosapi/mojom/cros_display_config.mojom.h"
24 #include "mojo/public/cpp/bindings/associated_receiver.h"
25 #include "mojo/public/cpp/bindings/associated_remote.h"
26 #include "ui/display/display_switches.h"
27 #include "ui/display/manager/display_manager.h"
28 #include "ui/display/manager/test/touch_transform_controller_test_api.h"
29 #include "ui/display/manager/touch_transform_setter.h"
30 #include "ui/display/test/display_manager_test_api.h"
31 #include "ui/events/devices/device_data_manager_test_api.h"
32 #include "ui/events/devices/touch_device_transform.h"
33 #include "ui/events/devices/touchscreen_device.h"
39 void SetResult(crosapi::mojom::DisplayConfigResult* result_ptr,
40 base::OnceClosure callback,
41 crosapi::mojom::DisplayConfigResult result) {
43 std::move(callback).Run();
46 void InitExternalTouchDevices(int64_t display_id) {
47 ui::TouchscreenDevice touchdevice(123, ui::InputDeviceType::INPUT_DEVICE_USB,
48 std::string("test external touch device"),
49 gfx::Size(1000, 1000), 1);
50 ui::DeviceDataManagerTestApi().SetTouchscreenDevices({touchdevice});
52 std::vector<ui::TouchDeviceTransform> transforms;
53 ui::TouchDeviceTransform touch_device_transform;
54 touch_device_transform.display_id = display_id;
55 touch_device_transform.device_id = touchdevice.id;
56 transforms.push_back(touch_device_transform);
57 display::test::TouchTransformControllerTestApi(
58 ash::Shell::Get()->touch_transformer_controller())
59 .touch_transform_setter()
60 ->ConfigureTouchDevices(transforms);
63 class TestObserver : public crosapi::mojom::CrosDisplayConfigObserver {
65 TestObserver() = default;
67 TestObserver(const TestObserver&) = delete;
68 TestObserver& operator=(const TestObserver&) = delete;
70 // crosapi::mojom::CrosDisplayConfigObserver:
71 void OnDisplayConfigChanged() override { display_changes_++; }
73 int display_changes() const { return display_changes_; }
74 void reset_display_changes() { display_changes_ = 0; }
77 int display_changes_ = 0;
82 class CrosDisplayConfigTest : public AshTestBase {
84 CrosDisplayConfigTest() = default;
86 CrosDisplayConfigTest(const CrosDisplayConfigTest&) = delete;
87 CrosDisplayConfigTest& operator=(const CrosDisplayConfigTest&) = delete;
89 ~CrosDisplayConfigTest() override = default;
91 void SetUp() override {
92 scoped_feature_list_.InitAndEnableFeature(features::kDisplayAlignAssist);
94 base::CommandLine::ForCurrentProcess()->AppendSwitch(
95 switches::kUseFirstDisplayAsInternal);
97 CHECK(display::Screen::GetScreen());
98 cros_display_config_ = Shell::Get()->cros_display_config();
101 crosapi::mojom::DisplayLayoutInfoPtr GetDisplayLayoutInfo() {
102 crosapi::mojom::DisplayLayoutInfoPtr display_layout_info;
103 base::RunLoop run_loop;
104 cros_display_config_->GetDisplayLayoutInfo(base::BindOnce(
105 [](crosapi::mojom::DisplayLayoutInfoPtr* result_ptr,
106 base::OnceClosure callback,
107 crosapi::mojom::DisplayLayoutInfoPtr result) {
108 *result_ptr = std::move(result);
109 std::move(callback).Run();
111 &display_layout_info, run_loop.QuitClosure()));
113 return display_layout_info;
116 crosapi::mojom::DisplayConfigResult SetDisplayLayoutInfo(
117 crosapi::mojom::DisplayLayoutInfoPtr display_layout_info) {
118 crosapi::mojom::DisplayConfigResult result;
119 base::RunLoop run_loop;
120 cros_display_config_->SetDisplayLayoutInfo(
121 std::move(display_layout_info),
122 base::BindOnce(&SetResult, &result, run_loop.QuitClosure()));
127 std::vector<crosapi::mojom::DisplayUnitInfoPtr> GetDisplayUnitInfoList() {
128 std::vector<crosapi::mojom::DisplayUnitInfoPtr> display_info_list;
129 base::RunLoop run_loop;
130 cros_display_config_->GetDisplayUnitInfoList(
131 /*single_unified=*/false,
133 [](std::vector<crosapi::mojom::DisplayUnitInfoPtr>* result_ptr,
134 base::OnceClosure callback,
135 std::vector<crosapi::mojom::DisplayUnitInfoPtr> result) {
136 *result_ptr = std::move(result);
137 std::move(callback).Run();
139 &display_info_list, run_loop.QuitClosure()));
141 return display_info_list;
144 crosapi::mojom::DisplayConfigResult SetDisplayProperties(
145 const std::string& id,
146 crosapi::mojom::DisplayConfigPropertiesPtr properties) {
147 crosapi::mojom::DisplayConfigResult result;
148 base::RunLoop run_loop;
149 cros_display_config_->SetDisplayProperties(
150 id, std::move(properties), crosapi::mojom::DisplayConfigSource::kUser,
151 base::BindOnce(&SetResult, &result, run_loop.QuitClosure()));
156 bool OverscanCalibration(int64_t id,
157 crosapi::mojom::DisplayConfigOperation op,
158 const absl::optional<gfx::Insets>& delta) {
159 crosapi::mojom::DisplayConfigResult result;
160 base::RunLoop run_loop;
161 cros_display_config()->OverscanCalibration(
162 base::NumberToString(id), op, delta,
163 base::BindOnce(&SetResult, &result, run_loop.QuitClosure()));
165 return result == crosapi::mojom::DisplayConfigResult::kSuccess;
168 bool DisplayExists(int64_t display_id) {
169 const display::Display& display =
170 display_manager()->GetDisplayForId(display_id);
171 return display.id() != display::kInvalidDisplayId;
174 crosapi::mojom::TouchCalibrationPtr GetDefaultCalibration() {
175 auto calibration = crosapi::mojom::TouchCalibration::New();
176 for (int i = 0; i < 4; ++i)
177 calibration->pairs.emplace_back(
178 crosapi::mojom::TouchCalibrationPair::New());
182 bool StartTouchCalibration(const std::string& display_id) {
183 return CallTouchCalibration(
184 display_id, crosapi::mojom::DisplayConfigOperation::kStart, nullptr);
187 bool CompleteCustomTouchCalibration(
188 const std::string& display_id,
189 crosapi::mojom::TouchCalibrationPtr calibration) {
190 return CallTouchCalibration(
191 display_id, crosapi::mojom::DisplayConfigOperation::kComplete,
192 std::move(calibration));
195 bool CallTouchCalibration(const std::string& id,
196 crosapi::mojom::DisplayConfigOperation op,
197 crosapi::mojom::TouchCalibrationPtr calibration) {
198 crosapi::mojom::DisplayConfigResult result;
199 base::RunLoop run_loop;
200 cros_display_config_->TouchCalibration(
201 id, op, std::move(calibration),
202 base::BindOnce(&SetResult, &result, run_loop.QuitClosure()));
204 return result == crosapi::mojom::DisplayConfigResult::kSuccess;
207 bool IsTouchCalibrationActive() {
208 TouchCalibratorController* touch_calibrator =
209 cros_display_config_->touch_calibrator_for_test();
210 return touch_calibrator && touch_calibrator->IsCalibrating();
213 void HighlightDisplay(int64_t id) {
214 cros_display_config_->HighlightDisplay(id);
217 void DragDisplayDelta(int64_t id, int32_t delta_x, int32_t delta_y) {
218 cros_display_config_->DragDisplayDelta(id, delta_x, delta_y);
221 bool PreviewIndicatorsExist() {
223 ->display_alignment_controller()
224 ->GetActiveIndicatorsForTesting()
228 CrosDisplayConfig* cros_display_config() { return cros_display_config_; }
231 raw_ptr<CrosDisplayConfig, DanglingUntriaged | ExperimentalAsh>
232 cros_display_config_ = nullptr;
234 base::test::ScopedFeatureList scoped_feature_list_;
237 TEST_F(CrosDisplayConfigTest, OnDisplayConfigChanged) {
238 TestObserver observer;
239 mojo::AssociatedRemote<crosapi::mojom::CrosDisplayConfigObserver>
241 mojo::AssociatedReceiver<crosapi::mojom::CrosDisplayConfigObserver> receiver(
242 &observer, observer_remote.BindNewEndpointAndPassDedicatedReceiver());
243 cros_display_config()->AddObserver(observer_remote.Unbind());
244 base::RunLoop().RunUntilIdle();
246 // Adding one display should trigger one notification.
247 UpdateDisplay("500x400");
248 base::RunLoop().RunUntilIdle();
249 EXPECT_EQ(1, observer.display_changes());
250 observer.reset_display_changes();
252 // Adding two displays should trigger two notification.
253 UpdateDisplay("500x400,500x400");
254 base::RunLoop().RunUntilIdle();
255 EXPECT_EQ(2, observer.display_changes());
258 TEST_F(CrosDisplayConfigTest, GetDisplayLayoutInfo) {
259 UpdateDisplay("500x400,500x400,500x400");
260 std::vector<display::Display> displays =
261 display::Screen::GetScreen()->GetAllDisplays();
262 ASSERT_EQ(3u, displays.size());
264 crosapi::mojom::DisplayLayoutInfoPtr display_layout_info =
265 GetDisplayLayoutInfo();
267 ASSERT_TRUE(display_layout_info);
268 const std::vector<crosapi::mojom::DisplayLayoutPtr>& layouts =
269 *display_layout_info->layouts;
270 ASSERT_EQ(2u, layouts.size());
272 EXPECT_EQ(base::NumberToString(displays[1].id()), layouts[0]->id);
273 EXPECT_EQ(base::NumberToString(displays[0].id()), layouts[0]->parent_id);
274 EXPECT_EQ(crosapi::mojom::DisplayLayoutPosition::kRight,
275 layouts[0]->position);
276 EXPECT_EQ(0, layouts[0]->offset);
278 EXPECT_EQ(base::NumberToString(displays[2].id()), layouts[1]->id);
279 EXPECT_EQ(layouts[0]->id, layouts[1]->parent_id);
280 EXPECT_EQ(crosapi::mojom::DisplayLayoutPosition::kRight,
281 layouts[1]->position);
282 EXPECT_EQ(0, layouts[1]->offset);
285 TEST_F(CrosDisplayConfigTest, FailToSetLayoutUnifiedWithOneDisplay) {
286 UpdateDisplay("500x400");
287 EXPECT_FALSE(display_manager()->IsInUnifiedMode());
289 // Enable unified desktop and expect to fail due to not enough connected
291 auto properties = crosapi::mojom::DisplayLayoutInfo::New();
292 properties->layout_mode = crosapi::mojom::DisplayLayoutMode::kUnified;
293 crosapi::mojom::DisplayConfigResult result =
294 SetDisplayLayoutInfo(std::move(properties));
295 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSingleDisplayError, result);
296 EXPECT_FALSE(display_manager()->IsInUnifiedMode());
299 TEST_F(CrosDisplayConfigTest, SetLayoutUnified) {
300 UpdateDisplay("500x400,500x400");
301 EXPECT_FALSE(display_manager()->IsInUnifiedMode());
303 // Enable unified desktop. Enables unified mode.
304 cros_display_config()->SetUnifiedDesktopEnabled(true);
305 base::RunLoop().RunUntilIdle();
306 EXPECT_TRUE(display_manager()->IsInUnifiedMode());
308 // Disable unified mode.
309 auto properties = crosapi::mojom::DisplayLayoutInfo::New();
310 properties->layout_mode = crosapi::mojom::DisplayLayoutMode::kNormal;
311 crosapi::mojom::DisplayConfigResult result =
312 SetDisplayLayoutInfo(std::move(properties));
313 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
314 EXPECT_FALSE(display_manager()->IsInUnifiedMode());
316 // Enable unified mode.
317 properties = crosapi::mojom::DisplayLayoutInfo::New();
318 properties->layout_mode = crosapi::mojom::DisplayLayoutMode::kUnified;
319 result = SetDisplayLayoutInfo(std::move(properties));
320 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
321 EXPECT_TRUE(display_manager()->IsInUnifiedMode());
323 // Restore extended mode.
324 cros_display_config()->SetUnifiedDesktopEnabled(false);
325 EXPECT_FALSE(display_manager()->IsInUnifiedMode());
328 TEST_F(CrosDisplayConfigTest, FailToSetLayoutMirroredDefaultWithOneDisplay) {
329 UpdateDisplay("500x400");
330 EXPECT_FALSE(display_manager()->IsInMirrorMode());
332 // Enable default mirror mode and expect to fail due to not enough connected
334 auto properties = crosapi::mojom::DisplayLayoutInfo::New();
335 properties->layout_mode = crosapi::mojom::DisplayLayoutMode::kMirrored;
336 crosapi::mojom::DisplayConfigResult result =
337 SetDisplayLayoutInfo(std::move(properties));
338 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSingleDisplayError, result);
339 EXPECT_FALSE(display_manager()->IsInMirrorMode());
341 display::DisplayIdList id_list =
342 display_manager()->GetMirroringDestinationDisplayIdList();
343 ASSERT_TRUE(id_list.empty());
346 TEST_F(CrosDisplayConfigTest, SetLayoutMirroredDefault) {
347 UpdateDisplay("500x400,500x400,500x400");
349 auto properties = crosapi::mojom::DisplayLayoutInfo::New();
350 properties->layout_mode = crosapi::mojom::DisplayLayoutMode::kMirrored;
351 crosapi::mojom::DisplayConfigResult result =
352 SetDisplayLayoutInfo(std::move(properties));
353 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
354 EXPECT_TRUE(display_manager()->IsInMirrorMode());
355 display::DisplayIdList id_list =
356 display_manager()->GetMirroringDestinationDisplayIdList();
357 ASSERT_EQ(2u, id_list.size());
359 properties = crosapi::mojom::DisplayLayoutInfo::New();
360 properties->layout_mode = crosapi::mojom::DisplayLayoutMode::kNormal;
361 result = SetDisplayLayoutInfo(std::move(properties));
362 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
363 EXPECT_FALSE(display_manager()->IsInMirrorMode());
366 TEST_F(CrosDisplayConfigTest, FailToSetLayoutMirroredMixedWithOneDisplay) {
367 UpdateDisplay("500x400");
368 EXPECT_FALSE(display_manager()->IsInMirrorMode());
370 std::vector<display::Display> displays =
371 display::Screen::GetScreen()->GetAllDisplays();
372 ASSERT_EQ(1u, displays.size());
374 // Enable mixed mirror mode and expect to fail due to not enough connected
376 auto properties = crosapi::mojom::DisplayLayoutInfo::New();
377 properties->layout_mode = crosapi::mojom::DisplayLayoutMode::kMirrored;
378 properties->mirror_source_id = base::NumberToString(displays[0].id());
379 properties->mirror_destination_ids =
380 absl::make_optional<std::vector<std::string>>();
382 crosapi::mojom::DisplayConfigResult result =
383 SetDisplayLayoutInfo(std::move(properties));
384 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSingleDisplayError, result);
385 EXPECT_FALSE(display_manager()->IsInMirrorMode());
387 display::DisplayIdList id_list =
388 display_manager()->GetMirroringDestinationDisplayIdList();
389 ASSERT_TRUE(id_list.empty());
392 TEST_F(CrosDisplayConfigTest, SetLayoutMirroredMixed) {
393 UpdateDisplay("500x400,500x400,500x400,500x400");
395 std::vector<display::Display> displays =
396 display::Screen::GetScreen()->GetAllDisplays();
397 ASSERT_EQ(4u, displays.size());
399 auto properties = crosapi::mojom::DisplayLayoutInfo::New();
400 properties->layout_mode = crosapi::mojom::DisplayLayoutMode::kMirrored;
401 properties->mirror_source_id = base::NumberToString(displays[0].id());
402 properties->mirror_destination_ids =
403 absl::make_optional<std::vector<std::string>>(
404 {base::NumberToString(displays[1].id()),
405 base::NumberToString(displays[3].id())});
406 crosapi::mojom::DisplayConfigResult result =
407 SetDisplayLayoutInfo(std::move(properties));
408 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
409 EXPECT_TRUE(display_manager()->IsInMirrorMode());
410 display::DisplayIdList id_list =
411 display_manager()->GetMirroringDestinationDisplayIdList();
412 ASSERT_EQ(2u, id_list.size());
413 EXPECT_TRUE(base::Contains(id_list, displays[1].id()));
414 EXPECT_TRUE(base::Contains(id_list, displays[3].id()));
417 TEST_F(CrosDisplayConfigTest, GetDisplayUnitInfoListBasic) {
418 UpdateDisplay("500x600,400x520");
419 std::vector<crosapi::mojom::DisplayUnitInfoPtr> result =
420 GetDisplayUnitInfoList();
421 ASSERT_EQ(2u, result.size());
424 ASSERT_TRUE(base::StringToInt64(result[0]->id, &display_id));
425 ASSERT_TRUE(DisplayExists(display_id));
426 const crosapi::mojom::DisplayUnitInfo& info_0 = *result[0];
427 EXPECT_TRUE(info_0.is_primary);
428 EXPECT_TRUE(info_0.is_internal);
429 EXPECT_TRUE(info_0.is_enabled);
430 EXPECT_FALSE(info_0.is_auto_rotation_allowed);
431 EXPECT_FALSE(info_0.has_touch_support);
432 EXPECT_FALSE(info_0.has_accelerometer_support);
433 EXPECT_EQ(96, info_0.dpi_x);
434 EXPECT_EQ(96, info_0.dpi_y);
435 EXPECT_EQ(crosapi::mojom::DisplayRotationOptions::kZeroDegrees,
436 info_0.rotation_options);
437 EXPECT_EQ(gfx::Rect(0, 0, 500, 600), info_0.bounds);
438 EXPECT_EQ(gfx::Insets(), info_0.overscan);
440 ASSERT_TRUE(base::StringToInt64(result[1]->id, &display_id));
441 ASSERT_TRUE(DisplayExists(display_id));
442 const crosapi::mojom::DisplayUnitInfo& info_1 = *result[1];
443 EXPECT_EQ(display_manager()->GetDisplayNameForId(display_id), info_1.name);
444 // Second display is left of the primary display whose width 500.
445 EXPECT_EQ(gfx::Rect(500, 0, 400, 520), info_1.bounds);
446 EXPECT_EQ(gfx::Insets(), info_1.overscan);
447 EXPECT_EQ(crosapi::mojom::DisplayRotationOptions::kZeroDegrees,
448 info_1.rotation_options);
449 EXPECT_FALSE(info_1.is_primary);
450 EXPECT_FALSE(info_1.is_internal);
451 EXPECT_TRUE(info_1.is_enabled);
452 EXPECT_EQ(96, info_1.dpi_x);
453 EXPECT_EQ(96, info_1.dpi_y);
456 TEST_F(CrosDisplayConfigTest, GetDisplayUnitInfoListZoomFactor) {
457 UpdateDisplay("1024x512,1024x512");
458 std::vector<crosapi::mojom::DisplayUnitInfoPtr> result =
459 GetDisplayUnitInfoList();
460 ASSERT_EQ(2u, result.size());
462 const crosapi::mojom::DisplayUnitInfo& info_0 = *result[0];
463 EXPECT_EQ(1.0, info_0.display_zoom_factor);
464 const std::vector<double>& zoom_factors =
465 info_0.available_display_zoom_factors;
466 EXPECT_EQ(9u, zoom_factors.size());
467 EXPECT_FLOAT_EQ(0.90f, zoom_factors[0]);
468 EXPECT_FLOAT_EQ(0.95f, zoom_factors[1]);
469 EXPECT_FLOAT_EQ(1.f, zoom_factors[2]);
470 EXPECT_FLOAT_EQ(1.05f, zoom_factors[3]);
471 EXPECT_FLOAT_EQ(1.10f, zoom_factors[4]);
472 EXPECT_FLOAT_EQ(1.15f, zoom_factors[5]);
473 EXPECT_FLOAT_EQ(1.20f, zoom_factors[6]);
474 EXPECT_FLOAT_EQ(1.25f, zoom_factors[7]);
475 EXPECT_FLOAT_EQ(1.30f, zoom_factors[8]);
478 TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesPrimary) {
479 UpdateDisplay("1200x600,600x1000");
480 int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
481 int64_t secondary_id = display::test::DisplayManagerTestApi(display_manager())
482 .GetSecondaryDisplay()
484 ASSERT_NE(primary_id, secondary_id);
486 auto properties = crosapi::mojom::DisplayConfigProperties::New();
487 properties->set_primary = true;
488 crosapi::mojom::DisplayConfigResult result = SetDisplayProperties(
489 base::NumberToString(secondary_id), std::move(properties));
490 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
492 // secondary display should now be primary.
493 primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
494 EXPECT_EQ(primary_id, secondary_id);
497 TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesOverscan) {
498 UpdateDisplay("1200x600,600x1000*2");
499 const display::Display& secondary =
500 display::test::DisplayManagerTestApi(display_manager())
501 .GetSecondaryDisplay();
503 auto properties = crosapi::mojom::DisplayConfigProperties::New();
504 properties->overscan = gfx::Insets::TLBR(199, 20, 51, 130);
505 crosapi::mojom::DisplayConfigResult result = SetDisplayProperties(
506 base::NumberToString(secondary.id()), std::move(properties));
507 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
508 EXPECT_EQ(gfx::Rect(1200, 0, 150, 250), secondary.bounds());
509 const gfx::Insets overscan =
510 display_manager()->GetOverscanInsets(secondary.id());
511 EXPECT_EQ(199, overscan.top());
512 EXPECT_EQ(20, overscan.left());
513 EXPECT_EQ(51, overscan.bottom());
514 EXPECT_EQ(130, overscan.right());
517 TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesRotation) {
518 UpdateDisplay("1200x600,600x1000*2");
519 const display::Display& secondary =
520 display::test::DisplayManagerTestApi(display_manager())
521 .GetSecondaryDisplay();
523 crosapi::mojom::DisplayConfigResult result;
525 auto properties = crosapi::mojom::DisplayConfigProperties::New();
526 properties->rotation = crosapi::mojom::DisplayRotation::New(
527 crosapi::mojom::DisplayRotationOptions::k90Degrees);
528 result = SetDisplayProperties(base::NumberToString(secondary.id()),
529 std::move(properties));
530 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
531 EXPECT_EQ(gfx::Rect(1200, 0, 500, 300), secondary.bounds());
532 EXPECT_EQ(display::Display::ROTATE_90, secondary.rotation());
534 properties = crosapi::mojom::DisplayConfigProperties::New();
535 properties->rotation = crosapi::mojom::DisplayRotation::New(
536 crosapi::mojom::DisplayRotationOptions::k270Degrees);
537 result = SetDisplayProperties(base::NumberToString(secondary.id()),
538 std::move(properties));
539 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
540 EXPECT_EQ(gfx::Rect(1200, 0, 500, 300), secondary.bounds());
541 EXPECT_EQ(display::Display::ROTATE_270, secondary.rotation());
543 // Test setting primary and rotating.
544 properties = crosapi::mojom::DisplayConfigProperties::New();
545 properties->set_primary = true;
546 properties->rotation = crosapi::mojom::DisplayRotation::New(
547 crosapi::mojom::DisplayRotationOptions::k180Degrees);
548 result = SetDisplayProperties(base::NumberToString(secondary.id()),
549 std::move(properties));
550 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
551 const display::Display& primary =
552 display::Screen::GetScreen()->GetPrimaryDisplay();
553 EXPECT_EQ(secondary.id(), primary.id());
554 EXPECT_EQ(gfx::Rect(0, 0, 300, 500), primary.bounds());
555 EXPECT_EQ(display::Display::ROTATE_180, primary.rotation());
558 TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesBoundsOrigin) {
559 UpdateDisplay("1200x600,520x400");
560 const display::Display& secondary =
561 display::test::DisplayManagerTestApi(display_manager())
562 .GetSecondaryDisplay();
564 crosapi::mojom::DisplayConfigResult result;
566 auto properties = crosapi::mojom::DisplayConfigProperties::New();
567 properties->bounds_origin = gfx::Point({-520, 50});
568 result = SetDisplayProperties(base::NumberToString(secondary.id()),
569 std::move(properties));
570 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
571 EXPECT_EQ(gfx::Rect(-520, 50, 520, 400), secondary.bounds());
573 properties = crosapi::mojom::DisplayConfigProperties::New();
574 properties->bounds_origin = gfx::Point({1200, 100});
575 result = SetDisplayProperties(base::NumberToString(secondary.id()),
576 std::move(properties));
577 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
578 EXPECT_EQ(gfx::Rect(1200, 100, 520, 400), secondary.bounds());
580 properties = crosapi::mojom::DisplayConfigProperties::New();
581 properties->bounds_origin = gfx::Point({1100, -400});
582 result = SetDisplayProperties(base::NumberToString(secondary.id()),
583 std::move(properties));
584 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
585 EXPECT_EQ(gfx::Rect(1100, -400, 520, 400), secondary.bounds());
587 properties = crosapi::mojom::DisplayConfigProperties::New();
588 properties->bounds_origin = gfx::Point({-350, 600});
589 result = SetDisplayProperties(base::NumberToString(secondary.id()),
590 std::move(properties));
591 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
592 EXPECT_EQ(gfx::Rect(-350, 600, 520, 400), secondary.bounds());
595 TEST_F(CrosDisplayConfigTest, SetDisplayPropertiesDisplayZoomFactor) {
596 static std::string configs[] = {
597 "1200x600, 1600x1000#1600x1000", // landscape
598 "600x1200, 1000x1600#1000x1600", // portrait
600 for (auto config : configs) {
601 SCOPED_TRACE(config);
602 UpdateDisplay(config);
603 display::DisplayIdList display_id_list =
604 display_manager()->GetConnectedDisplayIdList();
606 const float zoom_factor_1 = 1.23f;
607 const float zoom_factor_2 = 2.34f;
609 display_manager()->UpdateZoomFactor(display_id_list[0], zoom_factor_2);
610 display_manager()->UpdateZoomFactor(display_id_list[1], zoom_factor_1);
614 display_manager()->GetDisplayInfo(display_id_list[0]).zoom_factor());
617 display_manager()->GetDisplayInfo(display_id_list[1]).zoom_factor());
619 // Set zoom factor for display 0, should not affect display 1.
620 auto properties = crosapi::mojom::DisplayConfigProperties::New();
621 properties->display_zoom_factor = zoom_factor_1;
622 crosapi::mojom::DisplayConfigResult result = SetDisplayProperties(
623 base::NumberToString(display_id_list[0]), std::move(properties));
624 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
627 display_manager()->GetDisplayInfo(display_id_list[0]).zoom_factor());
630 display_manager()->GetDisplayInfo(display_id_list[1]).zoom_factor());
632 // Set zoom factor for display 1.
633 properties = crosapi::mojom::DisplayConfigProperties::New();
634 properties->display_zoom_factor = zoom_factor_2;
635 result = SetDisplayProperties(base::NumberToString(display_id_list[1]),
636 std::move(properties));
637 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
640 display_manager()->GetDisplayInfo(display_id_list[0]).zoom_factor());
643 display_manager()->GetDisplayInfo(display_id_list[1]).zoom_factor());
645 // Invalid zoom factor should fail.
646 const float invalid_zoom_factor = 0.01f;
647 properties = crosapi::mojom::DisplayConfigProperties::New();
648 properties->display_zoom_factor = invalid_zoom_factor;
649 result = SetDisplayProperties(base::NumberToString(display_id_list[1]),
650 std::move(properties));
652 crosapi::mojom::DisplayConfigResult::kPropertyValueOutOfRangeError,
656 display_manager()->GetDisplayInfo(display_id_list[1]).zoom_factor());
660 TEST_F(CrosDisplayConfigTest, SetDisplayMode) {
661 UpdateDisplay("1024x512,1024x512");
662 std::vector<crosapi::mojom::DisplayUnitInfoPtr> result =
663 GetDisplayUnitInfoList();
664 ASSERT_EQ(2u, result.size());
665 // Internal display has just one mode.
666 EXPECT_EQ(0, result[0]->selected_display_mode_index);
667 ASSERT_EQ(1u, result[0]->available_display_modes.size());
669 auto properties = crosapi::mojom::DisplayConfigProperties::New();
670 auto display_mode = result[0]->available_display_modes[0].Clone();
671 properties->display_mode = std::move(display_mode);
672 ASSERT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess,
673 SetDisplayProperties(result[0]->id, std::move(properties)));
675 result = GetDisplayUnitInfoList();
676 ASSERT_EQ(2u, result.size());
677 EXPECT_EQ(0, result[0]->selected_display_mode_index);
680 TEST_F(CrosDisplayConfigTest, OverscanCalibration) {
681 UpdateDisplay("1200x600");
682 int64_t id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
683 ASSERT_NE(display::kInvalidDisplayId, id);
685 // Test that kAdjust succeeds after kComplete call.
686 EXPECT_TRUE(OverscanCalibration(
687 id, crosapi::mojom::DisplayConfigOperation::kStart, absl::nullopt));
688 EXPECT_EQ(gfx::Insets(), display_manager()->GetOverscanInsets(id));
690 gfx::Insets insets(10);
691 EXPECT_TRUE(OverscanCalibration(
692 id, crosapi::mojom::DisplayConfigOperation::kAdjust, insets));
693 // Adjust has no effect until Complete.
694 EXPECT_EQ(gfx::Insets(), display_manager()->GetOverscanInsets(id));
696 EXPECT_TRUE(OverscanCalibration(
697 id, crosapi::mojom::DisplayConfigOperation::kComplete, absl::nullopt));
698 gfx::Insets overscan = display_manager()->GetOverscanInsets(id);
699 EXPECT_EQ(insets, overscan)
700 << "Overscan: " << overscan.ToString() << " != " << insets.ToString();
702 // Test that kReset clears restores previous insets.
704 // Start clears any overscan values.
705 EXPECT_TRUE(OverscanCalibration(
706 id, crosapi::mojom::DisplayConfigOperation::kStart, absl::nullopt));
707 EXPECT_EQ(gfx::Insets(), display_manager()->GetOverscanInsets(id));
709 // Reset + Complete restores previously set insets.
710 EXPECT_TRUE(OverscanCalibration(
711 id, crosapi::mojom::DisplayConfigOperation::kReset, absl::nullopt));
712 EXPECT_EQ(gfx::Insets(), display_manager()->GetOverscanInsets(id));
713 EXPECT_TRUE(OverscanCalibration(
714 id, crosapi::mojom::DisplayConfigOperation::kComplete, absl::nullopt));
715 EXPECT_EQ(insets, display_manager()->GetOverscanInsets(id));
717 // Additional complete call should fail.
718 EXPECT_FALSE(OverscanCalibration(
719 id, crosapi::mojom::DisplayConfigOperation::kComplete, absl::nullopt));
722 TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationInternal) {
723 UpdateDisplay("1200x600,600x1000*2");
724 const int64_t internal_display_id =
725 display::test::DisplayManagerTestApi(display_manager())
726 .SetFirstDisplayAsInternalDisplay();
728 InitExternalTouchDevices(internal_display_id);
731 StartTouchCalibration(base::NumberToString(internal_display_id)));
732 EXPECT_FALSE(IsTouchCalibrationActive());
735 TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationWithoutStart) {
736 UpdateDisplay("1200x600,600x1000*2");
737 EXPECT_FALSE(IsTouchCalibrationActive());
740 TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationNonTouchDisplay) {
741 UpdateDisplay("1200x600,600x1000*2");
743 const int64_t internal_display_id =
744 display::test::DisplayManagerTestApi(display_manager())
745 .SetFirstDisplayAsInternalDisplay();
747 display::DisplayIdList display_id_list =
748 display_manager()->GetConnectedDisplayIdList();
750 // Pick the non internal display Id.
751 const int64_t display_id = display_id_list[0] == internal_display_id
753 : display_id_list[0];
755 ui::DeviceDataManagerTestApi().SetTouchscreenDevices({});
756 std::string id = base::NumberToString(display_id);
758 // Since no external touch devices are present, the calibration should fail.
759 EXPECT_FALSE(StartTouchCalibration(id));
761 // If an external touch device is present, the calibration should proceed.
762 InitExternalTouchDevices(display_id);
763 EXPECT_TRUE(StartTouchCalibration(id));
764 EXPECT_TRUE(IsTouchCalibrationActive());
767 TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationInvalidPoints) {
768 UpdateDisplay("1200x600,600x1000*2");
770 const int64_t internal_display_id =
771 display::test::DisplayManagerTestApi(display_manager())
772 .SetFirstDisplayAsInternalDisplay();
774 display::DisplayIdList display_id_list =
775 display_manager()->GetConnectedDisplayIdList();
777 // Pick the non internal display Id.
778 const int64_t display_id = display_id_list[0] == internal_display_id
780 : display_id_list[0];
782 InitExternalTouchDevices(display_id);
784 std::string id = base::NumberToString(display_id);
786 EXPECT_TRUE(StartTouchCalibration(id));
787 crosapi::mojom::TouchCalibrationPtr calibration = GetDefaultCalibration();
788 calibration->pairs[0]->display_point.set_x(-1);
789 EXPECT_FALSE(CompleteCustomTouchCalibration(id, std::move(calibration)));
791 EXPECT_TRUE(StartTouchCalibration(id));
792 calibration = GetDefaultCalibration();
793 calibration->bounds.set_width(1);
794 calibration->pairs[0]->display_point.set_x(2);
795 EXPECT_FALSE(CompleteCustomTouchCalibration(id, std::move(calibration)));
798 TEST_F(CrosDisplayConfigTest, CustomTouchCalibrationSuccess) {
799 UpdateDisplay("1200x600,600x1000*2");
801 const int64_t internal_display_id =
802 display::test::DisplayManagerTestApi(display_manager())
803 .SetFirstDisplayAsInternalDisplay();
805 display::DisplayIdList display_id_list =
806 display_manager()->GetConnectedDisplayIdList();
808 // Pick the non internal display Id.
809 const int64_t display_id = display_id_list[0] == internal_display_id
811 : display_id_list[0];
813 InitExternalTouchDevices(display_id);
815 std::string id = base::NumberToString(display_id);
817 EXPECT_TRUE(StartTouchCalibration(id));
818 EXPECT_TRUE(IsTouchCalibrationActive());
819 crosapi::mojom::TouchCalibrationPtr calibration = GetDefaultCalibration();
820 EXPECT_TRUE(CompleteCustomTouchCalibration(id, std::move(calibration)));
823 TEST_F(CrosDisplayConfigTest, TabletModeAutoRotationInternalOnly) {
824 UpdateDisplay("500x600,400x520");
826 auto* screen_orientation_controller =
827 Shell::Get()->screen_orientation_controller();
828 EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
830 TabletModeControllerTestApi tablet_mode_controller_test_api;
831 ScreenOrientationControllerTestApi screen_orientation_controller_test_api(
832 screen_orientation_controller);
833 tablet_mode_controller_test_api.EnterTabletMode();
834 EXPECT_TRUE(tablet_mode_controller_test_api.IsInPhysicalTabletState());
835 EXPECT_TRUE(screen_orientation_controller_test_api.IsAutoRotationAllowed());
836 EXPECT_TRUE(tablet_mode_controller_test_api.IsTabletModeStarted());
838 std::vector<crosapi::mojom::DisplayUnitInfoPtr> result =
839 GetDisplayUnitInfoList();
840 ASSERT_EQ(2u, result.size());
843 ASSERT_TRUE(base::StringToInt64(result[0]->id, &display_id));
844 ASSERT_TRUE(DisplayExists(display_id));
845 const crosapi::mojom::DisplayUnitInfo& info_0 = *result[0];
846 EXPECT_TRUE(info_0.is_internal);
847 EXPECT_TRUE(info_0.is_auto_rotation_allowed);
849 ASSERT_TRUE(base::StringToInt64(result[1]->id, &display_id));
850 ASSERT_TRUE(DisplayExists(display_id));
851 const crosapi::mojom::DisplayUnitInfo& info_1 = *result[1];
852 EXPECT_FALSE(info_1.is_internal);
853 EXPECT_FALSE(info_1.is_auto_rotation_allowed);
856 TEST_F(CrosDisplayConfigTest, TabletModeAutoRotation) {
857 TestObserver observer;
858 mojo::AssociatedRemote<crosapi::mojom::CrosDisplayConfigObserver>
860 mojo::AssociatedReceiver<crosapi::mojom::CrosDisplayConfigObserver> receiver(
861 &observer, observer_remote.BindNewEndpointAndPassDedicatedReceiver());
862 cros_display_config()->AddObserver(observer_remote.Unbind());
863 base::RunLoop().RunUntilIdle();
865 display::test::DisplayManagerTestApi(display_manager())
866 .SetFirstDisplayAsInternalDisplay();
868 // Setting the rotation to kAutoRotate from outside the physical tablet state
869 // is treated as a request to set the rotation to 0.
870 const display::Display& display =
871 display_manager()->GetPrimaryDisplayCandidate();
872 auto properties = crosapi::mojom::DisplayConfigProperties::New();
873 properties->rotation = crosapi::mojom::DisplayRotation::New(
874 crosapi::mojom::DisplayRotationOptions::kAutoRotate);
875 auto result = SetDisplayProperties(base::NumberToString(display.id()),
876 std::move(properties));
877 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
878 auto* screen_orientation_controller =
879 Shell::Get()->screen_orientation_controller();
880 EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
881 EXPECT_EQ(display::Display::ROTATE_0, display.rotation());
883 TabletModeControllerTestApi tablet_mode_controller_test_api;
884 ScreenOrientationControllerTestApi screen_orientation_controller_test_api(
885 screen_orientation_controller);
886 tablet_mode_controller_test_api.EnterTabletMode();
887 EXPECT_TRUE(tablet_mode_controller_test_api.IsInPhysicalTabletState());
888 EXPECT_TRUE(screen_orientation_controller_test_api.IsAutoRotationAllowed());
889 EXPECT_TRUE(tablet_mode_controller_test_api.IsTabletModeStarted());
891 // Clear out any pending observer calls.
892 base::RunLoop().RunUntilIdle();
893 observer.reset_display_changes();
895 properties = crosapi::mojom::DisplayConfigProperties::New();
896 properties->rotation = crosapi::mojom::DisplayRotation::New(
897 crosapi::mojom::DisplayRotationOptions::k90Degrees);
898 result = SetDisplayProperties(base::NumberToString(display.id()),
899 std::move(properties));
900 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
901 EXPECT_TRUE(screen_orientation_controller->user_rotation_locked());
902 EXPECT_EQ(display::Display::ROTATE_90, display.rotation());
903 // OnDisplayConfigChanged() will be called twice, once as a result of the
904 // user rotation lock change, and another due to the actual display rotation
906 base::RunLoop().RunUntilIdle();
907 EXPECT_EQ(2, observer.display_changes());
909 // Hooking up an external mouse device, should exit UI tablet mode, but the
910 // device is still in a tablet physical state, the API should still be valid
912 tablet_mode_controller_test_api.AttachExternalMouse();
913 EXPECT_TRUE(tablet_mode_controller_test_api.IsInPhysicalTabletState());
914 EXPECT_TRUE(screen_orientation_controller_test_api.IsAutoRotationAllowed());
915 EXPECT_FALSE(tablet_mode_controller_test_api.IsTabletModeStarted());
917 // Clear out any pending observer calls.
918 base::RunLoop().RunUntilIdle();
919 observer.reset_display_changes();
921 properties = crosapi::mojom::DisplayConfigProperties::New();
922 properties->rotation = crosapi::mojom::DisplayRotation::New(
923 crosapi::mojom::DisplayRotationOptions::kAutoRotate);
924 result = SetDisplayProperties(base::NumberToString(display.id()),
925 std::move(properties));
926 EXPECT_EQ(crosapi::mojom::DisplayConfigResult::kSuccess, result);
927 EXPECT_FALSE(screen_orientation_controller->user_rotation_locked());
928 // Unlocking auto-rotate doesn't actually change the display rotation. It
929 // simply allows it to auto-rotate in response to accelerometer updates.
930 EXPECT_EQ(display::Display::ROTATE_90, display.rotation());
931 // This time, OnDisplayConfigChanged() will be called only once as a result of
932 // the user rotation lock change.
933 base::RunLoop().RunUntilIdle();
934 EXPECT_EQ(1, observer.display_changes());
936 // Once the device is no longer in a physical tablet state, the rotation is
938 tablet_mode_controller_test_api.LeaveTabletMode();
939 EXPECT_FALSE(tablet_mode_controller_test_api.IsInPhysicalTabletState());
940 EXPECT_FALSE(screen_orientation_controller_test_api.IsAutoRotationAllowed());
941 EXPECT_FALSE(tablet_mode_controller_test_api.IsTabletModeStarted());
942 EXPECT_EQ(display::Display::ROTATE_0, display.rotation());
945 TEST_F(CrosDisplayConfigTest, HighlightDisplayValid) {
946 UpdateDisplay("500x400,500x400");
948 const display::Display& display = display_manager()->GetDisplayAt(0);
949 const int64_t display_id = display.id();
951 HighlightDisplay(display_id);
953 views::Widget* widget =
954 Shell::Get()->display_highlight_controller()->GetWidgetForTesting();
955 ASSERT_NE(widget, nullptr);
956 EXPECT_EQ(widget->GetNativeWindow()->GetRootWindow(),
957 Shell::GetRootWindowForDisplayId(display_id));
960 TEST_F(CrosDisplayConfigTest, HighlightDisplayInvalid) {
961 UpdateDisplay("500x400,500x400");
963 HighlightDisplay(display::kInvalidDisplayId);
965 EXPECT_EQ(Shell::Get()->display_highlight_controller()->GetWidgetForTesting(),
969 TEST_F(CrosDisplayConfigTest, DragDisplayDelta) {
970 UpdateDisplay("500x400,500x400");
972 const auto& display = display_manager()->GetDisplayAt(0);
974 EXPECT_FALSE(PreviewIndicatorsExist());
976 DragDisplayDelta(display.id(), 0, 16);
978 EXPECT_TRUE(PreviewIndicatorsExist());