// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "apps/shell/browser/shell_desktop_controller.h"
#include "apps/shell/browser/shell_app_window_controller.h"
#include "content/public/browser/context_factory.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/default_capture_client.h"
#include "ui/aura/env.h"
#include "ui/aura/layout_manager.h"
#include "ui/aura/test/test_screen.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/cursor/image_cursors.h"
#include "ui/base/ime/input_method_initializer.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/screen.h"
#include "ui/wm/core/base_focus_rules.h"
#include "ui/wm/core/compound_event_filter.h"
#include "ui/wm/core/cursor_manager.h"
#include "ui/wm/core/focus_controller.h"
#include "ui/wm/core/input_method_event_filter.h"
#include "ui/wm/core/native_cursor_manager.h"
#include "ui/wm/core/native_cursor_manager_delegate.h"
#include "ui/wm/core/user_activity_detector.h"
#if defined(OS_CHROMEOS)
#include "ui/chromeos/user_activity_power_manager_notifier.h"
#include "ui/display/types/chromeos/display_mode.h"
#include "ui/display/types/chromeos/display_snapshot.h"
#endif
namespace apps {
namespace {
// A simple layout manager that makes each new window fill its parent.
class FillLayout : public aura::LayoutManager {
public:
FillLayout() {}
virtual ~FillLayout() {}
private:
// aura::LayoutManager:
virtual void OnWindowResized() OVERRIDE {}
virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
if (!child->parent())
return;
// Create a rect at 0,0 with the size of the parent.
gfx::Size parent_size = child->parent()->bounds().size();
child->SetBounds(gfx::Rect(parent_size));
}
virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {}
virtual void OnChildWindowVisibilityChanged(aura::Window* child,
bool visible) OVERRIDE {}
virtual void SetChildBounds(aura::Window* child,
const gfx::Rect& requested_bounds) OVERRIDE {
SetChildBoundsDirect(child, requested_bounds);
}
DISALLOW_COPY_AND_ASSIGN(FillLayout);
};
// A class that bridges the gap between CursorManager and Aura. It borrows
// heavily from AshNativeCursorManager.
class ShellNativeCursorManager : public wm::NativeCursorManager {
public:
explicit ShellNativeCursorManager(aura::WindowTreeHost* host)
: host_(host),
image_cursors_(new ui::ImageCursors) {}
virtual ~ShellNativeCursorManager() {}
// wm::NativeCursorManager overrides.
virtual void SetDisplay(
const gfx::Display& display,
wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
if (image_cursors_->SetDisplay(display, display.device_scale_factor()))
SetCursor(delegate->GetCursor(), delegate);
}
virtual void SetCursor(
gfx::NativeCursor cursor,
wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
image_cursors_->SetPlatformCursor(&cursor);
cursor.set_device_scale_factor(image_cursors_->GetScale());
delegate->CommitCursor(cursor);
if (delegate->IsCursorVisible())
ApplyCursor(cursor);
}
virtual void SetVisibility(
bool visible,
wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
delegate->CommitVisibility(visible);
if (visible) {
SetCursor(delegate->GetCursor(), delegate);
} else {
gfx::NativeCursor invisible_cursor(ui::kCursorNone);
image_cursors_->SetPlatformCursor(&invisible_cursor);
ApplyCursor(invisible_cursor);
}
}
virtual void SetCursorSet(
ui::CursorSetType cursor_set,
wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
image_cursors_->SetCursorSet(cursor_set);
delegate->CommitCursorSet(cursor_set);
if (delegate->IsCursorVisible())
SetCursor(delegate->GetCursor(), delegate);
}
virtual void SetMouseEventsEnabled(
bool enabled,
wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
delegate->CommitMouseEventsEnabled(enabled);
SetVisibility(delegate->IsCursorVisible(), delegate);
}
private:
// Sets |cursor| as the active cursor within Aura.
void ApplyCursor(gfx::NativeCursor cursor) {
host_->SetCursor(cursor);
}
aura::WindowTreeHost* host_; // Not owned.
scoped_ptr<ui::ImageCursors> image_cursors_;
DISALLOW_COPY_AND_ASSIGN(ShellNativeCursorManager);
};
class AppsFocusRules : public wm::BaseFocusRules {
public:
AppsFocusRules() {}
virtual ~AppsFocusRules() {}
virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE {
return true;
}
private:
DISALLOW_COPY_AND_ASSIGN(AppsFocusRules);
};
ShellDesktopController* g_instance = NULL;
} // namespace
ShellDesktopController::ShellDesktopController() {
#if defined(OS_CHROMEOS)
display_configurator_.reset(new ui::DisplayConfigurator);
display_configurator_->Init(false);
display_configurator_->ForceInitialConfigure(0);
display_configurator_->AddObserver(this);
#endif
aura::Env::CreateInstance(true);
aura::Env::GetInstance()->set_context_factory(content::GetContextFactory());
g_instance = this;
}
ShellDesktopController::~ShellDesktopController() {
app_window_controller_.reset();
g_instance = NULL;
DestroyRootWindow();
aura::Env::DeleteInstance();
}
// static
ShellDesktopController* ShellDesktopController::instance() {
return g_instance;
}
void ShellDesktopController::SetAppWindowController(
ShellAppWindowController* app_window_controller) {
app_window_controller_.reset(app_window_controller);
}
ShellAppWindow* ShellDesktopController::CreateAppWindow(
content::BrowserContext* context) {
return app_window_controller_->CreateAppWindow(context);
}
void ShellDesktopController::CloseAppWindows() {
if (app_window_controller_)
app_window_controller_->CloseAppWindows();
}
aura::Window* ShellDesktopController::GetDefaultParent(
aura::Window* context,
aura::Window* window,
const gfx::Rect& bounds) {
return host_->window();
}
#if defined(OS_CHROMEOS)
void ShellDesktopController::OnDisplayModeChanged(
const std::vector<ui::DisplayConfigurator::DisplayState>& displays) {
gfx::Size size = GetPrimaryDisplaySize();
if (!size.IsEmpty())
host_->UpdateRootWindowSize(size);
}
#endif
void ShellDesktopController::OnHostCloseRequested(
const aura::WindowTreeHost* host) {
DCHECK_EQ(host_.get(), host);
CloseAppWindows();
base::MessageLoop::current()->PostTask(FROM_HERE,
base::MessageLoop::QuitClosure());
}
void ShellDesktopController::CreateRootWindow() {
// Set up basic pieces of ui::wm.
gfx::Size size = GetPrimaryDisplaySize();
if (size.IsEmpty())
size = gfx::Size(1280, 720);
test_screen_.reset(aura::TestScreen::Create(size));
// TODO(jamescook): Replace this with a real Screen implementation.
gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen_.get());
// TODO(mukai): Set up input method.
host_.reset(aura::WindowTreeHost::Create(gfx::Rect(size)));
host_->InitHost();
aura::client::SetWindowTreeClient(host_->window(), this);
root_window_event_filter_.reset(new wm::CompoundEventFilter);
host_->window()->AddPreTargetHandler(root_window_event_filter_.get());
InitWindowManager();
host_->AddObserver(this);
// Ensure the X window gets mapped.
host_->Show();
}
void ShellDesktopController::InitWindowManager() {
wm::FocusController* focus_controller =
new wm::FocusController(new AppsFocusRules());
aura::client::SetFocusClient(host_->window(), focus_controller);
host_->window()->AddPreTargetHandler(focus_controller);
aura::client::SetActivationClient(host_->window(), focus_controller);
focus_client_.reset(focus_controller);
input_method_filter_.reset(
new wm::InputMethodEventFilter(host_->GetAcceleratedWidget()));
input_method_filter_->SetInputMethodPropertyInRootWindow(host_->window());
root_window_event_filter_->AddHandler(input_method_filter_.get());
capture_client_.reset(
new aura::client::DefaultCaptureClient(host_->window()));
// Ensure new windows fill the display.
host_->window()->SetLayoutManager(new FillLayout);
cursor_manager_.reset(
new wm::CursorManager(scoped_ptr<wm::NativeCursorManager>(
new ShellNativeCursorManager(host_.get()))));
cursor_manager_->SetDisplay(
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay());
cursor_manager_->SetCursor(ui::kCursorPointer);
aura::client::SetCursorClient(host_->window(), cursor_manager_.get());
user_activity_detector_.reset(new wm::UserActivityDetector);
host_->event_processor()->GetRootTarget()->AddPreTargetHandler(
user_activity_detector_.get());
#if defined(OS_CHROMEOS)
user_activity_notifier_.reset(
new ui::UserActivityPowerManagerNotifier(user_activity_detector_.get()));
#endif
}
void ShellDesktopController::DestroyRootWindow() {
host_->RemoveObserver(this);
if (input_method_filter_)
root_window_event_filter_->RemoveHandler(input_method_filter_.get());
if (user_activity_detector_) {
host_->event_processor()->GetRootTarget()->RemovePreTargetHandler(
user_activity_detector_.get());
}
wm::FocusController* focus_controller =
static_cast<wm::FocusController*>(focus_client_.get());
if (focus_controller) {
host_->window()->RemovePreTargetHandler(focus_controller);
aura::client::SetActivationClient(host_->window(), NULL);
}
root_window_event_filter_.reset();
capture_client_.reset();
input_method_filter_.reset();
focus_client_.reset();
cursor_manager_.reset();
#if defined(OS_CHROMEOS)
user_activity_notifier_.reset();
#endif
user_activity_detector_.reset();
host_.reset();
}
gfx::Size ShellDesktopController::GetPrimaryDisplaySize() {
#if defined(OS_CHROMEOS)
const std::vector<ui::DisplayConfigurator::DisplayState>& displays =
display_configurator_->cached_displays();
if (displays.empty())
return gfx::Size();
const ui::DisplayMode* mode = displays[0].display->current_mode();
return mode ? mode->size() : gfx::Size();
#else
return gfx::Size();
#endif
}
} // namespace apps