1 // Copyright 2014 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_util.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10 
11 #include "ash/display/extended_mouse_warp_controller.h"
12 #include "ash/display/null_mouse_warp_controller.h"
13 #include "ash/display/unified_mouse_warp_controller.h"
14 #include "ash/host/ash_window_tree_host.h"
15 #include "ash/public/cpp/new_window_delegate.h"
16 #include "ash/public/cpp/notification_utils.h"
17 #include "ash/resources/vector_icons/vector_icons.h"
18 #include "ash/shell.h"
19 #include "ash/strings/grit/ash_strings.h"
20 #include "base/bind.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/system/sys_info.h"
24 #include "ui/aura/env.h"
25 #include "ui/aura/window_tree_host.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/display/display.h"
28 #include "ui/display/manager/display_manager.h"
29 #include "ui/display/manager/managed_display_info.h"
30 #include "ui/gfx/geometry/point.h"
31 #include "ui/gfx/geometry/rect.h"
32 #include "ui/gfx/geometry/size_conversions.h"
33 #include "ui/gfx/paint_vector_icon.h"
34 #include "ui/message_center/message_center.h"
35 #include "ui/message_center/notification_list.h"
36 #include "ui/message_center/public/cpp/notification.h"
37 #include "ui/message_center/public/cpp/notification_delegate.h"
38 #include "ui/wm/core/coordinate_conversion.h"
39 
40 namespace ash {
41 namespace {
42 
43 const char kDisplayErrorNotificationId[] = "chrome://settings/display/error";
44 const char kNotifierDisplayError[] = "ash.display.error";
45 
ConvertPointFromScreenToNative(aura::WindowTreeHost * host,gfx::Point * point)46 void ConvertPointFromScreenToNative(aura::WindowTreeHost* host,
47                                     gfx::Point* point) {
48   ::wm::ConvertPointFromScreen(host->window(), point);
49   host->ConvertDIPToScreenInPixels(point);
50 }
51 
52 }  // namespace
53 
CreateMouseWarpController(display::DisplayManager * manager,aura::Window * drag_source)54 std::unique_ptr<MouseWarpController> CreateMouseWarpController(
55     display::DisplayManager* manager,
56     aura::Window* drag_source) {
57   if (manager->IsInUnifiedMode() && manager->num_connected_displays() >= 2)
58     return std::make_unique<UnifiedMouseWarpController>();
59   // Extra check for |num_connected_displays()| is for SystemDisplayApiTest
60   // that injects MockScreen.
61   if (manager->GetNumDisplays() < 2 || manager->num_connected_displays() < 2)
62     return std::make_unique<NullMouseWarpController>();
63   return std::make_unique<ExtendedMouseWarpController>(drag_source);
64 }
65 
GetNativeEdgeBounds(AshWindowTreeHost * ash_host,const gfx::Rect & bounds_in_screen)66 gfx::Rect GetNativeEdgeBounds(AshWindowTreeHost* ash_host,
67                               const gfx::Rect& bounds_in_screen) {
68   aura::WindowTreeHost* host = ash_host->AsWindowTreeHost();
69   gfx::Rect native_bounds = host->GetBoundsInPixels();
70   native_bounds.Inset(ash_host->GetHostInsets());
71   gfx::Point start_in_native = bounds_in_screen.origin();
72   gfx::Point end_in_native = bounds_in_screen.bottom_right();
73 
74   ConvertPointFromScreenToNative(host, &start_in_native);
75   ConvertPointFromScreenToNative(host, &end_in_native);
76 
77   if (std::abs(start_in_native.x() - end_in_native.x()) <
78       std::abs(start_in_native.y() - end_in_native.y())) {
79     // vertical in native
80     int x = std::abs(native_bounds.x() - start_in_native.x()) <
81                     std::abs(native_bounds.right() - start_in_native.x())
82                 ? native_bounds.x()
83                 : native_bounds.right() - 1;
84     return gfx::Rect(x, std::min(start_in_native.y(), end_in_native.y()), 1,
85                      std::abs(end_in_native.y() - start_in_native.y()));
86   } else {
87     // horizontal in native
88     int y = std::abs(native_bounds.y() - start_in_native.y()) <
89                     std::abs(native_bounds.bottom() - start_in_native.y())
90                 ? native_bounds.y()
91                 : native_bounds.bottom() - 1;
92     return gfx::Rect(std::min(start_in_native.x(), end_in_native.x()), y,
93                      std::abs(end_in_native.x() - start_in_native.x()), 1);
94   }
95 }
96 
97 // Moves the cursor to the point inside the root that is closest to
98 // the point_in_screen, which is outside of the root window.
MoveCursorTo(AshWindowTreeHost * ash_host,const gfx::Point & point_in_screen,bool update_last_location_now)99 void MoveCursorTo(AshWindowTreeHost* ash_host,
100                   const gfx::Point& point_in_screen,
101                   bool update_last_location_now) {
102   aura::WindowTreeHost* host = ash_host->AsWindowTreeHost();
103   gfx::Point point_in_native = point_in_screen;
104   ::wm::ConvertPointFromScreen(host->window(), &point_in_native);
105   host->ConvertDIPToScreenInPixels(&point_in_native);
106 
107   // now fit the point inside the native bounds.
108   gfx::Rect native_bounds = host->GetBoundsInPixels();
109   gfx::Point native_origin = native_bounds.origin();
110   native_bounds.Inset(ash_host->GetHostInsets());
111   // Shrink further so that the mouse doesn't warp on the
112   // edge. The right/bottom needs to be shrink by 2 to subtract
113   // the 1 px from width/height value.
114   native_bounds.Inset(1, 1, 2, 2);
115 
116   // Ensure that |point_in_native| is inside the |native_bounds|.
117   point_in_native.SetToMax(native_bounds.origin());
118   point_in_native.SetToMin(native_bounds.bottom_right());
119 
120   gfx::Point point_in_host = point_in_native;
121 
122   point_in_host.Offset(-native_origin.x(), -native_origin.y());
123   host->MoveCursorToLocationInPixels(point_in_host);
124 
125   if (update_last_location_now) {
126     gfx::Point new_point_in_screen = point_in_native;
127     host->ConvertScreenInPixelsToDIP(&new_point_in_screen);
128     ::wm::ConvertPointToScreen(host->window(), &new_point_in_screen);
129 
130     if (Shell::Get()->display_manager()->IsInUnifiedMode()) {
131       // In unified desktop mode, the mirroring host converts the point to the
132       // unified host's pixel coordinates, so we also need to apply the unified
133       // host transform to get a point in the unified screen coordinates to take
134       // into account any device scale factors or ui scaling.
135       Shell::GetPrimaryRootWindow()->GetHost()->ConvertScreenInPixelsToDIP(
136           &new_point_in_screen);
137     }
138     aura::Env::GetInstance()->SetLastMouseLocation(new_point_in_screen);
139   }
140 }
141 
ShowDisplayErrorNotification(const base::string16 & message,bool allow_feedback)142 void ShowDisplayErrorNotification(const base::string16& message,
143                                   bool allow_feedback) {
144   // Always remove the notification to make sure the notification appears
145   // as a popup in any situation.
146   message_center::MessageCenter::Get()->RemoveNotification(
147       kDisplayErrorNotificationId, false /* by_user */);
148 
149   message_center::RichNotificationData data;
150   if (allow_feedback) {
151     message_center::ButtonInfo send_button(
152         l10n_util::GetStringUTF16(IDS_ASH_DISPLAY_FAILURE_SEND_FEEDBACK));
153     data.buttons.push_back(send_button);
154   }
155 
156   std::unique_ptr<message_center::Notification> notification =
157       CreateSystemNotification(
158           message_center::NOTIFICATION_TYPE_SIMPLE, kDisplayErrorNotificationId,
159           base::string16(),  // title
160           message,
161           base::string16(),  // display_source
162           GURL(),
163           message_center::NotifierId(
164               message_center::NotifierType::SYSTEM_COMPONENT,
165               kNotifierDisplayError),
166           data,
167           base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
168               base::BindRepeating([](base::Optional<int> button_index) {
169                 if (button_index)
170                   NewWindowDelegate::GetInstance()->OpenFeedbackPage();
171               })),
172           kNotificationMonitorWarningIcon,
173           message_center::SystemNotificationWarningLevel::WARNING);
174   message_center::MessageCenter::Get()->AddNotification(
175       std::move(notification));
176 }
177 
ConvertRefreshRateToString16(float refresh_rate)178 base::string16 ConvertRefreshRateToString16(float refresh_rate) {
179   std::string str = base::StringPrintf("%.2f", refresh_rate);
180 
181   // Remove the mantissa for whole numbers.
182   if (EndsWith(str, ".00", base::CompareCase::INSENSITIVE_ASCII))
183     str.erase(str.length() - 3);
184 
185   return base::UTF8ToUTF16(str);
186 }
187 
GetDisplayErrorNotificationMessageForTest()188 base::string16 GetDisplayErrorNotificationMessageForTest() {
189   message_center::NotificationList::Notifications notifications =
190       message_center::MessageCenter::Get()->GetVisibleNotifications();
191   for (auto* const notification : notifications) {
192     if (notification->id() == kDisplayErrorNotificationId)
193       return notification->message();
194   }
195   return base::string16();
196 }
197 
198 }  // namespace ash
199