root/ui/wm/core/compound_event_filter.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ShouldHideCursorOnKeyEvent
  2. ShouldHideCursorOnTouch
  3. CursorForWindowComponent
  4. AddHandler
  5. RemoveHandler
  6. UpdateCursor
  7. FilterKeyEvent
  8. FilterMouseEvent
  9. FilterTouchEvent
  10. SetCursorVisibilityOnEvent
  11. SetMouseEventsEnableStateOnEvent
  12. OnKeyEvent
  13. OnMouseEvent
  14. OnScrollEvent
  15. OnTouchEvent
  16. OnGestureEvent

// Copyright (c) 2012 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 "ui/wm/core/compound_event_filter.h"

#include "base/containers/hash_tables.h"
#include "base/logging.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tracker.h"
#include "ui/base/hit_test.h"
#include "ui/events/event.h"
#include "ui/wm/public/activation_client.h"
#include "ui/wm/public/drag_drop_client.h"

#if defined(OS_CHROMEOS) && defined(USE_X11)
#include "ui/events/x/touch_factory_x11.h"
#endif

namespace wm {

namespace {

bool ShouldHideCursorOnKeyEvent(const ui::KeyEvent& event) {
#if defined(OS_CHROMEOS)
  // All alt and control key commands are ignored.
  if (event.IsAltDown() || event.IsControlDown())
    return false;

  static bool inited = false;
  static base::hash_set<int32> ignored_keys;
  if (!inited) {
    // Modifiers.
    ignored_keys.insert(ui::VKEY_SHIFT);
    ignored_keys.insert(ui::VKEY_CONTROL);
    ignored_keys.insert(ui::VKEY_MENU);

    // Search key == VKEY_LWIN.
    ignored_keys.insert(ui::VKEY_LWIN);

    // Function keys.
    for (int key = ui::VKEY_F1; key <= ui::VKEY_F24; ++key)
      ignored_keys.insert(key);

    // Media keys.
    for (int key = ui::VKEY_BROWSER_BACK; key <= ui::VKEY_MEDIA_LAUNCH_APP2;
         ++key) {
      ignored_keys.insert(key);
    }

#if defined(OS_POSIX)
    ignored_keys.insert(ui::VKEY_WLAN);
    ignored_keys.insert(ui::VKEY_POWER);
    ignored_keys.insert(ui::VKEY_BRIGHTNESS_DOWN);
    ignored_keys.insert(ui::VKEY_BRIGHTNESS_UP);
    ignored_keys.insert(ui::VKEY_KBD_BRIGHTNESS_DOWN);
    ignored_keys.insert(ui::VKEY_KBD_BRIGHTNESS_UP);
#endif

    inited = true;
  }

  if (ignored_keys.count(event.key_code()) > 0)
    return false;

  return true;
#else  // !defined(OS_CHROMEOS)
  return false;
#endif  // defined(OS_CHROMEOS)
}

// Returns true if the cursor should be hidden on touch events.
bool ShouldHideCursorOnTouch(const ui::TouchEvent& event) {
#if defined(OS_WIN)
  return true;
#endif  // defind(OS_WIN)
#if defined(OS_CHROMEOS)
#if defined(USE_X11)
  int device_id = event.source_device_id();
  if (device_id >= 0 &&
      !ui::TouchFactory::GetInstance()->IsMultiTouchDevice(device_id)) {
    // If the touch event is coming from a mouse-device (i.e. not a real
    // touch-device), then do not hide the cursor.
    return false;
  }
#endif  // defined(USE_X11)
  return true;
#else
  // Linux Aura does not hide the cursor on touch by default.
  // TODO(tdanderson): Change this if having consistency across
  // all platforms which use Aura is desired.
  return false;
#endif
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// CompoundEventFilter, public:

CompoundEventFilter::CompoundEventFilter() {
}

CompoundEventFilter::~CompoundEventFilter() {
  // Additional filters are not owned by CompoundEventFilter and they
  // should all be removed when running here. |handlers_| has
  // check_empty == true and will DCHECK failure if it is not empty.
}

// static
gfx::NativeCursor CompoundEventFilter::CursorForWindowComponent(
    int window_component) {
  switch (window_component) {
    case HTBOTTOM:
      return ui::kCursorSouthResize;
    case HTBOTTOMLEFT:
      return ui::kCursorSouthWestResize;
    case HTBOTTOMRIGHT:
      return ui::kCursorSouthEastResize;
    case HTLEFT:
      return ui::kCursorWestResize;
    case HTRIGHT:
      return ui::kCursorEastResize;
    case HTTOP:
      return ui::kCursorNorthResize;
    case HTTOPLEFT:
      return ui::kCursorNorthWestResize;
    case HTTOPRIGHT:
      return ui::kCursorNorthEastResize;
    default:
      return ui::kCursorNull;
  }
}

void CompoundEventFilter::AddHandler(ui::EventHandler* handler) {
  handlers_.AddObserver(handler);
}

void CompoundEventFilter::RemoveHandler(ui::EventHandler* handler) {
  handlers_.RemoveObserver(handler);
}

////////////////////////////////////////////////////////////////////////////////
// CompoundEventFilter, private:

void CompoundEventFilter::UpdateCursor(aura::Window* target,
                                       ui::MouseEvent* event) {
  // If drag and drop is in progress, let the drag drop client set the cursor
  // instead of setting the cursor here.
  aura::Window* root_window = target->GetRootWindow();
  aura::client::DragDropClient* drag_drop_client =
      aura::client::GetDragDropClient(root_window);
  if (drag_drop_client && drag_drop_client->IsDragDropInProgress())
    return;

  aura::client::CursorClient* cursor_client =
      aura::client::GetCursorClient(root_window);
  if (cursor_client) {
    gfx::NativeCursor cursor = target->GetCursor(event->location());
    if ((event->flags() & ui::EF_IS_NON_CLIENT)) {
      if (target->delegate()) {
        int window_component =
            target->delegate()->GetNonClientComponent(event->location());
        cursor = CursorForWindowComponent(window_component);
      } else {
        // Allow the OS to handle non client cursors if we don't have a
        // a delegate to handle the non client hittest.
        return;
      }
    }
    cursor_client->SetCursor(cursor);
  }
}

void CompoundEventFilter::FilterKeyEvent(ui::KeyEvent* event) {
  if (handlers_.might_have_observers()) {
    ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
    ui::EventHandler* handler;
    while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
      handler->OnKeyEvent(event);
  }
}

void CompoundEventFilter::FilterMouseEvent(ui::MouseEvent* event) {
  if (handlers_.might_have_observers()) {
    ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
    ui::EventHandler* handler;
    while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
      handler->OnMouseEvent(event);
  }
}

void CompoundEventFilter::FilterTouchEvent(ui::TouchEvent* event) {
  if (handlers_.might_have_observers()) {
    ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
    ui::EventHandler* handler;
    while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
      handler->OnTouchEvent(event);
  }
}

void CompoundEventFilter::SetCursorVisibilityOnEvent(aura::Window* target,
                                                     ui::Event* event,
                                                     bool show) {
  if (event->flags() & ui::EF_IS_SYNTHESIZED)
    return;

  aura::client::CursorClient* client =
      aura::client::GetCursorClient(target->GetRootWindow());
  if (!client)
    return;

  if (show)
    client->ShowCursor();
  else
    client->HideCursor();
}

void CompoundEventFilter::SetMouseEventsEnableStateOnEvent(aura::Window* target,
                                                           ui::Event* event,
                                                           bool enable) {
  if (event->flags() & ui::EF_IS_SYNTHESIZED)
    return;
  aura::client::CursorClient* client =
      aura::client::GetCursorClient(target->GetRootWindow());
  if (!client)
    return;

  if (enable)
    client->EnableMouseEvents();
  else
    client->DisableMouseEvents();
}

////////////////////////////////////////////////////////////////////////////////
// CompoundEventFilter, ui::EventHandler implementation:

void CompoundEventFilter::OnKeyEvent(ui::KeyEvent* event) {
  if (ShouldHideCursorOnKeyEvent(*event)) {
    SetCursorVisibilityOnEvent(
        static_cast<aura::Window*>(event->target()), event, false);
  }

  FilterKeyEvent(event);
}

void CompoundEventFilter::OnMouseEvent(ui::MouseEvent* event) {
  aura::Window* window = static_cast<aura::Window*>(event->target());
  aura::WindowTracker window_tracker;
  window_tracker.Add(window);

  // We must always update the cursor, otherwise the cursor can get stuck if an
  // event filter registered with us consumes the event.
  // It should also update the cursor for clicking and wheels for ChromeOS boot.
  // When ChromeOS is booted, it hides the mouse cursor but immediate mouse
  // operation will show the cursor.
  // We also update the cursor for mouse enter in case a mouse cursor is sent to
  // outside of the root window and moved back for some reasons (e.g. running on
  // on Desktop for testing, or a bug in pointer barrier).
  if (!(event->flags() & ui::EF_FROM_TOUCH) &&
       (event->type() == ui::ET_MOUSE_ENTERED ||
        event->type() == ui::ET_MOUSE_MOVED ||
        event->type() == ui::ET_MOUSE_PRESSED ||
        event->type() == ui::ET_MOUSEWHEEL)) {
    SetMouseEventsEnableStateOnEvent(window, event, true);
    SetCursorVisibilityOnEvent(window, event, true);
    UpdateCursor(window, event);
  }

  FilterMouseEvent(event);
}

void CompoundEventFilter::OnScrollEvent(ui::ScrollEvent* event) {
}

void CompoundEventFilter::OnTouchEvent(ui::TouchEvent* event) {
  FilterTouchEvent(event);
  if (!event->handled() && event->type() == ui::ET_TOUCH_PRESSED &&
      ShouldHideCursorOnTouch(*event) &&
      !aura::Env::GetInstance()->IsMouseButtonDown()) {
    SetMouseEventsEnableStateOnEvent(
        static_cast<aura::Window*>(event->target()), event, false);
  }
}

void CompoundEventFilter::OnGestureEvent(ui::GestureEvent* event) {
  if (handlers_.might_have_observers()) {
    ObserverListBase<ui::EventHandler>::Iterator it(handlers_);
    ui::EventHandler* handler;
    while (!event->stopped_propagation() && (handler = it.GetNext()) != NULL)
      handler->OnGestureEvent(event);
  }
}

}  // namespace wm

/* [<][>][^][v][top][bottom][index][help] */