shell_desktop_controller.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 "extensions/shell/browser/shell_desktop_controller.h"
6
7#include "base/command_line.h"
8#include "content/public/browser/context_factory.h"
9#include "extensions/shell/browser/shell_app_window_controller.h"
10#include "extensions/shell/common/switches.h"
11#include "ui/aura/client/cursor_client.h"
12#include "ui/aura/client/default_capture_client.h"
13#include "ui/aura/env.h"
14#include "ui/aura/layout_manager.h"
15#include "ui/aura/test/test_screen.h"
16#include "ui/aura/window.h"
17#include "ui/aura/window_event_dispatcher.h"
18#include "ui/aura/window_tree_host.h"
19#include "ui/base/cursor/cursor.h"
20#include "ui/base/cursor/image_cursors.h"
21#include "ui/base/ime/input_method_initializer.h"
22#include "ui/gfx/native_widget_types.h"
23#include "ui/gfx/screen.h"
24#include "ui/wm/core/base_focus_rules.h"
25#include "ui/wm/core/compound_event_filter.h"
26#include "ui/wm/core/cursor_manager.h"
27#include "ui/wm/core/focus_controller.h"
28#include "ui/wm/core/input_method_event_filter.h"
29#include "ui/wm/core/native_cursor_manager.h"
30#include "ui/wm/core/native_cursor_manager_delegate.h"
31#include "ui/wm/core/user_activity_detector.h"
32
33#if defined(OS_CHROMEOS)
34#include "ui/chromeos/user_activity_power_manager_notifier.h"
35#include "ui/display/types/chromeos/display_mode.h"
36#include "ui/display/types/chromeos/display_snapshot.h"
37#endif
38
39namespace extensions {
40namespace {
41
42// A simple layout manager that makes each new window fill its parent.
43class FillLayout : public aura::LayoutManager {
44 public:
45  FillLayout() {}
46  virtual ~FillLayout() {}
47
48 private:
49  // aura::LayoutManager:
50  virtual void OnWindowResized() OVERRIDE {}
51
52  virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
53    if (!child->parent())
54      return;
55
56    // Create a rect at 0,0 with the size of the parent.
57    gfx::Size parent_size = child->parent()->bounds().size();
58    child->SetBounds(gfx::Rect(parent_size));
59  }
60
61  virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
62
63  virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {}
64
65  virtual void OnChildWindowVisibilityChanged(aura::Window* child,
66                                              bool visible) OVERRIDE {}
67
68  virtual void SetChildBounds(aura::Window* child,
69                              const gfx::Rect& requested_bounds) OVERRIDE {
70    SetChildBoundsDirect(child, requested_bounds);
71  }
72
73  DISALLOW_COPY_AND_ASSIGN(FillLayout);
74};
75
76// A class that bridges the gap between CursorManager and Aura. It borrows
77// heavily from AshNativeCursorManager.
78class ShellNativeCursorManager : public wm::NativeCursorManager {
79 public:
80  explicit ShellNativeCursorManager(aura::WindowTreeHost* host)
81      : host_(host), image_cursors_(new ui::ImageCursors) {}
82  virtual ~ShellNativeCursorManager() {}
83
84  // wm::NativeCursorManager overrides.
85  virtual void SetDisplay(const gfx::Display& display,
86                          wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
87    if (image_cursors_->SetDisplay(display, display.device_scale_factor()))
88      SetCursor(delegate->GetCursor(), delegate);
89  }
90
91  virtual void SetCursor(gfx::NativeCursor cursor,
92                         wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
93    image_cursors_->SetPlatformCursor(&cursor);
94    cursor.set_device_scale_factor(image_cursors_->GetScale());
95    delegate->CommitCursor(cursor);
96
97    if (delegate->IsCursorVisible())
98      ApplyCursor(cursor);
99  }
100
101  virtual void SetVisibility(
102      bool visible,
103      wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
104    delegate->CommitVisibility(visible);
105
106    if (visible) {
107      SetCursor(delegate->GetCursor(), delegate);
108    } else {
109      gfx::NativeCursor invisible_cursor(ui::kCursorNone);
110      image_cursors_->SetPlatformCursor(&invisible_cursor);
111      ApplyCursor(invisible_cursor);
112    }
113  }
114
115  virtual void SetCursorSet(
116      ui::CursorSetType cursor_set,
117      wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
118    image_cursors_->SetCursorSet(cursor_set);
119    delegate->CommitCursorSet(cursor_set);
120    if (delegate->IsCursorVisible())
121      SetCursor(delegate->GetCursor(), delegate);
122  }
123
124  virtual void SetMouseEventsEnabled(
125      bool enabled,
126      wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
127    delegate->CommitMouseEventsEnabled(enabled);
128    SetVisibility(delegate->IsCursorVisible(), delegate);
129  }
130
131 private:
132  // Sets |cursor| as the active cursor within Aura.
133  void ApplyCursor(gfx::NativeCursor cursor) { host_->SetCursor(cursor); }
134
135  aura::WindowTreeHost* host_;  // Not owned.
136
137  scoped_ptr<ui::ImageCursors> image_cursors_;
138
139  DISALLOW_COPY_AND_ASSIGN(ShellNativeCursorManager);
140};
141
142class AppsFocusRules : public wm::BaseFocusRules {
143 public:
144  AppsFocusRules() {}
145  virtual ~AppsFocusRules() {}
146
147  virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE {
148    return true;
149  }
150
151 private:
152  DISALLOW_COPY_AND_ASSIGN(AppsFocusRules);
153};
154
155ShellDesktopController* g_instance = NULL;
156
157}  // namespace
158
159ShellDesktopController::ShellDesktopController() {
160#if defined(OS_CHROMEOS)
161  display_configurator_.reset(new ui::DisplayConfigurator);
162  display_configurator_->Init(false);
163  display_configurator_->ForceInitialConfigure(0);
164  display_configurator_->AddObserver(this);
165#endif
166  aura::Env::CreateInstance(true);
167  aura::Env::GetInstance()->set_context_factory(content::GetContextFactory());
168
169  g_instance = this;
170}
171
172ShellDesktopController::~ShellDesktopController() {
173  app_window_controller_.reset();
174  g_instance = NULL;
175  DestroyRootWindow();
176  aura::Env::DeleteInstance();
177}
178
179// static
180ShellDesktopController* ShellDesktopController::instance() {
181  return g_instance;
182}
183
184void ShellDesktopController::SetAppWindowController(
185    ShellAppWindowController* app_window_controller) {
186  app_window_controller_.reset(app_window_controller);
187}
188
189ShellAppWindow* ShellDesktopController::CreateAppWindow(
190    content::BrowserContext* context,
191    const Extension* extension) {
192  return app_window_controller_->CreateAppWindow(context, extension);
193}
194
195void ShellDesktopController::CloseAppWindows() {
196  if (app_window_controller_)
197    app_window_controller_->CloseAppWindows();
198}
199
200void ShellDesktopController::SetDisplayWorkAreaInsets(
201    const gfx::Insets& insets) {
202  test_screen_->SetWorkAreaInsets(insets);
203}
204
205aura::Window* ShellDesktopController::GetDefaultParent(
206    aura::Window* context,
207    aura::Window* window,
208    const gfx::Rect& bounds) {
209  return host_->window();
210}
211
212#if defined(OS_CHROMEOS)
213void ShellDesktopController::OnDisplayModeChanged(
214    const std::vector<ui::DisplayConfigurator::DisplayState>& displays) {
215  gfx::Size size = GetPrimaryDisplaySize();
216  if (!size.IsEmpty())
217    host_->UpdateRootWindowSize(size);
218}
219#endif
220
221void ShellDesktopController::OnHostCloseRequested(
222    const aura::WindowTreeHost* host) {
223  DCHECK_EQ(host_.get(), host);
224  CloseAppWindows();
225  base::MessageLoop::current()->PostTask(FROM_HERE,
226                                         base::MessageLoop::QuitClosure());
227}
228
229void ShellDesktopController::CreateRootWindow() {
230  // Set up basic pieces of ui::wm.
231  gfx::Size size;
232  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
233  if (command_line->HasSwitch(switches::kAppShellHostWindowBounds)) {
234    const std::string size_str =
235        command_line->GetSwitchValueASCII(switches::kAppShellHostWindowBounds);
236    int width, height;
237    CHECK_EQ(2, sscanf(size_str.c_str(), "%dx%d", &width, &height));
238    size = gfx::Size(width, height);
239  } else {
240    size = GetPrimaryDisplaySize();
241  }
242  if (size.IsEmpty())
243    size = gfx::Size(1280, 720);
244
245  test_screen_.reset(aura::TestScreen::Create(size));
246  // TODO(jamescook): Replace this with a real Screen implementation.
247  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen_.get());
248  // TODO(mukai): Set up input method.
249
250  host_.reset(test_screen_->CreateHostForPrimaryDisplay());
251  host_->InitHost();
252  aura::client::SetWindowTreeClient(host_->window(), this);
253  root_window_event_filter_.reset(new wm::CompoundEventFilter);
254  host_->window()->AddPreTargetHandler(root_window_event_filter_.get());
255  InitWindowManager();
256
257  host_->AddObserver(this);
258
259  // Ensure the X window gets mapped.
260  host_->Show();
261}
262
263void ShellDesktopController::InitWindowManager() {
264  wm::FocusController* focus_controller =
265      new wm::FocusController(CreateFocusRules());
266  aura::client::SetFocusClient(host_->window(), focus_controller);
267  host_->window()->AddPreTargetHandler(focus_controller);
268  aura::client::SetActivationClient(host_->window(), focus_controller);
269  focus_client_.reset(focus_controller);
270
271  input_method_filter_.reset(
272      new wm::InputMethodEventFilter(host_->GetAcceleratedWidget()));
273  input_method_filter_->SetInputMethodPropertyInRootWindow(host_->window());
274  root_window_event_filter_->AddHandler(input_method_filter_.get());
275
276  capture_client_.reset(
277      new aura::client::DefaultCaptureClient(host_->window()));
278
279  // Ensure new windows fill the display.
280  host_->window()->SetLayoutManager(new FillLayout);
281
282  cursor_manager_.reset(
283      new wm::CursorManager(scoped_ptr<wm::NativeCursorManager>(
284          new ShellNativeCursorManager(host_.get()))));
285  cursor_manager_->SetDisplay(
286      gfx::Screen::GetNativeScreen()->GetPrimaryDisplay());
287  cursor_manager_->SetCursor(ui::kCursorPointer);
288  aura::client::SetCursorClient(host_->window(), cursor_manager_.get());
289
290  user_activity_detector_.reset(new wm::UserActivityDetector);
291  host_->event_processor()->GetRootTarget()->AddPreTargetHandler(
292      user_activity_detector_.get());
293#if defined(OS_CHROMEOS)
294  user_activity_notifier_.reset(
295      new ui::UserActivityPowerManagerNotifier(user_activity_detector_.get()));
296#endif
297}
298
299wm::FocusRules* ShellDesktopController::CreateFocusRules() {
300  return new AppsFocusRules();
301}
302
303void ShellDesktopController::DestroyRootWindow() {
304  host_->RemoveObserver(this);
305  if (input_method_filter_)
306    root_window_event_filter_->RemoveHandler(input_method_filter_.get());
307  if (user_activity_detector_) {
308    host_->event_processor()->GetRootTarget()->RemovePreTargetHandler(
309        user_activity_detector_.get());
310  }
311  wm::FocusController* focus_controller =
312      static_cast<wm::FocusController*>(focus_client_.get());
313  if (focus_controller) {
314    host_->window()->RemovePreTargetHandler(focus_controller);
315    aura::client::SetActivationClient(host_->window(), NULL);
316  }
317  root_window_event_filter_.reset();
318  capture_client_.reset();
319  input_method_filter_.reset();
320  focus_client_.reset();
321  cursor_manager_.reset();
322#if defined(OS_CHROMEOS)
323  user_activity_notifier_.reset();
324#endif
325  user_activity_detector_.reset();
326  host_.reset();
327}
328
329gfx::Size ShellDesktopController::GetPrimaryDisplaySize() {
330#if defined(OS_CHROMEOS)
331  const std::vector<ui::DisplayConfigurator::DisplayState>& displays =
332      display_configurator_->cached_displays();
333  if (displays.empty())
334    return gfx::Size();
335  const ui::DisplayMode* mode = displays[0].display->current_mode();
336  return mode ? mode->size() : gfx::Size();
337#else
338  return gfx::Size();
339#endif
340}
341
342}  // namespace extensions
343