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.
5 #include "ash/utility/partial_screenshot_controller.h"
9 #include "ash/screenshot_delegate.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "base/stl_util.h"
13 #include "ui/events/event_handler.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/wm/core/cursor_manager.h"
21 PartialScreenshotController
* instance
= nullptr;
23 // The size to increase the invalidated area in the layer to repaint. The area
24 // should be slightly bigger than the actual region because the region indicator
25 // rectangles are drawn outside of the selected region.
26 const int kInvalidateRegionAdditionalSize
= 3;
30 class PartialScreenshotController::PartialScreenshotLayer
31 : public ui::LayerOwner
,
32 public ui::LayerDelegate
{
34 PartialScreenshotLayer(ui::Layer
* parent
) {
35 SetLayer(new ui::Layer(ui::LAYER_TEXTURED
));
36 layer()->SetFillsBoundsOpaquely(false);
37 layer()->SetBounds(parent
->bounds());
39 parent
->StackAtTop(layer());
40 layer()->SetVisible(true);
41 layer()->set_delegate(this);
43 ~PartialScreenshotLayer() override
{}
45 const gfx::Rect
& region() const { return region_
; }
47 void SetRegion(const gfx::Rect
& region
) {
48 // Invalidates the region which covers the current and new region.
49 gfx::Rect
union_rect(region_
);
50 union_rect
.Union(region
);
51 union_rect
.Inset(-kInvalidateRegionAdditionalSize
,
52 -kInvalidateRegionAdditionalSize
);
53 union_rect
.Intersects(layer()->bounds());
56 layer()->SchedulePaint(union_rect
);
61 void OnPaintLayer(gfx::Canvas
* canvas
) override
{
62 if (region_
.IsEmpty())
65 // Screenshot area representation: black rectangle with white
66 // rectangle inside. To avoid capturing these rectangles when mouse
67 // release, they should be outside of the actual capturing area.
68 gfx::Rect
rect(region_
);
70 canvas
->DrawRect(rect
, SK_ColorWHITE
);
72 canvas
->DrawRect(rect
, SK_ColorBLACK
);
75 void OnDelegatedFrameDamage(const gfx::Rect
& damage_rect_in_dip
) override
{}
77 void OnDeviceScaleFactorChanged(float device_scale_factor
) override
{}
79 base::Closure
PrepareForLayerBoundsChange() override
{
80 return base::Closure();
85 DISALLOW_COPY_AND_ASSIGN(PartialScreenshotLayer
);
88 class PartialScreenshotController::ScopedCursorSetter
{
90 ScopedCursorSetter(::wm::CursorManager
* cursor_manager
,
91 gfx::NativeCursor cursor
)
92 : cursor_manager_(nullptr) {
93 if (cursor_manager
->IsCursorLocked())
95 gfx::NativeCursor original_cursor
= cursor_manager
->GetCursor();
96 cursor_manager_
= cursor_manager
;
97 cursor_manager_
->SetCursor(cursor
);
98 cursor_manager_
->LockCursor();
99 // SetCursor does not make any effects at this point but it sets back to the
100 // original cursor when unlocked.
101 cursor_manager_
->SetCursor(original_cursor
);
104 ~ScopedCursorSetter() {
106 cursor_manager_
->UnlockCursor();
110 ::wm::CursorManager
* cursor_manager_
;
112 DISALLOW_COPY_AND_ASSIGN(ScopedCursorSetter
);
116 void PartialScreenshotController::StartPartialScreenshotSession(
117 ScreenshotDelegate
* screenshot_delegate
) {
118 // Already in the session.
121 instance
= new PartialScreenshotController(screenshot_delegate
);
125 PartialScreenshotController
* PartialScreenshotController::GetInstanceForTest() {
129 PartialScreenshotController::PartialScreenshotController(
130 ScreenshotDelegate
* screenshot_delegate
)
131 : root_window_(nullptr), screenshot_delegate_(screenshot_delegate
) {
132 DCHECK(screenshot_delegate_
);
133 Shell
* shell
= Shell::GetInstance();
134 shell
->PrependPreTargetHandler(this);
135 shell
->AddShellObserver(this);
136 Shell::GetScreen()->AddObserver(this);
138 for (aura::Window
* root
: Shell::GetAllRootWindows()) {
139 layers_
[root
] = new PartialScreenshotLayer(
140 Shell::GetContainer(root
, kShellWindowId_OverlayContainer
)->layer());
143 cursor_setter_
.reset(
144 new ScopedCursorSetter(shell
->cursor_manager(), ui::kCursorCross
));
147 PartialScreenshotController::~PartialScreenshotController() {
148 DCHECK_EQ(this, instance
);
151 Shell::GetScreen()->RemoveObserver(this);
152 Shell::GetInstance()->RemovePreTargetHandler(this);
153 Shell::GetInstance()->RemoveShellObserver(this);
154 STLDeleteValues(&layers_
);
157 void PartialScreenshotController::MaybeStart(const ui::LocatedEvent
& event
) {
158 aura::Window
* current_root
=
159 static_cast<aura::Window
*>(event
.target())->GetRootWindow();
161 // It's already started. This can happen when the second finger touches
162 // the screen, or combination of the touch and mouse. We should grab the
163 // partial screenshot instead of restarting.
164 if (current_root
== root_window_
) {
169 root_window_
= current_root
;
170 start_position_
= event
.root_location();
174 void PartialScreenshotController::Complete() {
175 const gfx::Rect
& region
= layers_
[root_window_
]->region();
176 if (!region
.IsEmpty()) {
177 screenshot_delegate_
->HandleTakePartialScreenshot(
178 root_window_
, gfx::IntersectRects(root_window_
->bounds(), region
));
183 void PartialScreenshotController::Cancel() {
187 void PartialScreenshotController::Update(const ui::LocatedEvent
& event
) {
188 // Update may happen without MaybeStart() if the partial screenshot session
189 // starts when dragging.
193 DCHECK(layers_
.find(root_window_
) != layers_
.end());
194 layers_
[root_window_
]->SetRegion(
195 gfx::Rect(std::min(start_position_
.x(), event
.root_location().x()),
196 std::min(start_position_
.y(), event
.root_location().y()),
197 ::abs(start_position_
.x() - event
.root_location().x()),
198 ::abs(start_position_
.y() - event
.root_location().y())));
201 void PartialScreenshotController::OnKeyEvent(ui::KeyEvent
* event
) {
202 if (event
->type() == ui::ET_KEY_RELEASED
&&
203 event
->key_code() == ui::VKEY_ESCAPE
) {
207 // Intercepts all key events.
208 event
->StopPropagation();
211 void PartialScreenshotController::OnMouseEvent(ui::MouseEvent
* event
) {
212 switch (event
->type()) {
213 case ui::ET_MOUSE_PRESSED
:
216 case ui::ET_MOUSE_DRAGGED
:
219 case ui::ET_MOUSE_RELEASED
:
226 event
->StopPropagation();
229 void PartialScreenshotController::OnTouchEvent(ui::TouchEvent
* event
) {
230 switch (event
->type()) {
231 case ui::ET_TOUCH_PRESSED
:
234 case ui::ET_TOUCH_MOVED
:
237 case ui::ET_TOUCH_RELEASED
:
244 event
->StopPropagation();
247 void PartialScreenshotController::OnAppTerminating() {
251 void PartialScreenshotController::OnDisplayAdded(
252 const gfx::Display
& new_display
) {
256 void PartialScreenshotController::OnDisplayRemoved(
257 const gfx::Display
& old_display
) {
261 void PartialScreenshotController::OnDisplayMetricsChanged(
262 const gfx::Display
& display
,
263 uint32_t changed_metrics
) {