<commit_before>// Copyright (c) 2011 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 "chrome/browser/ui/touch/keyboard/keyboard_manager.h"

#include "base/json/json_writer.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_function_dispatcher.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/views/dom_view.h"
#include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/url_constants.h"
#include "content/browser/site_instance.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/tab_contents/tab_contents_observer.h"
#include "content/common/notification_service.h"
#include "ui/base/animation/animation_delegate.h"
#include "ui/base/animation/slide_animation.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/interpolated_transform.h"
#include "views/ime/text_input_type_tracker.h"
#include "views/widget/widget.h"

#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/input_method/input_method_manager.h"
#include "chrome/browser/chromeos/input_method/virtual_keyboard_selector.h"
#endif

namespace {

const int kDefaultKeyboardHeight = 300;
const int kKeyboardSlideDuration = 300;  // In milliseconds
const char kOnTextInputTypeChanged[] =
    "experimental.input.onTextInputTypeChanged";

}  // namespace

// TODO(sad): Is the default profile always going to be the one we want?

class KeyboardWidget
    : public views::Widget,
      public ui::AnimationDelegate,
      public TabContentsObserver,
      public ExtensionFunctionDispatcher::Delegate,
#if defined(OS_CHROMEOS)
      public chromeos::input_method::InputMethodManager::VirtualKeyboardObserver,
#endif
      public NotificationObserver,
      public views::Widget::Observer,
      public views::TextInputTypeObserver {
 public:
  KeyboardWidget();
  virtual ~KeyboardWidget();

  // Show the keyboard for the target widget. The events from the keyboard will
  // be sent to |widget|.
  // TODO(sad): Allow specifying the type of keyboard to show.
  void ShowKeyboardForWidget(views::Widget* widget);

  // Overridden from views::Widget
  void Hide() OVERRIDE;

 private:
  // Sets the target widget, adds/removes Widget::Observer, reparents etc.
  void SetTarget(Widget* target);

  // Overridden from views::Widget.
  virtual bool OnKeyEvent(const views::KeyEvent& event) OVERRIDE;

  // Overridden from ui::AnimationDelegate.
  virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
  virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;

  // Overridden from TabContentsObserver.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
  void OnRequest(const ExtensionHostMsg_Request_Params& params);

  // Overridden from TextInputTypeObserver.
  virtual void TextInputTypeChanged(ui::TextInputType type,
                                    views::Widget *widget) OVERRIDE;

  // Overridden from ExtensionFunctionDispatcher::Delegate.
  virtual Browser* GetBrowser() OVERRIDE;
  virtual gfx::NativeView GetNativeViewOfHost() OVERRIDE;
  virtual TabContents* GetAssociatedTabContents() const OVERRIDE;

#if defined(OS_CHROMEOS)
  // Overridden from input_method::InputMethodManager::VirtualKeyboardObserver.
  virtual void VirtualKeyboardChanged(
      chromeos::input_method::InputMethodManager* manager,
      const chromeos::input_method::VirtualKeyboard& virtual_keyboard,
      const std::string& virtual_keyboard_layout);
#endif

  // Overridden from NotificationObserver.
  virtual void Observe(int type,
                       const NotificationSource& source,
                       const NotificationDetails& details) OVERRIDE;

  // Overridden from views::Widget::Observer.
  virtual void OnWidgetClosing(Widget* widget) OVERRIDE;
  virtual void OnWidgetVisibilityChanged(Widget* widget, bool visible) OVERRIDE;
  virtual void OnWidgetActivationChanged(Widget* widget, bool active) OVERRIDE;

  // The animation.
  scoped_ptr<ui::SlideAnimation> animation_;

  // Interpolated transform used during animation.
  scoped_ptr<ui::InterpolatedTransform> transform_;

  // The DOM view to host the keyboard.
  DOMView* dom_view_;

  ExtensionFunctionDispatcher extension_dispatcher_;

  // The widget the events from the keyboard should be directed to.
  views::Widget* target_;

  // Height of the keyboard.
  int keyboard_height_;

  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(KeyboardWidget);
};

KeyboardWidget::KeyboardWidget()
    : views::Widget::Widget(),
      dom_view_(new DOMView),
      ALLOW_THIS_IN_INITIALIZER_LIST(
          extension_dispatcher_(ProfileManager::GetDefaultProfile(), this)),
      target_(NULL),
      keyboard_height_(kDefaultKeyboardHeight) {

  // Initialize the widget first.
  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  params.transparent = true;
  Init(params);

  // Setup the DOM view to host the keyboard.
  Profile* profile = ProfileManager::GetDefaultProfile();
  GURL keyboard_url(chrome::kChromeUIKeyboardURL);
  dom_view_->Init(profile,
      SiteInstance::CreateSiteInstanceForURL(profile, keyboard_url));
  dom_view_->LoadURL(keyboard_url);
  dom_view_->SetVisible(true);
  SetContentsView(dom_view_);

  // Setup observer so the events from the keyboard can be handled.
  TabContentsObserver::Observe(dom_view_->tab_contents());

  // Initialize the animation.
  animation_.reset(new ui::SlideAnimation(this));
  animation_->SetTweenType(ui::Tween::LINEAR);
  animation_->SetSlideDuration(kKeyboardSlideDuration);

  views::TextInputTypeTracker::GetInstance()->AddTextInputTypeObserver(this);
  registrar_.Add(this,
                 chrome::NOTIFICATION_HIDE_KEYBOARD_INVOKED,
                 NotificationService::AllSources());
  registrar_.Add(this,
                 chrome::NOTIFICATION_SET_KEYBOARD_HEIGHT_INVOKED,
                 NotificationService::AllSources());
  registrar_.Add(this,
                 chrome::NOTIFICATION_EDITABLE_ELEMENT_TOUCHED,
                 NotificationService::AllSources());
  registrar_.Add(this,
                 content::NOTIFICATION_APP_EXITING,
                 NotificationService::AllSources());

#if defined(OS_CHROMEOS)
  chromeos::input_method::InputMethodManager* manager =
      chromeos::input_method::InputMethodManager::GetInstance();
  manager->AddVirtualKeyboardObserver(this);
#endif
}

KeyboardWidget::~KeyboardWidget() {
  if (target_)
    target_->RemoveObserver(this);
  views::TextInputTypeTracker::GetInstance()->RemoveTextInputTypeObserver(this);
#if defined(OS_CHROMEOS)
  chromeos::input_method::InputMethodManager* manager =
      chromeos::input_method::InputMethodManager::GetInstance();
  manager->RemoveVirtualKeyboardObserver(this);
#endif

  // TODO(sad): Do anything else?
}

void KeyboardWidget::ShowKeyboardForWidget(views::Widget* widget) {
  SetTarget(widget);

  gfx::Rect rect = target_->GetWindowScreenBounds();
  rect.set_y(rect.y() + rect.height() - keyboard_height_);
  rect.set_height(keyboard_height_);
  SetBounds(rect);

  transform_.reset(new ui::InterpolatedTranslation(
      gfx::Point(0, keyboard_height_), gfx::Point()));

  GetRootView()->SetTransform(
      transform_->Interpolate(animation_->GetCurrentValue()));
  animation_->Show();

  MoveToTop();
  Show();

  bool visible = true;
  NotificationService::current()->Notify(
      chrome::NOTIFICATION_KEYBOARD_VISIBILITY_CHANGED,
      Source<KeyboardWidget>(this),
      Details<bool>(&visible));
}

void KeyboardWidget::Hide() {
  animation_->Hide();

  bool visible = false;
  NotificationService::current()->Notify(
      chrome::NOTIFICATION_KEYBOARD_VISIBILITY_CHANGED,
      Source<KeyboardWidget>(this),
      Details<bool>(&visible));
}

void KeyboardWidget::SetTarget(views::Widget* target) {
  if (target_)
    target_->RemoveObserver(this);

  target_ = target;

  if (target_) {
    // TODO(sad): Make |target_| the parent widget.
    target_->AddObserver(this);
  } else if (IsVisible()) {
    Hide();
  }
}

bool KeyboardWidget::OnKeyEvent(const views::KeyEvent& event) {
  return target_ ? target_->OnKeyEvent(event) : false;
}

void KeyboardWidget::AnimationProgressed(const ui::Animation* animation) {
  GetRootView()->SetTransform(
      transform_->Interpolate(animation_->GetCurrentValue()));
}

void KeyboardWidget::AnimationEnded(const ui::Animation* animation) {
  if (animation_->GetCurrentValue() < 0.01)
    Widget::Hide();
}

bool KeyboardWidget::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(KeyboardWidget, message)
    IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void KeyboardWidget::OnRequest(const ExtensionHostMsg_Request_Params& request) {
  extension_dispatcher_.Dispatch(request,
      dom_view_->tab_contents()->render_view_host());
}

void KeyboardWidget::TextInputTypeChanged(ui::TextInputType type,
                                          views::Widget *widget) {
  // Send onTextInputTypeChanged event to keyboard extension.
  ListValue args;
  switch (type) {
    case ui::TEXT_INPUT_TYPE_NONE: {
      args.Append(Value::CreateStringValue("none"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_TEXT: {
      args.Append(Value::CreateStringValue("text"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_PASSWORD: {
      args.Append(Value::CreateStringValue("password"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_SEARCH: {
      args.Append(Value::CreateStringValue("search"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_EMAIL: {
      args.Append(Value::CreateStringValue("email"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_NUMBER: {
      args.Append(Value::CreateStringValue("number"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_TELEPHONE: {
      args.Append(Value::CreateStringValue("tel"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_URL: {
      args.Append(Value::CreateStringValue("url"));
      break;
    }
    default: {
      NOTREACHED();
      args.Append(Value::CreateStringValue("none"));
      break;
    }
  }

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);

  Profile* profile =
      Profile::FromBrowserContext(dom_view_->tab_contents()->browser_context());
  profile->GetExtensionEventRouter()->DispatchEventToRenderers(
      kOnTextInputTypeChanged, json_args, NULL, GURL());

  if (type == ui::TEXT_INPUT_TYPE_NONE)
    Hide();
  else
    ShowKeyboardForWidget(widget);
}

Browser* KeyboardWidget::GetBrowser() {
  // TODO(sad): Find a better way. Perhaps just return NULL, and fix
  // SendKeyboardEventInputFunction::GetTopLevelWidget to somehow interact with
  // the WM to find the top level widget?
  return BrowserList::GetLastActive();
}

gfx::NativeView KeyboardWidget::GetNativeViewOfHost() {
  return dom_view_->native_view();
}

TabContents* KeyboardWidget::GetAssociatedTabContents() const {
  return dom_view_->tab_contents();
}

#if defined(OS_CHROMEOS)
void KeyboardWidget::VirtualKeyboardChanged(
    chromeos::input_method::InputMethodManager* manager,
    const chromeos::input_method::VirtualKeyboard& virtual_keyboard,
    const std::string& virtual_keyboard_layout) {
  const GURL& url = virtual_keyboard.GetURLForLayout(virtual_keyboard_layout);
  dom_view_->LoadURL(url);
  VLOG(1) << "VirtualKeyboardChanged: Switched to " << url.spec();
}
#endif

void KeyboardWidget::Observe(int type,
                             const NotificationSource& source,
                             const NotificationDetails& details) {
  switch (type) {
    case chrome::NOTIFICATION_HIDE_KEYBOARD_INVOKED: {
      Hide();
      break;
    }

    case chrome::NOTIFICATION_SET_KEYBOARD_HEIGHT_INVOKED: {
      // The keyboard is resizing itself.

      // TODO(penghuang) Allow extension conrtol the virtual keyboard directly
      // instead of using Notification.
      int height = *Details<int>(details).ptr();
      if (height != keyboard_height_) {
        DCHECK_GE(height, 0) << "Keyboard height should not be negative.";

        keyboard_height_ = height;
        gfx::Size size = GetWindowScreenBounds().size();
        size.set_height(keyboard_height_);
        SetSize(size);

        // TODO(sad): Notify the target widget that the size has changed so it
        // can update its display accordingly if it wanted to.
      }
      break;
    }

    case chrome::NOTIFICATION_EDITABLE_ELEMENT_TOUCHED: {
      // In case the keyboard hid itself and the focus is still in an editable
      // field, and the user touches the field, then we want to show the
      // keyboard again.
      views::View* src = Source<views::View>(source).ptr();
      ShowKeyboardForWidget(src->GetWidget());
      break;
    }

    case content::NOTIFICATION_APP_EXITING: {
      CloseNow();
      break;
    }

    default:
      NOTREACHED();
  }
}

void KeyboardWidget::OnWidgetClosing(Widget* widget) {
  if (target_ == widget)
    SetTarget(NULL);
}

void KeyboardWidget::OnWidgetVisibilityChanged(Widget* widget, bool visible) {
  if (target_ == widget && !visible)
    SetTarget(NULL);
}

void KeyboardWidget::OnWidgetActivationChanged(Widget* widget, bool active) {
  if (target_ == widget && !active)
    SetTarget(NULL);
}

KeyboardManager::KeyboardManager()
    : keyboard_(new KeyboardWidget()) {
  keyboard_->AddObserver(this);
}

KeyboardManager::~KeyboardManager() {
  DCHECK(!keyboard_);
}

void KeyboardManager::ShowKeyboardForWidget(views::Widget* widget) {
  keyboard_->ShowKeyboardForWidget(widget);
}

void KeyboardManager::Hide() {
  keyboard_->Hide();
}

void KeyboardManager::OnWidgetClosing(views::Widget* widget) {
  DCHECK_EQ(keyboard_, widget);
  keyboard_ = NULL;
}

// static
KeyboardManager* KeyboardManager::GetInstance() {
  return Singleton<KeyboardManager>::get();
}
<commit_msg>keyboard widget: Always position at the bottom of the screen.<commit_after>// Copyright (c) 2011 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 "chrome/browser/ui/touch/keyboard/keyboard_manager.h"

#include "base/json/json_writer.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_function_dispatcher.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/views/dom_view.h"
#include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/url_constants.h"
#include "content/browser/site_instance.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/tab_contents/tab_contents_observer.h"
#include "content/common/notification_service.h"
#include "ui/base/animation/animation_delegate.h"
#include "ui/base/animation/slide_animation.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/gfx/interpolated_transform.h"
#include "ui/gfx/screen.h"
#include "views/ime/text_input_type_tracker.h"
#include "views/widget/widget.h"

#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/input_method/input_method_manager.h"
#include "chrome/browser/chromeos/input_method/virtual_keyboard_selector.h"
#endif

namespace {

const int kDefaultKeyboardHeight = 300;
const int kKeyboardSlideDuration = 300;  // In milliseconds
const char kOnTextInputTypeChanged[] =
    "experimental.input.onTextInputTypeChanged";

}  // namespace

// TODO(sad): Is the default profile always going to be the one we want?

class KeyboardWidget
    : public views::Widget,
      public ui::AnimationDelegate,
      public TabContentsObserver,
      public ExtensionFunctionDispatcher::Delegate,
#if defined(OS_CHROMEOS)
      public chromeos::input_method::InputMethodManager::VirtualKeyboardObserver,
#endif
      public NotificationObserver,
      public views::Widget::Observer,
      public views::TextInputTypeObserver {
 public:
  KeyboardWidget();
  virtual ~KeyboardWidget();

  // Show the keyboard for the target widget. The events from the keyboard will
  // be sent to |widget|.
  // TODO(sad): Allow specifying the type of keyboard to show.
  void ShowKeyboardForWidget(views::Widget* widget);

  // Overridden from views::Widget
  void Hide() OVERRIDE;

 private:
  // Sets the target widget, adds/removes Widget::Observer, reparents etc.
  void SetTarget(Widget* target);

  // Overridden from views::Widget.
  virtual bool OnKeyEvent(const views::KeyEvent& event) OVERRIDE;

  // Overridden from ui::AnimationDelegate.
  virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
  virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;

  // Overridden from TabContentsObserver.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
  void OnRequest(const ExtensionHostMsg_Request_Params& params);

  // Overridden from TextInputTypeObserver.
  virtual void TextInputTypeChanged(ui::TextInputType type,
                                    views::Widget *widget) OVERRIDE;

  // Overridden from ExtensionFunctionDispatcher::Delegate.
  virtual Browser* GetBrowser() OVERRIDE;
  virtual gfx::NativeView GetNativeViewOfHost() OVERRIDE;
  virtual TabContents* GetAssociatedTabContents() const OVERRIDE;

#if defined(OS_CHROMEOS)
  // Overridden from input_method::InputMethodManager::VirtualKeyboardObserver.
  virtual void VirtualKeyboardChanged(
      chromeos::input_method::InputMethodManager* manager,
      const chromeos::input_method::VirtualKeyboard& virtual_keyboard,
      const std::string& virtual_keyboard_layout);
#endif

  // Overridden from NotificationObserver.
  virtual void Observe(int type,
                       const NotificationSource& source,
                       const NotificationDetails& details) OVERRIDE;

  // Overridden from views::Widget::Observer.
  virtual void OnWidgetClosing(Widget* widget) OVERRIDE;
  virtual void OnWidgetVisibilityChanged(Widget* widget, bool visible) OVERRIDE;
  virtual void OnWidgetActivationChanged(Widget* widget, bool active) OVERRIDE;

  // The animation.
  scoped_ptr<ui::SlideAnimation> animation_;

  // Interpolated transform used during animation.
  scoped_ptr<ui::InterpolatedTransform> transform_;

  // The DOM view to host the keyboard.
  DOMView* dom_view_;

  ExtensionFunctionDispatcher extension_dispatcher_;

  // The widget the events from the keyboard should be directed to.
  views::Widget* target_;

  // Height of the keyboard.
  int keyboard_height_;

  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(KeyboardWidget);
};

KeyboardWidget::KeyboardWidget()
    : views::Widget::Widget(),
      dom_view_(new DOMView),
      ALLOW_THIS_IN_INITIALIZER_LIST(
          extension_dispatcher_(ProfileManager::GetDefaultProfile(), this)),
      target_(NULL),
      keyboard_height_(kDefaultKeyboardHeight) {

  // The default position of the keyboard widget should be at the bottom,
  // spanning the entire width of the screen.
  gfx::Rect area = gfx::Screen::GetMonitorAreaNearestPoint(gfx::Point());

  // Initialize the widget first.
  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  params.transparent = true;
  params.bounds = gfx::Rect(area.x(),
                            area.y() + area.height() - keyboard_height_,
                            area.width(), keyboard_height_);
  Init(params);

  // Setup the DOM view to host the keyboard.
  Profile* profile = ProfileManager::GetDefaultProfile();
  GURL keyboard_url(chrome::kChromeUIKeyboardURL);
  dom_view_->Init(profile,
      SiteInstance::CreateSiteInstanceForURL(profile, keyboard_url));
  dom_view_->LoadURL(keyboard_url);
  dom_view_->SetVisible(true);
  SetContentsView(dom_view_);

  // Setup observer so the events from the keyboard can be handled.
  TabContentsObserver::Observe(dom_view_->tab_contents());

  // Initialize the animation.
  animation_.reset(new ui::SlideAnimation(this));
  animation_->SetTweenType(ui::Tween::LINEAR);
  animation_->SetSlideDuration(kKeyboardSlideDuration);

  views::TextInputTypeTracker::GetInstance()->AddTextInputTypeObserver(this);
  registrar_.Add(this,
                 chrome::NOTIFICATION_HIDE_KEYBOARD_INVOKED,
                 NotificationService::AllSources());
  registrar_.Add(this,
                 chrome::NOTIFICATION_SET_KEYBOARD_HEIGHT_INVOKED,
                 NotificationService::AllSources());
  registrar_.Add(this,
                 chrome::NOTIFICATION_EDITABLE_ELEMENT_TOUCHED,
                 NotificationService::AllSources());
  registrar_.Add(this,
                 content::NOTIFICATION_APP_EXITING,
                 NotificationService::AllSources());

#if defined(OS_CHROMEOS)
  chromeos::input_method::InputMethodManager* manager =
      chromeos::input_method::InputMethodManager::GetInstance();
  manager->AddVirtualKeyboardObserver(this);
#endif
}

KeyboardWidget::~KeyboardWidget() {
  if (target_)
    target_->RemoveObserver(this);
  views::TextInputTypeTracker::GetInstance()->RemoveTextInputTypeObserver(this);
#if defined(OS_CHROMEOS)
  chromeos::input_method::InputMethodManager* manager =
      chromeos::input_method::InputMethodManager::GetInstance();
  manager->RemoveVirtualKeyboardObserver(this);
#endif

  // TODO(sad): Do anything else?
}

void KeyboardWidget::ShowKeyboardForWidget(views::Widget* widget) {
  SetTarget(widget);

  transform_.reset(new ui::InterpolatedTranslation(
      gfx::Point(0, keyboard_height_), gfx::Point()));

  GetRootView()->SetTransform(
      transform_->Interpolate(animation_->GetCurrentValue()));
  animation_->Show();

  MoveToTop();
  Show();

  bool visible = true;
  NotificationService::current()->Notify(
      chrome::NOTIFICATION_KEYBOARD_VISIBILITY_CHANGED,
      Source<KeyboardWidget>(this),
      Details<bool>(&visible));
}

void KeyboardWidget::Hide() {
  animation_->Hide();

  bool visible = false;
  NotificationService::current()->Notify(
      chrome::NOTIFICATION_KEYBOARD_VISIBILITY_CHANGED,
      Source<KeyboardWidget>(this),
      Details<bool>(&visible));
}

void KeyboardWidget::SetTarget(views::Widget* target) {
  if (target_)
    target_->RemoveObserver(this);

  target_ = target;

  if (target_) {
    // TODO(sad): Make |target_| the parent widget.
    target_->AddObserver(this);
  } else if (IsVisible()) {
    Hide();
  }
}

bool KeyboardWidget::OnKeyEvent(const views::KeyEvent& event) {
  return target_ ? target_->OnKeyEvent(event) : false;
}

void KeyboardWidget::AnimationProgressed(const ui::Animation* animation) {
  GetRootView()->SetTransform(
      transform_->Interpolate(animation_->GetCurrentValue()));
}

void KeyboardWidget::AnimationEnded(const ui::Animation* animation) {
  if (animation_->GetCurrentValue() < 0.01)
    Widget::Hide();
}

bool KeyboardWidget::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(KeyboardWidget, message)
    IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void KeyboardWidget::OnRequest(const ExtensionHostMsg_Request_Params& request) {
  extension_dispatcher_.Dispatch(request,
      dom_view_->tab_contents()->render_view_host());
}

void KeyboardWidget::TextInputTypeChanged(ui::TextInputType type,
                                          views::Widget *widget) {
  // Send onTextInputTypeChanged event to keyboard extension.
  ListValue args;
  switch (type) {
    case ui::TEXT_INPUT_TYPE_NONE: {
      args.Append(Value::CreateStringValue("none"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_TEXT: {
      args.Append(Value::CreateStringValue("text"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_PASSWORD: {
      args.Append(Value::CreateStringValue("password"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_SEARCH: {
      args.Append(Value::CreateStringValue("search"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_EMAIL: {
      args.Append(Value::CreateStringValue("email"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_NUMBER: {
      args.Append(Value::CreateStringValue("number"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_TELEPHONE: {
      args.Append(Value::CreateStringValue("tel"));
      break;
    }
    case ui::TEXT_INPUT_TYPE_URL: {
      args.Append(Value::CreateStringValue("url"));
      break;
    }
    default: {
      NOTREACHED();
      args.Append(Value::CreateStringValue("none"));
      break;
    }
  }

  std::string json_args;
  base::JSONWriter::Write(&args, false, &json_args);

  Profile* profile =
      Profile::FromBrowserContext(dom_view_->tab_contents()->browser_context());
  profile->GetExtensionEventRouter()->DispatchEventToRenderers(
      kOnTextInputTypeChanged, json_args, NULL, GURL());

  if (type == ui::TEXT_INPUT_TYPE_NONE)
    Hide();
  else
    ShowKeyboardForWidget(widget);
}

Browser* KeyboardWidget::GetBrowser() {
  // TODO(sad): Find a better way. Perhaps just return NULL, and fix
  // SendKeyboardEventInputFunction::GetTopLevelWidget to somehow interact with
  // the WM to find the top level widget?
  return BrowserList::GetLastActive();
}

gfx::NativeView KeyboardWidget::GetNativeViewOfHost() {
  return dom_view_->native_view();
}

TabContents* KeyboardWidget::GetAssociatedTabContents() const {
  return dom_view_->tab_contents();
}

#if defined(OS_CHROMEOS)
void KeyboardWidget::VirtualKeyboardChanged(
    chromeos::input_method::InputMethodManager* manager,
    const chromeos::input_method::VirtualKeyboard& virtual_keyboard,
    const std::string& virtual_keyboard_layout) {
  const GURL& url = virtual_keyboard.GetURLForLayout(virtual_keyboard_layout);
  dom_view_->LoadURL(url);
  VLOG(1) << "VirtualKeyboardChanged: Switched to " << url.spec();
}
#endif

void KeyboardWidget::Observe(int type,
                             const NotificationSource& source,
                             const NotificationDetails& details) {
  switch (type) {
    case chrome::NOTIFICATION_HIDE_KEYBOARD_INVOKED: {
      Hide();
      break;
    }

    case chrome::NOTIFICATION_SET_KEYBOARD_HEIGHT_INVOKED: {
      // The keyboard is resizing itself.

      // TODO(penghuang) Allow extension conrtol the virtual keyboard directly
      // instead of using Notification.
      int height = *Details<int>(details).ptr();
      if (height != keyboard_height_) {
        DCHECK_GE(height, 0) << "Keyboard height should not be negative.";

        int old_height = keyboard_height_;
        keyboard_height_ = height;
        gfx::Rect rect = GetWindowScreenBounds();
        rect.set_y(rect.y() + old_height - keyboard_height_);
        rect.set_height(keyboard_height_);
        SetBounds(rect);

        // TODO(sad): Notify the target widget that the size has changed so it
        // can update its display accordingly if it wanted to.
      }
      break;
    }

    case chrome::NOTIFICATION_EDITABLE_ELEMENT_TOUCHED: {
      // In case the keyboard hid itself and the focus is still in an editable
      // field, and the user touches the field, then we want to show the
      // keyboard again.
      views::View* src = Source<views::View>(source).ptr();
      ShowKeyboardForWidget(src->GetWidget());
      break;
    }

    case content::NOTIFICATION_APP_EXITING: {
      CloseNow();
      break;
    }

    default:
      NOTREACHED();
  }
}

void KeyboardWidget::OnWidgetClosing(Widget* widget) {
  if (target_ == widget)
    SetTarget(NULL);
}

void KeyboardWidget::OnWidgetVisibilityChanged(Widget* widget, bool visible) {
  if (target_ == widget && !visible)
    SetTarget(NULL);
}

void KeyboardWidget::OnWidgetActivationChanged(Widget* widget, bool active) {
  if (target_ == widget && !active)
    SetTarget(NULL);
}

KeyboardManager::KeyboardManager()
    : keyboard_(new KeyboardWidget()) {
  keyboard_->AddObserver(this);
}

KeyboardManager::~KeyboardManager() {
  DCHECK(!keyboard_);
}

void KeyboardManager::ShowKeyboardForWidget(views::Widget* widget) {
  keyboard_->ShowKeyboardForWidget(widget);
}

void KeyboardManager::Hide() {
  keyboard_->Hide();
}

void KeyboardManager::OnWidgetClosing(views::Widget* widget) {
  DCHECK_EQ(keyboard_, widget);
  keyboard_ = NULL;
}

// static
KeyboardManager* KeyboardManager::GetInstance() {
  return Singleton<KeyboardManager>::get();
}
<|endoftext|>


<commit_before>#ifndef resplunk_event_Events_HeaderPlusPlus
#define resplunk_event_Events_HeaderPlusPlus

#include "resplunk/util/TemplateImplRepo.hpp"

#include <cstdint>
#include <limits>
#include <type_traits>
#include <functional>
#include <memory>
#include <map>
#include <exception>
#include <stack>
#include <set>
#include <tuple>

namespace resplunk
{
	namespace event
	{
		struct Event;

		struct ListenerPriority final
		{
			using Priority_t = std::intmax_t;

			static constexpr Priority_t FIRST = std::numeric_limits<Priority_t>::min();
			static constexpr Priority_t LAST  = std::numeric_limits<Priority_t>::max();

			constexpr ListenerPriority()
			: priority{}
			{
			}
			constexpr ListenerPriority(Priority_t p)
			: priority{p}
			{
			}
			constexpr ListenerPriority(ListenerPriority const &) = default;
			ListenerPriority &operator=(ListenerPriority const &) = delete;
			constexpr ListenerPriority(ListenerPriority &&) = default;
			ListenerPriority &operator=(ListenerPriority &&) = delete;

			constexpr operator Priority_t() const
			{
				return priority;
			}

			friend constexpr bool operator==(ListenerPriority const &a, ListenerPriority const &b)
			{
				return a.priority == b.priority;
			}
			friend constexpr bool operator<(ListenerPriority const &a, ListenerPriority const &b)
			{
				return a.priority < b.priority;
			}

		private:
			Priority_t const priority;
		};

		template<typename EventT>
		struct EventProcessor
		{
			static_assert(std::is_base_of<Event, EventT>::value, "EventT must derive from Event");
			using E = EventT;
			EventProcessor(ListenerPriority priority = ListenerPriority{})
			{
				listen(priority);
			}
			EventProcessor(EventProcessor const &) = delete;
			EventProcessor &operator=(EventProcessor const &) = delete;
			EventProcessor(EventProcessor &&) = delete;
			EventProcessor &operator=(EventProcessor &&) = delete;
			virtual ~EventProcessor()
			{
				ignore();
			}

		protected:
			virtual void listen(ListenerPriority priority = ListenerPriority{}) final
			{
				return E::listen(*this, priority);
			}
			virtual void ignore() final
			{
				return E::ignore(*this);
			}

		private:
			virtual void onEvent(E &e) const = 0;

			friend typename E::Registrar;
		};

		template<typename EventT>
		struct LambdaEventProcessor
		: EventProcessor<EventT>
		{
			using Lambda_t = std::function<void (EventT &e) /*const*/>;
			LambdaEventProcessor(Lambda_t l, ListenerPriority priority = ListenerPriority{})
			: EventProcessor<EventT>(priority)
			, lambda(l)
			{
			}

		private:
			Lambda_t lambda;
			virtual void onEvent(EventT &e) const override
			{
				return lambda(e);
			}
		};

		template<typename EventT>
		struct EventReactor
		{
			static_assert(std::is_base_of<Event, EventT>::value, "EventT must derive from Event");
			using E = EventT;
			EventReactor(ListenerPriority priority = ListenerPriority{})
			{
				listen(priority);
			}
			EventReactor(EventReactor const &) = delete;
			EventReactor &operator=(EventReactor const &) = delete;
			EventReactor(EventReactor &&) = delete;
			EventReactor &operator=(EventReactor &&) = delete;
			virtual ~EventReactor()
			{
				ignore();
			}

		protected:
			virtual void listen(ListenerPriority priority = ListenerPriority{}) final
			{
				return E::listen(*this, priority);
			}
			virtual void ignore() final
			{
				return E::ignore(*this);
			}

		private:
			virtual void onEvent(E const &e) = 0;

			friend typename E::Registrar;
		};

		template<typename EventT>
		struct LambdaEventReactor
		: EventReactor<EventT>
		{
			using Lambda_t = std::function<void (EventT const &e)>;
			LambdaEventReactor(Lambda_t l, ListenerPriority priority = ListenerPriority{})
			: EventReactor<EventT>(priority)
			, lambda(l)
			{
			}

		private:
			Lambda_t lambda;
			virtual void onEvent(EventT const &e) override
			{
				return lambda(e);
			}
		};

		template<typename EventT>
		struct EventRegistrar final
		{
			static_assert(std::is_base_of<Event, EventT>::value, "EventT must derive from Event");
			using E = EventT;
			using Processor = EventProcessor<EventT>;
			using Reactor = EventReactor<EventT>;
			EventRegistrar() = delete;

			static void listen(Processor const &p, ListenerPriority priority = ListenerPriority{})
			{
				processors().emplace(priority, std::cref(p));
			}
			static void listen(Reactor &r, ListenerPriority priority = ListenerPriority{})
			{
				reactors().emplace(priority, std::ref(r));
			}

			static void ignore(Processor const &p)
			{
				auto &procs = processors();
				for(auto it = procs.begin(); it != procs.end(); )
				{
					if(std::addressof(p) == std::addressof(it->second.get()))
					{
						it = procs.erase(it);
					}
					else ++it;
				}
			}
			static void ignore(Reactor &r)
			{
				auto &reacts = reactors();
				for(auto it = reacts.begin(); it != reacts.end(); )
				{
					if(std::addressof(r) == std::addressof(it->second.get()))
					{
						it = reacts.erase(it);
					}
					else ++it;
				}
			}

			static void process(E &e)
			{
				if(!e.shouldProcess())
				{
					return;
				}
				auto &procs = processors();
				for(auto it = procs.begin(); it != procs.end(); ++it)
				{
#if !defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS
					try
#endif
					{
						it->second.get().onEvent(e);
					}
#if !defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS
					catch(std::exception &e)
					{
						//
					}
					catch(...)
					{
						//
					}
#endif
				}
			}
			static void react(E const &e)
			{
				if(!e.shouldReact())
				{
					return;
				}
				auto &reacts = reactors();
				for(auto it = reacts.begin(); it != reacts.end(); ++it)
				{
#if !defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS
					try
#endif
					{
						it->second.get().onEvent(e);
					}
#if !defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS
					catch(std::exception &e)
					{
						//
					}
					catch(...)
					{
						//
					}
#endif
				}
			}

		private:
			struct Key final
			{
				Key() = delete;
			};
			using Processors_t = std::multimap<ListenerPriority, std::reference_wrapper<Processor const>>;
			using Reactors_t = std::multimap<ListenerPriority, std::reference_wrapper<Reactor>>;
			static Processors_t &processors()
			{
				return util::TemplateImplRepo::get<Key, Processors_t>();
			}
			static auto reactors()
			-> Reactors_t &
			{
				return util::TemplateImplRepo::get<Key, Reactors_t>();
			}
		};

		namespace impl
		{
			template<typename T, typename...>
			struct Unwrapper;
			template<typename T, typename First, typename... Rest>
			struct Unwrapper<T, First, Rest...> final
			{
				static_assert(std::is_base_of<Event, First>::value, "ParentT must derive from Event");
				using Next = Unwrapper<T, Rest...>;
				Unwrapper() = delete;
				static void process(T &t)
				{
					t.First::process();
					Next::process(t);
				}
				static void react(T const &t)
				{
					t.First::react();
					Next::react(t);
				}
			};
			template<typename T>
			struct Unwrapper<T> final
			{
				Unwrapper() = delete;
				static void process(T &t)
				{
				}
				static void react(T const &t)
				{
				}
			};
			using Guard_t = std::stack<std::set<std::type_index>>;
			struct ProcessKey final
			{
				ProcessKey() = delete;
			};
			struct ReactKey final
			{
				ReactKey() = delete;
			};
			struct PR
			{
				virtual void process() = 0;
				virtual void react() const = 0;
			};
		}
		template<typename EventT, typename... ParentT>
		struct EventImplementor
		: virtual impl::PR
		, virtual ParentT...
		{
			using Unwrapper = impl::Unwrapper<EventImplementor, ParentT...>;
			using E = EventT;
			using ParentE = std::tuple<ParentT...>;
			using Implementor = EventImplementor;
			using Processor = EventProcessor<EventT>;
			using Reactor = EventReactor<EventT>;
			using Registrar = EventRegistrar<EventT>;
			static constexpr bool MI = (sizeof...(ParentT) > 1);
			static constexpr bool ROOT = (sizeof...(ParentT) == 0);
			EventImplementor() = default;
			virtual ~EventImplementor() = 0;

			static void listen(Processor const &p, ListenerPriority priority = ListenerPriority{})
			{
				return Registrar::listen(p, priority);
			}
			static void listen(Reactor &r, ListenerPriority priority = ListenerPriority{})
			{
				return Registrar::listen(r, priority);
			}

			static void ignore(Processor const &p)
			{
				return Registrar::ignore(p);
			}
			static void ignore(Reactor &r)
			{
				return Registrar::ignore(r);
			}

			virtual void process() override
			{
				using namespace impl;
				Guard_t &guard = util::TemplateImplRepo::get<ProcessKey, Guard_t>();
				if(typeid(*this) == typeid(E))
				{
					guard.emplace();
					guard.top().emplace(typeid(E));
				}
				else if(guard.top().find(typeid(E)) != guard.top().end())
				{
					return;
				}
				else
				{
					guard.top().emplace(typeid(E));
				}

				Unwrapper::process(*this);
				Registrar::process(dynamic_cast<E &>(*this));

				if(typeid(*this) == typeid(E))
				{
					guard.pop();
				}
			}
			virtual void react() const override
			{
				using namespace impl;
				Guard_t &guard = util::TemplateImplRepo::get<ReactKey, Guard_t>();
				if(typeid(*this) == typeid(E))
				{
					guard.emplace();
					guard.top().emplace(typeid(E));
				}
				else if(guard.top().find(typeid(E)) != guard.top().end())
				{
					return;
				}
				else
				{
					guard.top().emplace(typeid(E));
				}

				Unwrapper::react(*this);
				Registrar::react(dynamic_cast<E const &>(*this));

				if(typeid(*this) == typeid(E))
				{
					guard.pop();
				}
			}
		};
		template<typename EventT, typename... ParentT>
		EventImplementor<EventT, ParentT...>::~EventImplementor<EventT, ParentT...>() = default;
	}
}

#endif
<commit_msg>Fix possibility for non-Event roots<commit_after>#ifndef resplunk_event_Events_HeaderPlusPlus
#define resplunk_event_Events_HeaderPlusPlus

#include "resplunk/util/TemplateImplRepo.hpp"

#include <cstdint>
#include <limits>
#include <type_traits>
#include <functional>
#include <memory>
#include <map>
#include <exception>
#include <stack>
#include <set>
#include <tuple>

namespace resplunk
{
	namespace event
	{
		struct Event;

		struct ListenerPriority final
		{
			using Priority_t = std::intmax_t;

			static constexpr Priority_t FIRST = std::numeric_limits<Priority_t>::min();
			static constexpr Priority_t LAST  = std::numeric_limits<Priority_t>::max();

			constexpr ListenerPriority()
			: priority{}
			{
			}
			constexpr ListenerPriority(Priority_t p)
			: priority{p}
			{
			}
			constexpr ListenerPriority(ListenerPriority const &) = default;
			ListenerPriority &operator=(ListenerPriority const &) = delete;
			constexpr ListenerPriority(ListenerPriority &&) = default;
			ListenerPriority &operator=(ListenerPriority &&) = delete;

			constexpr operator Priority_t() const
			{
				return priority;
			}

			friend constexpr bool operator==(ListenerPriority const &a, ListenerPriority const &b)
			{
				return a.priority == b.priority;
			}
			friend constexpr bool operator<(ListenerPriority const &a, ListenerPriority const &b)
			{
				return a.priority < b.priority;
			}

		private:
			Priority_t const priority;
		};

		template<typename EventT>
		struct EventProcessor
		{
			static_assert(std::is_base_of<Event, EventT>::value, "EventT must derive from Event");
			using E = EventT;
			EventProcessor(ListenerPriority priority = ListenerPriority{})
			{
				listen(priority);
			}
			EventProcessor(EventProcessor const &) = delete;
			EventProcessor &operator=(EventProcessor const &) = delete;
			EventProcessor(EventProcessor &&) = delete;
			EventProcessor &operator=(EventProcessor &&) = delete;
			virtual ~EventProcessor()
			{
				ignore();
			}

		protected:
			virtual void listen(ListenerPriority priority = ListenerPriority{}) final
			{
				return E::listen(*this, priority);
			}
			virtual void ignore() final
			{
				return E::ignore(*this);
			}

		private:
			virtual void onEvent(E &e) const = 0;

			friend typename E::Registrar;
		};

		template<typename EventT>
		struct LambdaEventProcessor
		: EventProcessor<EventT>
		{
			using Lambda_t = std::function<void (EventT &e) /*const*/>;
			LambdaEventProcessor(Lambda_t l, ListenerPriority priority = ListenerPriority{})
			: EventProcessor<EventT>(priority)
			, lambda(l)
			{
			}

		private:
			Lambda_t lambda;
			virtual void onEvent(EventT &e) const override
			{
				return lambda(e);
			}
		};

		template<typename EventT>
		struct EventReactor
		{
			static_assert(std::is_base_of<Event, EventT>::value, "EventT must derive from Event");
			using E = EventT;
			EventReactor(ListenerPriority priority = ListenerPriority{})
			{
				listen(priority);
			}
			EventReactor(EventReactor const &) = delete;
			EventReactor &operator=(EventReactor const &) = delete;
			EventReactor(EventReactor &&) = delete;
			EventReactor &operator=(EventReactor &&) = delete;
			virtual ~EventReactor()
			{
				ignore();
			}

		protected:
			virtual void listen(ListenerPriority priority = ListenerPriority{}) final
			{
				return E::listen(*this, priority);
			}
			virtual void ignore() final
			{
				return E::ignore(*this);
			}

		private:
			virtual void onEvent(E const &e) = 0;

			friend typename E::Registrar;
		};

		template<typename EventT>
		struct LambdaEventReactor
		: EventReactor<EventT>
		{
			using Lambda_t = std::function<void (EventT const &e)>;
			LambdaEventReactor(Lambda_t l, ListenerPriority priority = ListenerPriority{})
			: EventReactor<EventT>(priority)
			, lambda(l)
			{
			}

		private:
			Lambda_t lambda;
			virtual void onEvent(EventT const &e) override
			{
				return lambda(e);
			}
		};

		template<typename EventT>
		struct EventRegistrar final
		{
			static_assert(std::is_base_of<Event, EventT>::value, "EventT must derive from Event");
			using E = EventT;
			using Processor = EventProcessor<EventT>;
			using Reactor = EventReactor<EventT>;
			EventRegistrar() = delete;

			static void listen(Processor const &p, ListenerPriority priority = ListenerPriority{})
			{
				processors().emplace(priority, std::cref(p));
			}
			static void listen(Reactor &r, ListenerPriority priority = ListenerPriority{})
			{
				reactors().emplace(priority, std::ref(r));
			}

			static void ignore(Processor const &p)
			{
				auto &procs = processors();
				for(auto it = procs.begin(); it != procs.end(); )
				{
					if(std::addressof(p) == std::addressof(it->second.get()))
					{
						it = procs.erase(it);
					}
					else ++it;
				}
			}
			static void ignore(Reactor &r)
			{
				auto &reacts = reactors();
				for(auto it = reacts.begin(); it != reacts.end(); )
				{
					if(std::addressof(r) == std::addressof(it->second.get()))
					{
						it = reacts.erase(it);
					}
					else ++it;
				}
			}

			static void process(E &e)
			{
				if(!e.shouldProcess())
				{
					return;
				}
				auto &procs = processors();
				for(auto it = procs.begin(); it != procs.end(); ++it)
				{
#if !defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS
					try
#endif
					{
						it->second.get().onEvent(e);
					}
#if !defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS
					catch(std::exception &e)
					{
						//
					}
					catch(...)
					{
						//
					}
#endif
				}
			}
			static void react(E const &e)
			{
				if(!e.shouldReact())
				{
					return;
				}
				auto &reacts = reactors();
				for(auto it = reacts.begin(); it != reacts.end(); ++it)
				{
#if !defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS
					try
#endif
					{
						it->second.get().onEvent(e);
					}
#if !defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS
					catch(std::exception &e)
					{
						//
					}
					catch(...)
					{
						//
					}
#endif
				}
			}

		private:
			struct Key final
			{
				Key() = delete;
			};
			using Processors_t = std::multimap<ListenerPriority, std::reference_wrapper<Processor const>>;
			using Reactors_t = std::multimap<ListenerPriority, std::reference_wrapper<Reactor>>;
			static Processors_t &processors()
			{
				return util::TemplateImplRepo::get<Key, Processors_t>();
			}
			static auto reactors()
			-> Reactors_t &
			{
				return util::TemplateImplRepo::get<Key, Reactors_t>();
			}
		};

		namespace impl
		{
			template<typename T, typename...>
			struct Unwrapper;
			template<typename T, typename First, typename... Rest>
			struct Unwrapper<T, First, Rest...> final
			{
				static_assert(std::is_base_of<Event, First>::value, "ParentT must derive from Event");
				using Next = Unwrapper<T, Rest...>;
				Unwrapper() = delete;
				static void process(T &t)
				{
					t.First::process();
					Next::process(t);
				}
				static void react(T const &t)
				{
					t.First::react();
					Next::react(t);
				}
			};
			template<typename T>
			struct Unwrapper<T> final
			{
				static_assert(std::is_base_of<Event, typename T::E>::value, "Only Event can be root");
				Unwrapper() = delete;
				static void process(T &t)
				{
				}
				static void react(T const &t)
				{
				}
			};
			using Guard_t = std::stack<std::set<std::type_index>>;
			struct ProcessKey final
			{
				ProcessKey() = delete;
			};
			struct ReactKey final
			{
				ReactKey() = delete;
			};
			struct PR
			{
				virtual void process() = 0;
				virtual void react() const = 0;
			};
		}
		template<typename EventT, typename... ParentT>
		struct EventImplementor
		: virtual impl::PR
		, virtual ParentT...
		{
			using Unwrapper = impl::Unwrapper<EventImplementor, ParentT...>;
			using E = EventT;
			using ParentE = std::tuple<ParentT...>;
			using Implementor = EventImplementor;
			using Processor = EventProcessor<EventT>;
			using Reactor = EventReactor<EventT>;
			using Registrar = EventRegistrar<EventT>;
			static constexpr bool MI = (sizeof...(ParentT) > 1);
			static constexpr bool ROOT = (sizeof...(ParentT) == 0);
			EventImplementor() = default;
			virtual ~EventImplementor() = 0;

			static void listen(Processor const &p, ListenerPriority priority = ListenerPriority{})
			{
				return Registrar::listen(p, priority);
			}
			static void listen(Reactor &r, ListenerPriority priority = ListenerPriority{})
			{
				return Registrar::listen(r, priority);
			}

			static void ignore(Processor const &p)
			{
				return Registrar::ignore(p);
			}
			static void ignore(Reactor &r)
			{
				return Registrar::ignore(r);
			}

			virtual void process() override
			{
				using namespace impl;
				Guard_t &guard = util::TemplateImplRepo::get<ProcessKey, Guard_t>();
				if(typeid(*this) == typeid(E))
				{
					guard.emplace();
					guard.top().emplace(typeid(E));
				}
				else if(guard.top().find(typeid(E)) != guard.top().end())
				{
					return;
				}
				else
				{
					guard.top().emplace(typeid(E));
				}

				Unwrapper::process(*this);
				Registrar::process(dynamic_cast<E &>(*this));

				if(typeid(*this) == typeid(E))
				{
					guard.pop();
				}
			}
			virtual void react() const override
			{
				using namespace impl;
				Guard_t &guard = util::TemplateImplRepo::get<ReactKey, Guard_t>();
				if(typeid(*this) == typeid(E))
				{
					guard.emplace();
					guard.top().emplace(typeid(E));
				}
				else if(guard.top().find(typeid(E)) != guard.top().end())
				{
					return;
				}
				else
				{
					guard.top().emplace(typeid(E));
				}

				Unwrapper::react(*this);
				Registrar::react(dynamic_cast<E const &>(*this));

				if(typeid(*this) == typeid(E))
				{
					guard.pop();
				}
			}
		};
		template<typename EventT, typename... ParentT>
		EventImplementor<EventT, ParentT...>::~EventImplementor<EventT, ParentT...>() = default;
	}
}

#endif
<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2014-2017 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file mavlink_stream.cpp
 * Mavlink messages stream implementation.
 *
 * @author Anton Babushkin <anton.babushkin@me.com>
 * @author Lorenz Meier <lorenz@px4.io>
 */

#include <stdlib.h>

#include "mavlink_stream.h"
#include "mavlink_main.h"

MavlinkStream::MavlinkStream(Mavlink *mavlink) :
	_mavlink(mavlink)
{
	_last_sent = hrt_absolute_time();
}

/**
 * Update subscriptions and send message if necessary
 */
int
MavlinkStream::update(const hrt_abstime &t)
{
	update_data();

	// If the message has never been sent before we want
	// to send it immediately and can return right away
	if (_last_sent == 0) {
		// this will give different messages on the same run a different
		// initial timestamp which will help spacing them out
		// on the link scheduling
		if (send(t)) {
			_last_sent = hrt_absolute_time();

			if (!_first_message_sent) {
				_first_message_sent = true;
			}
		}

		return 0;
	}

	// One of the previous iterations sent the update
	// already before the deadline
	if (_last_sent > t) {
		return -1;
	}

	int64_t dt = t - _last_sent;
	int interval = (_interval > 0) ? _interval : 0;

	if (!const_rate()) {
		interval /= _mavlink->get_rate_mult();
	}

	// Send the message if it is due or
	// if it will overrun the next scheduled send interval
	// by 30% of the interval time. This helps to avoid
	// sending a scheduled message on average slower than
	// scheduled. Doing this at 50% would risk sending
	// the message too often as the loop runtime of the app
	// needs to be accounted for as well.
	// This method is not theoretically optimal but a suitable
	// stopgap as it hits its deadlines well (0.5 Hz, 50 Hz and 250 Hz)

	if (interval == 0 || (dt > (interval - (_mavlink->get_main_loop_delay() / 10) * 3))) {
		// interval expired, send message

		// If the interval is non-zero and dt is smaller than 1.5 times the interval
		// do not use the actual time but increment at a fixed rate, so that processing delays do not
		// distort the average rate. The check of the maximum interval is done to ensure that after a
		// long time not sending anything, sending multiple messages in a short time is avoided.
		if (send(t)) {
			_last_sent = ((interval > 0) && ((int64_t)(1.5f * interval) > dt)) ? _last_sent + interval : t;

			if (!_first_message_sent) {
				_first_message_sent = true;
			}

			return 0;

		} else {
			return -1;
		}
	}

	return -1;
}
<commit_msg>mavlink: don't send streams with interval 0<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2014-2017 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file mavlink_stream.cpp
 * Mavlink messages stream implementation.
 *
 * @author Anton Babushkin <anton.babushkin@me.com>
 * @author Lorenz Meier <lorenz@px4.io>
 */

#include <stdlib.h>

#include "mavlink_stream.h"
#include "mavlink_main.h"

MavlinkStream::MavlinkStream(Mavlink *mavlink) :
	_mavlink(mavlink)
{
	_last_sent = hrt_absolute_time();
}

/**
 * Update subscriptions and send message if necessary
 */
int
MavlinkStream::update(const hrt_abstime &t)
{
	update_data();

	// If the message has never been sent before we want
	// to send it immediately and can return right away
	if (_last_sent == 0) {
		// this will give different messages on the same run a different
		// initial timestamp which will help spacing them out
		// on the link scheduling
		if (send(t)) {
			_last_sent = hrt_absolute_time();

			if (!_first_message_sent) {
				_first_message_sent = true;
			}
		}

		return 0;
	}

	// One of the previous iterations sent the update
	// already before the deadline
	if (_last_sent > t) {
		return -1;
	}

	int64_t dt = t - _last_sent;
	int interval = _interval;

	if (!const_rate()) {
		interval /= _mavlink->get_rate_mult();
	}

	// We don't need to send anything if the inverval is 0. send() will be called manually.
	if (interval == 0) {
		return 0;
	}

	const bool unlimited_rate = interval < 0;

	// Send the message if it is due or
	// if it will overrun the next scheduled send interval
	// by 30% of the interval time. This helps to avoid
	// sending a scheduled message on average slower than
	// scheduled. Doing this at 50% would risk sending
	// the message too often as the loop runtime of the app
	// needs to be accounted for as well.
	// This method is not theoretically optimal but a suitable
	// stopgap as it hits its deadlines well (0.5 Hz, 50 Hz and 250 Hz)

	if (unlimited_rate || (dt > (interval - (_mavlink->get_main_loop_delay() / 10) * 3))) {
		// interval expired, send message

		// If the interval is non-zero and dt is smaller than 1.5 times the interval
		// do not use the actual time but increment at a fixed rate, so that processing delays do not
		// distort the average rate. The check of the maximum interval is done to ensure that after a
		// long time not sending anything, sending multiple messages in a short time is avoided.
		if (send(t)) {
			_last_sent = ((interval > 0) && ((int64_t)(1.5f * interval) > dt)) ? _last_sent + interval : t;

			if (!_first_message_sent) {
				_first_message_sent = true;
			}

			return 0;

		} else {
			return -1;
		}
	}

	return -1;
}
<|endoftext|>


<commit_before>#define MODULE_NAME "Navigator"

#include "navigator.h"
#include "rtl.h"

#include <future>

#include <gtest/gtest.h>

TEST(Navigator_and_RTL, compiles_woohoooo)
{
	Navigator n;
	RTL rtl(&n);

	home_position_s home_pos{};
	home_pos.valid_hpos = true;
	home_pos.valid_alt = true;

	vehicle_global_position_s glob_pos{};

	vehicle_local_position_s local_pos{};
	local_pos.xy_valid = true;
	local_pos.z_valid = true;

	vehicle_status_s v_status{};
	v_status.vehicle_type = vehicle_status_s::VEHICLE_TYPE_ROTARY_WING;

	// TODO: can't do this, it hangs forever in the while loop
	// uORB::Publication<home_position_s> home_pos_pub{ORB_ID(home_position)};
	// uORB::Publication<vehicle_global_position_s> global_pos_pub{ORB_ID(vehicle_global_position)};
	// uORB::Publication<vehicle_local_position_s> local_pos_pub{ORB_ID(vehicle_local_position)};
	// uORB::Publication<vehicle_status_s> vehicle_status_pub{ORB_ID(vehicle_status)};
	// home_pos_pub.publish(home_pos);
	// global_pos_pub.publish(glob_pos);
	// local_pos_pub.publish(local_pos);
	// vehicle_status_pub.publish(v_status);
	// n.run();

	// Hacky-hack, don't use pub-sub, just set them directly in navigator. NB! This isn't the "real" API, they should
	// be set via pub-sub otherwise this will be a constant drag on development
	*n.get_home_position() = home_pos;
	*n.get_global_position() = glob_pos;
	*n.get_local_position() = local_pos;
	*n.get_vstatus() = v_status;

	uORB::SubscriptionData<rtl_flight_time_s> _rtl_flight_time_sub{ORB_ID(rtl_flight_time)};
	rtl.find_RTL_destination();
	ASSERT_TRUE(_rtl_flight_time_sub.update());
	auto msg = _rtl_flight_time_sub.get();
	EXPECT_EQ(msg.rtl_time_s, 0);

	// WHEN: we set the vehicle type to multirotor
	v_status.vehicle_type =
		n.get_vstatus()->vehicle_type = vehicle_status_s::VEHICLE_TYPE_ROTARY_WING;
	float xy, z;
	rtl.get_rtl_xy_z_speed(xy, z);

	// THEN: the RTL speed should correspond to multirotor parameters
	float xy_desired, z_desired;
	param_get(param_handle(px4::params::MPC_XY_CRUISE), &xy_desired);
	param_get(param_handle(px4::params::MPC_Z_VEL_MAX_DN), &z_desired);
	EXPECT_FLOAT_EQ(xy, xy_desired);
	EXPECT_FLOAT_EQ(z, z_desired);

	// WHEN: it is a fixed wing
	n.get_vstatus()->vehicle_type = vehicle_status_s::VEHICLE_TYPE_FIXED_WING;
	rtl.get_rtl_xy_z_speed(xy, z);

	// THEN: it should be fixed wing parameters
	param_get(param_handle(px4::params::FW_AIRSPD_TRIM), &xy_desired);
	param_get(param_handle(px4::params::FW_T_SINK_MIN), &z_desired);
	EXPECT_FLOAT_EQ(xy, xy_desired);
	EXPECT_FLOAT_EQ(z, z_desired);

	// WHEN: it is rover
	n.get_vstatus()->vehicle_type = vehicle_status_s::VEHICLE_TYPE_ROVER;
	rtl.get_rtl_xy_z_speed(xy, z);

	// THEN: it should be rover parameters, and z should just be large (no RTL time in Z -> high speed)
	param_get(param_handle(px4::params::GND_SPEED_THR_SC), &xy_desired);
	EXPECT_FLOAT_EQ(xy, xy_desired);
	EXPECT_GT(z, 1000);
}

class RangeRTL_tth : public ::testing::Test
{
public:
	matrix::Vector3f vehicle_local_pos ;
	matrix::Vector3f rtl_point_local_pos ;
	matrix::Vector2f wind_vel;
	float vehicle_speed;
	float vehicle_descent_speed;

	void SetUp() override
	{
		vehicle_local_pos  = matrix::Vector3f(0, 0, 0);
		rtl_point_local_pos  = matrix::Vector3f(0, 0, 0);
		wind_vel  = matrix::Vector2f(0, 0);
		vehicle_speed = 5;
		vehicle_descent_speed = 1;
	}
};

TEST_F(RangeRTL_tth, zero_distance_zero_time)
{
	// GIVEN: zero distances (defaults)

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be zero
	EXPECT_FLOAT_EQ(tth, 0.f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy)
{
	// GIVEN: 10 seconds of distance
	vehicle_speed = 6.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be zero
	EXPECT_FLOAT_EQ(tth, 10.f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy_5_seconds_z)
{
	// GIVEN: 10 seconds of xy distance and 5 seconds of Z
	vehicle_speed = 4.2f;
	vehicle_descent_speed = 1.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);
	vehicle_local_pos(2) = vehicle_descent_speed * 5;

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be 15 seconds
	EXPECT_FLOAT_EQ(tth, 15.f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy_downwind_to_home)
{
	// GIVEN: 10 seconds of xy distance and 5 seconds of Z, and the wind is towards home
	vehicle_speed = 4.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	wind_vel = matrix::Vector2f(-1, -1);

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be 10, because we don't rely on wind towards home for RTL
	EXPECT_FLOAT_EQ(tth, 10.f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy_upwind_to_home)
{
	// GIVEN: 10 seconds of distance
	vehicle_speed = 4.2f;
	vehicle_descent_speed = 1.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	wind_vel = matrix::Vector2f(1, 1) / sqrt(2) * vehicle_speed / 10;

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be 11.111111... because it slows us down by 10% and time = dist/speed
	EXPECT_FLOAT_EQ(tth, 10 / 0.9f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy_z_wind_across_home)
{
	// GIVEN: a 3 4 5 triangle, with vehicle airspeed being 5, wind 3, ground speed 4
	// and the vehicle is 10 seconds away

	vehicle_speed = 5.f;
	wind_vel = matrix::Vector2f(-1, 1) / sqrt(2) * 3.;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (4 * 10) / sqrtf(2);

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be 10
	EXPECT_FLOAT_EQ(tth, 10);
}

TEST_F(RangeRTL_tth, too_strong_upwind_to_home)
{
	// GIVEN: 10 seconds of distance
	vehicle_speed = 4.2f;
	vehicle_descent_speed = 1.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	wind_vel = matrix::Vector2f(1, 1) / sqrt(2) * vehicle_speed * 1.001f;

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should never get home
	EXPECT_TRUE(std::isinf(tth)) << tth;
}

TEST_F(RangeRTL_tth, too_strong_crosswind_to_home)
{
	// GIVEN: 10 seconds of distance
	vehicle_speed = 4.2f;
	vehicle_descent_speed = 1.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	wind_vel = matrix::Vector2f(1, -1) / sqrt(2) * vehicle_speed * 1.001f;

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should never get home
	EXPECT_TRUE(std::isinf(tth)) << tth;
}
<commit_msg>Fix RangeRTL test<commit_after>#define MODULE_NAME "Navigator"

#include "navigator.h"
#include "rtl.h"

#include <future>

#include <gtest/gtest.h>

TEST(Navigator_and_RTL, interact_correctly)
{
	Navigator n;
	RTL rtl(&n);

	home_position_s home_pos{};
	home_pos.valid_hpos = true;
	home_pos.valid_alt = true;
	home_pos.timestamp = 1000;

	vehicle_global_position_s glob_pos{};

	vehicle_status_s v_status{};
	v_status.vehicle_type = vehicle_status_s::VEHICLE_TYPE_ROTARY_WING;

	// TODO: can't do this, it hangs forever in Navigator's while loop
	// uORB::Publication<home_position_s> home_pos_pub{ORB_ID(home_position)};
	// uORB::Publication<vehicle_global_position_s> global_pos_pub{ORB_ID(vehicle_global_position)};
	// uORB::Publication<vehicle_local_position_s> local_pos_pub{ORB_ID(vehicle_local_position)};
	// uORB::Publication<vehicle_status_s> vehicle_status_pub{ORB_ID(vehicle_status)};
	// home_pos_pub.publish(home_pos);
	// global_pos_pub.publish(glob_pos);
	// local_pos_pub.publish(local_pos);
	// vehicle_status_pub.publish(v_status);
	// n.run();

	// Hacky-hack, don't use pub-sub, just set them directly in navigator. NB! This isn't the "real" API, they should
	// be set via pub-sub otherwise this will be a constant drag on development
	*n.get_home_position() = home_pos;
	*n.get_global_position() = glob_pos;
	*n.get_vstatus() = v_status;

	uORB::SubscriptionData<rtl_flight_time_s> _rtl_flight_time_sub{ORB_ID(rtl_flight_time)};
	ASSERT_FALSE(_rtl_flight_time_sub.update());
	rtl.find_RTL_destination();
	ASSERT_TRUE(_rtl_flight_time_sub.update());
	auto msg = _rtl_flight_time_sub.get();
	EXPECT_EQ(msg.rtl_time_s, 0);

	// WHEN: we set the vehicle type to multirotor
	v_status.vehicle_type = n.get_vstatus()->vehicle_type = vehicle_status_s::VEHICLE_TYPE_ROTARY_WING;
	float xy, z;
	rtl.get_rtl_xy_z_speed(xy, z);

	// THEN: the RTL speed should correspond to multirotor parameters
	float xy_desired, z_desired;
	param_get(param_handle(px4::params::MPC_XY_CRUISE), &xy_desired);
	param_get(param_handle(px4::params::MPC_Z_VEL_MAX_DN), &z_desired);
	EXPECT_FLOAT_EQ(xy, xy_desired);
	EXPECT_FLOAT_EQ(z, z_desired);

	// WHEN: it is a fixed wing
	n.get_vstatus()->vehicle_type = vehicle_status_s::VEHICLE_TYPE_FIXED_WING;
	rtl.get_rtl_xy_z_speed(xy, z);

	// THEN: it should be fixed wing parameters
	param_get(param_handle(px4::params::FW_AIRSPD_TRIM), &xy_desired);
	param_get(param_handle(px4::params::FW_T_SINK_MIN), &z_desired);
	EXPECT_FLOAT_EQ(xy, xy_desired);
	EXPECT_FLOAT_EQ(z, z_desired);

	// WHEN: it is rover
	n.get_vstatus()->vehicle_type = vehicle_status_s::VEHICLE_TYPE_ROVER;
	rtl.get_rtl_xy_z_speed(xy, z);

	// THEN: it should be rover parameters, and z should just be large (no RTL time in Z -> high speed)
	param_get(param_handle(px4::params::GND_SPEED_THR_SC), &xy_desired);
	EXPECT_FLOAT_EQ(xy, xy_desired);
	EXPECT_GT(z, 1000);
}

class RangeRTL_tth : public ::testing::Test
{
public:
	matrix::Vector3f vehicle_local_pos ;
	matrix::Vector3f rtl_point_local_pos ;
	matrix::Vector2f wind_vel;
	float vehicle_speed;
	float vehicle_descent_speed;

	void SetUp() override
	{
		vehicle_local_pos  = matrix::Vector3f(0, 0, 0);
		rtl_point_local_pos  = matrix::Vector3f(0, 0, 0);
		wind_vel  = matrix::Vector2f(0, 0);
		vehicle_speed = 5;
		vehicle_descent_speed = 1;
	}
};

TEST_F(RangeRTL_tth, zero_distance_zero_time)
{
	// GIVEN: zero distances (defaults)

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be zero
	EXPECT_FLOAT_EQ(tth, 0.f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy)
{
	// GIVEN: 10 seconds of distance
	vehicle_speed = 6.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be zero
	EXPECT_FLOAT_EQ(tth, 10.f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy_5_seconds_z)
{
	// GIVEN: 10 seconds of xy distance and 5 seconds of Z
	vehicle_speed = 4.2f;
	vehicle_descent_speed = 1.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);
	vehicle_local_pos(2) = vehicle_descent_speed * 5;

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be 15 seconds
	EXPECT_FLOAT_EQ(tth, 15.f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy_downwind_to_home)
{
	// GIVEN: 10 seconds of xy distance and 5 seconds of Z, and the wind is towards home
	vehicle_speed = 4.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	wind_vel = matrix::Vector2f(-1, -1);

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be 10, because we don't rely on wind towards home for RTL
	EXPECT_FLOAT_EQ(tth, 10.f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy_upwind_to_home)
{
	// GIVEN: 10 seconds of distance
	vehicle_speed = 4.2f;
	vehicle_descent_speed = 1.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	wind_vel = matrix::Vector2f(1, 1) / sqrt(2) * vehicle_speed / 10;

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be 11.111111... because it slows us down by 10% and time = dist/speed
	EXPECT_FLOAT_EQ(tth, 10 / 0.9f);
}

TEST_F(RangeRTL_tth, ten_seconds_xy_z_wind_across_home)
{
	// GIVEN: a 3 4 5 triangle, with vehicle airspeed being 5, wind 3, ground speed 4
	// and the vehicle is 10 seconds away

	vehicle_speed = 5.f;
	wind_vel = matrix::Vector2f(-1, 1) / sqrt(2) * 3.;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (4 * 10) / sqrtf(2);

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should be 10
	EXPECT_FLOAT_EQ(tth, 10);
}

TEST_F(RangeRTL_tth, too_strong_upwind_to_home)
{
	// GIVEN: 10 seconds of distance
	vehicle_speed = 4.2f;
	vehicle_descent_speed = 1.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	wind_vel = matrix::Vector2f(1, 1) / sqrt(2) * vehicle_speed * 1.001f;

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should never get home
	EXPECT_TRUE(std::isinf(tth)) << tth;
}

TEST_F(RangeRTL_tth, too_strong_crosswind_to_home)
{
	// GIVEN: 10 seconds of distance
	vehicle_speed = 4.2f;
	vehicle_descent_speed = 1.2f;
	vehicle_local_pos(0) = vehicle_local_pos(1) = (vehicle_speed * 10) / sqrtf(2);

	wind_vel = matrix::Vector2f(1, -1) / sqrt(2) * vehicle_speed * 1.001f;

	// WHEN: we get the tth
	float tth = time_to_home(vehicle_local_pos, rtl_point_local_pos, wind_vel, vehicle_speed, vehicle_descent_speed);

	// THEN: it should never get home
	EXPECT_TRUE(std::isinf(tth)) << tth;
}
<|endoftext|>


<commit_before>//Abstract class for mutation operators. Lets us define any mutation operation we like.

#include <random>
#include <chrono>
#include <string>
#include <sstream>
#include "nodes/EANode/MutationOperation.hpp"

using namespace std;

MutationOperation::MutationOperation() {
	seed = chrono::system_clock::now().time_since_epoch().count();
	init(0, seed);
}

MutationOperation::MutationOperation(double newMutationRate) {
	seed = chrono::system_clock::now().time_since_epoch().count();
	init(newMutationRate, seed);
}

MutationOperation::MutationOperation(double newMutationRate, unsigned newSeed) {
	init(newMutationRate, newSeed);
}

void MutationOperation::init(double newMutationRate, unsigned newSeed) {
	mutationRate = newMutationRate;
	seed = newSeed;
	mt19937 newGenerator(seed);
	generator = newGenerator;
}

Genome * MutationOperation::mutate(Genome * initialGenome) {
	vector<int> newGenome, existingGenome = initialGenome->getGenome();
	vector<Locus*> loci = initialGenome->getLoci();
	for (int i = 0; i < initialGenome->genomeLength(); i++) {
		newGenome.push_back(
			this->getNewLocusValue(
				existingGenome[i],
				loci[i]->topIndex()
			)
		);
	}
	return new Genome(newGenome, loci);
}

string MutationOperation::toString() {
	string returnString = "";
	stringstream ss;

	ss << "Random seed: " << seed << "\nMutation rate: " << mutationRate << "\n";

	returnString = ss.str();

	return returnString;

}
<commit_msg>[MutationOperation]: Fixed genome-scrambling bug<commit_after>//Abstract class for mutation operators. Lets us define any mutation operation we like.

#include <random>
#include <chrono>
#include <string>
#include <sstream>
#include "nodes/EANode/MutationOperation.hpp"

using namespace std;

MutationOperation::MutationOperation() {
	seed = chrono::system_clock::now().time_since_epoch().count();
	init(0, seed);
}

MutationOperation::MutationOperation(double newMutationRate) {
	seed = chrono::system_clock::now().time_since_epoch().count();
	init(newMutationRate, seed);
}

MutationOperation::MutationOperation(double newMutationRate, unsigned newSeed) {
	init(newMutationRate, newSeed);
}

void MutationOperation::init(double newMutationRate, unsigned newSeed) {
	mutationRate = newMutationRate;
	seed = newSeed;
	generator = mt19937(seed);
}

Genome * MutationOperation::mutate(Genome * initialGenome) {
	vector<int> newGenome, existingGenome = initialGenome->getGenome();
	vector<Locus*> loci = initialGenome->getLoci();
	uniform_real_distribution<double> mutationDist(0, 1);
	for (int i = 0; i < initialGenome->genomeLength(); i++) {
		if (mutationDist(this->generator) < this->mutationRate) {
			newGenome.push_back(
				this->getNewLocusValue(
					existingGenome[i],
					loci[i]->topIndex()
				)
			);
		} else {
			newGenome.push_back(existingGenome[i]);
		}
	}
	return new Genome(newGenome, loci);
}

string MutationOperation::toString() {
	string returnString = "";
	stringstream ss;

	ss << "Random seed: " << seed << "\nMutation rate: " << mutationRate << "\n";

	returnString = ss.str();

	return returnString;

}
<|endoftext|>


<commit_before>#ifndef APOLLO_INTEGER_SEQ_HPP_INCLUDED
#define APOLLO_INTEGER_SEQ_HPP_INCLUDED APOLLO_INTEGER_SEQ_HPP_INCLUDED

// Integer sequence adapted from http://stackoverflow.com/a/17426611/2128694

namespace apollo { namespace detail {

template<int...> struct iseq{ using type = iseq; };

template<class S1, class S2> struct concat_iseq;

template<int... I1, int... I2>
struct concat_iseq<iseq<I1...>, iseq<I2...>>
  : iseq<I1..., (sizeof...(I1)+I2)...>{};

template<int N, int Lo>
struct gen_iseq : concat_iseq<
    typename gen_iseq<N/2, Lo>::type,
    typename gen_iseq<N - N/2, Lo>::type>
    {};

template<int Lo> struct gen_iseq<0, Lo> : iseq<>{
    static_assert(Lo == 0, "gen_seq: illegal 0 reach");
};
template<int Lo> struct gen_iseq<1, Lo> : iseq<Lo>{};

template <int N, int Lo = 1>
using iseq_n_t = typename gen_iseq<N, Lo>::type;

}} // namespace apollo::detail

#endif // APOLLO_INTEGER_SEQ_HPP_INCLUDED
<commit_msg>gen_iseq: Remove wrong static_assert.<commit_after>#ifndef APOLLO_INTEGER_SEQ_HPP_INCLUDED
#define APOLLO_INTEGER_SEQ_HPP_INCLUDED APOLLO_INTEGER_SEQ_HPP_INCLUDED

// Integer sequence adapted from http://stackoverflow.com/a/17426611/2128694

namespace apollo { namespace detail {

template<int...> struct iseq{ using type = iseq; };

template<class S1, class S2> struct concat_iseq;

template<int... I1, int... I2>
struct concat_iseq<iseq<I1...>, iseq<I2...>>
  : iseq<I1..., (sizeof...(I1)+I2)...>{};

template<int N, int Lo>
struct gen_iseq : concat_iseq<
    typename gen_iseq<N/2, Lo>::type,
    typename gen_iseq<N - N/2, Lo>::type>
    {};

template<int Lo> struct gen_iseq<0, Lo> : iseq<>{};
template<int Lo> struct gen_iseq<1, Lo> : iseq<Lo>{};

template <int N, int Lo = 1>
using iseq_n_t = typename gen_iseq<N, Lo>::type;

}} // namespace apollo::detail

#endif // APOLLO_INTEGER_SEQ_HPP_INCLUDED
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2016 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#ifdef ETL_CUBLAS_MODE

#include "etl/impl/cublas/cuda.hpp"
#include "etl/impl/cublas/cublas.hpp"

#endif

namespace etl {

namespace impl {

namespace cublas {

#ifdef ETL_CUBLAS_MODE

using cfloat  = std::complex<float>;  ///< Complex float type
using cdouble = std::complex<double>; ///< Complex double type

/*!
 * \brief GPU Transpose (single-precision)
 * \param handle The cublas handle
 * \param transa The operation on a
 * \param transb The operation on b
 * \param m The first dimension of a
 * \param n The second dimension of a
 * \param alpha The multiplicator of a
 * \param A A pointer to a's memory
 * \param lda The leading dimension of a
 * \param beta The multiplicator of b
 * \param B A pointer to b's memory
 * \param ldb The leading dimension of b
 * \param C A pointer to c's memory
 * \param ldc The leading dimension of c
 */
inline void cublas_geam(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, size_t m, size_t n,
                        const float* alpha, const float* A, size_t lda, const float* beta, const float* B, size_t ldb, float* C, size_t ldc) {
    cublas_check(cublasSgeam(handle, transa, transb, m, n, alpha, A, lda, beta, B, ldb, C, ldc));
}

/*!
 * \brief GPU Transpose (double-precision)
 * \param handle The cublas handle
 * \param transa The operation on a
 * \param transb The operation on b
 * \param m The first dimension of a
 * \param n The second dimension of a
 * \param alpha The multiplicator of a
 * \param A A pointer to a's memory
 * \param lda The leading dimension of a
 * \param beta The multiplicator of b
 * \param B A pointer to b's memory
 * \param ldb The leading dimension of b
 * \param C A pointer to c's memory
 * \param ldc The leading dimension of c
 */
inline void cublas_geam(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, size_t m, size_t n,
                        const double* alpha, const double* A, size_t lda, const double* beta, const double* B, size_t ldb, double* C, size_t ldc) {
    cublas_check(cublasDgeam(handle, transa, transb, m, n, alpha, A, lda, beta, B, ldb, C, ldc));
}

/*!
 * \brief GPU Transpose (complex-single-precision)
 * \param handle The cublas handle
 * \param transa The operation on a
 * \param transb The operation on b
 * \param m The first dimension of a
 * \param n The second dimension of a
 * \param alpha The multiplicator of a
 * \param A A pointer to a's memory
 * \param lda The leading dimension of a
 * \param beta The multiplicator of b
 * \param B A pointer to b's memory
 * \param ldb The leading dimension of b
 * \param C A pointer to c's memory
 * \param ldc The leading dimension of c
 */
inline void cublas_geam(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, size_t m, size_t n,
                        const cfloat* alpha, const cfloat* A, size_t lda, const cfloat* beta, const cfloat* B, size_t ldb, cfloat* C, size_t ldc) {
    cublas_check(cublasCgeam(handle, transa, transb, m, n, reinterpret_cast<const cuComplex*>(alpha),
                reinterpret_cast<const cuComplex*>(A), lda,
                reinterpret_cast<const cuComplex*>(beta), reinterpret_cast<const cuComplex*>(B), ldb,
                reinterpret_cast<cuComplex*>(C), ldc));
}

/*!
 * \brief GPU Transpose (complex-double-precision)
 * \param handle The cublas handle
 * \param transa The operation on a
 * \param transb The operation on b
 * \param m The first dimension of a
 * \param n The second dimension of a
 * \param alpha The multiplicator of a
 * \param A A pointer to a's memory
 * \param lda The leading dimension of a
 * \param beta The multiplicator of b
 * \param B A pointer to b's memory
 * \param ldb The leading dimension of b
 * \param C A pointer to c's memory
 * \param ldc The leading dimension of c
 */
inline void cublas_geam(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, size_t m, size_t n,
                        const cdouble* alpha, const cdouble* A, size_t lda, const cdouble* beta,
                        const cdouble* B, size_t ldb, cdouble* C, size_t ldc) {
    cublas_check(cublasZgeam(handle, transa, transb, m, n, reinterpret_cast<const cuDoubleComplex*>(alpha),
                reinterpret_cast<const cuDoubleComplex*>(A), lda,
                reinterpret_cast<const cuDoubleComplex*>(beta), reinterpret_cast<const cuDoubleComplex*>(B), ldb,
                reinterpret_cast<cuDoubleComplex*>(C), ldc));
}

/*!
 * \brief Inplace transposition of the square matrix c
 * \param c The matrix to transpose
 */
template <typename C, cpp_enable_if(all_dma<C>::value&& all_floating<C>::value)>
void inplace_square_transpose(C&& c) {
    decltype(auto) handle = start_cublas();

    using T = value_t<C>;

    static constexpr bool row_major = decay_traits<C>::storage_order == order::RowMajor;

    auto alpha = T(1);
    auto beta  = T(0);

    auto a_gpu = cuda::cuda_allocate_only<T>(etl::size(c));

    auto c_gpu = c.direct();
    c_gpu.gpu_allocate_copy_if_necessary();

    cuda_check(cudaMemcpy(a_gpu.get(), c_gpu.gpu_memory(), etl::size(c) * sizeof(T), cudaMemcpyDeviceToDevice));

    if(row_major){
        cublas_geam(handle.get(), CUBLAS_OP_T, CUBLAS_OP_T, etl::dim<0>(c), etl::dim<1>(c), &alpha, a_gpu.get(), etl::dim<1>(c), &beta, a_gpu.get(), etl::dim<1>(c), c_gpu.gpu_memory(), etl::dim<0>(c));
    } else {
        cublas_geam(handle.get(), CUBLAS_OP_T, CUBLAS_OP_T, etl::dim<0>(c), etl::dim<1>(c), &alpha, a_gpu.get(), etl::dim<0>(c), &beta, a_gpu.get(), etl::dim<0>(c), c_gpu.gpu_memory(), etl::dim<1>(c));
    }
}

/*!
 * \brief Inplace transposition of the rectangular matrix c
 * \param c The matrix to transpose
 */
template <typename C, cpp_enable_if(all_dma<C>::value&& all_floating<C>::value)>
void inplace_rectangular_transpose(C&& c) {
    inplace_square_transpose(c);
}

/*!
 * \brief Transpose the matrix a and the store the result in c
 * \param a The matrix to transpose
 * \param c The target matrix
 */
template <typename A, typename C, cpp_enable_if(all_dma<A, C>::value&& all_floating<A, C>::value)>
void transpose(A&& a, C&& c) {
    //TODO

    decltype(auto) handle = start_cublas();

    static constexpr bool row_major = decay_traits<A>::storage_order == order::RowMajor;

    auto alpha = value_t<A>(1.0);
    auto beta  = value_t<A>(0.0);

    auto a_gpu = a.direct();
    auto c_gpu = c.direct();

    a_gpu.gpu_allocate_copy_if_necessary();
    c_gpu.gpu_allocate_if_necessary();

    if(row_major){
        cublas_geam(handle.get(), CUBLAS_OP_T, CUBLAS_OP_T, etl::dim<0>(a), etl::dim<1>(a), &alpha, a_gpu.gpu_memory(), etl::dim<1>(a), &beta, a_gpu.gpu_memory(), etl::dim<1>(a), c_gpu.gpu_memory(), etl::dim<1>(c));
    } else {
        cublas_geam(handle.get(), CUBLAS_OP_T, CUBLAS_OP_T, etl::dim<0>(a), etl::dim<1>(a), &alpha, a_gpu.gpu_memory(), etl::dim<0>(a), &beta, a_gpu.gpu_memory(), etl::dim<0>(a), c_gpu.gpu_memory(), etl::dim<0>(c));
    }
}

/*!
 * \brief Inplace transposition of the square matrix c
 * \param c The matrix to transpose
 */
template <typename C, cpp_disable_if(all_dma<C>::value&& all_floating<C>::value)>
void inplace_square_transpose(C&& /*c*/) {}

/*!
 * \brief Inplace transposition of the rectangular matrix c
 * \param c The matrix to transpose
 */ template <typename C, cpp_disable_if(all_dma<C>::value&& all_floating<C>::value)>
void inplace_rectangular_transpose(C&& /*c*/) {}

/*!
 * \brief Transpose the matrix a and the store the result in c
 * \param a The matrix to transpose
 * \param c The target matrix
 */
template <typename A, typename C, cpp_disable_if(all_dma<A, C>::value&& all_floating<A, C>::value)>
void transpose(A&& /*a*/, C&& /*c*/) {}

#else

//COVERAGE_EXCLUDE_BEGIN

/*!
 * \brief Inplace transposition of the square matrix c
 * \param c The matrix to transpose
 */
template <typename C>
void inplace_square_transpose(C&& c) {
    cpp_unused(c);
    cpp_unreachable("CUBLAS not enabled/available");
}

/*!
 * \brief Inplace transposition of the rectangular matrix c
 * \param c The matrix to transpose
 */
template <typename C>
void inplace_rectangular_transpose(C&& c) {
    cpp_unused(c);
    cpp_unreachable("CUBLAS not enabled/available");
}

/*!
 * \brief Transpose the matrix a and the store the result in c
 * \param a The matrix to transpose
 * \param c The target matrix
 */
template <typename A, typename C>
void transpose(A&& a, C&& c) {
    cpp_unused(a);
    cpp_unused(c);
    cpp_unreachable("CUBLAS not enabled/available");
}

//COVERAGE_EXCLUDE_END

#endif

} //end of namespace cublas

} //end of namespace impl

} //end of namespace etl
<commit_msg>Cleanup<commit_after>//=======================================================================
// Copyright (c) 2014-2016 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#pragma once

#ifdef ETL_CUBLAS_MODE

#include "etl/impl/cublas/cuda.hpp"
#include "etl/impl/cublas/cublas.hpp"

#endif

namespace etl {

namespace impl {

namespace cublas {

#ifdef ETL_CUBLAS_MODE

using cfloat  = std::complex<float>;  ///< Complex float type
using cdouble = std::complex<double>; ///< Complex double type

/*!
 * \brief GPU Transpose (single-precision)
 * \param handle The cublas handle
 * \param transa The operation on a
 * \param transb The operation on b
 * \param m The first dimension of a
 * \param n The second dimension of a
 * \param alpha The multiplicator of a
 * \param A A pointer to a's memory
 * \param lda The leading dimension of a
 * \param beta The multiplicator of b
 * \param B A pointer to b's memory
 * \param ldb The leading dimension of b
 * \param C A pointer to c's memory
 * \param ldc The leading dimension of c
 */
inline void cublas_geam(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, size_t m, size_t n,
                        const float* alpha, const float* A, size_t lda, const float* beta, const float* B, size_t ldb, float* C, size_t ldc) {
    cublas_check(cublasSgeam(handle, transa, transb, m, n, alpha, A, lda, beta, B, ldb, C, ldc));
}

/*!
 * \brief GPU Transpose (double-precision)
 * \param handle The cublas handle
 * \param transa The operation on a
 * \param transb The operation on b
 * \param m The first dimension of a
 * \param n The second dimension of a
 * \param alpha The multiplicator of a
 * \param A A pointer to a's memory
 * \param lda The leading dimension of a
 * \param beta The multiplicator of b
 * \param B A pointer to b's memory
 * \param ldb The leading dimension of b
 * \param C A pointer to c's memory
 * \param ldc The leading dimension of c
 */
inline void cublas_geam(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, size_t m, size_t n,
                        const double* alpha, const double* A, size_t lda, const double* beta, const double* B, size_t ldb, double* C, size_t ldc) {
    cublas_check(cublasDgeam(handle, transa, transb, m, n, alpha, A, lda, beta, B, ldb, C, ldc));
}

/*!
 * \brief GPU Transpose (complex-single-precision)
 * \param handle The cublas handle
 * \param transa The operation on a
 * \param transb The operation on b
 * \param m The first dimension of a
 * \param n The second dimension of a
 * \param alpha The multiplicator of a
 * \param A A pointer to a's memory
 * \param lda The leading dimension of a
 * \param beta The multiplicator of b
 * \param B A pointer to b's memory
 * \param ldb The leading dimension of b
 * \param C A pointer to c's memory
 * \param ldc The leading dimension of c
 */
inline void cublas_geam(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, size_t m, size_t n,
                        const cfloat* alpha, const cfloat* A, size_t lda, const cfloat* beta, const cfloat* B, size_t ldb, cfloat* C, size_t ldc) {
    cublas_check(cublasCgeam(handle, transa, transb, m, n,
                reinterpret_cast<const cuComplex*>(alpha), reinterpret_cast<const cuComplex*>(A), lda,
                reinterpret_cast<const cuComplex*>(beta), reinterpret_cast<const cuComplex*>(B), ldb,
                reinterpret_cast<cuComplex*>(C), ldc));
}

/*!
 * \brief GPU Transpose (complex-double-precision)
 * \param handle The cublas handle
 * \param transa The operation on a
 * \param transb The operation on b
 * \param m The first dimension of a
 * \param n The second dimension of a
 * \param alpha The multiplicator of a
 * \param A A pointer to a's memory
 * \param lda The leading dimension of a
 * \param beta The multiplicator of b
 * \param B A pointer to b's memory
 * \param ldb The leading dimension of b
 * \param C A pointer to c's memory
 * \param ldc The leading dimension of c
 */
inline void cublas_geam(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, size_t m, size_t n,
                        const cdouble* alpha, const cdouble* A, size_t lda, const cdouble* beta,
                        const cdouble* B, size_t ldb, cdouble* C, size_t ldc) {
    cublas_check(cublasZgeam(handle, transa, transb, m, n,
                reinterpret_cast<const cuDoubleComplex*>(alpha), reinterpret_cast<const cuDoubleComplex*>(A), lda,
                reinterpret_cast<const cuDoubleComplex*>(beta), reinterpret_cast<const cuDoubleComplex*>(B), ldb,
                reinterpret_cast<cuDoubleComplex*>(C), ldc));
}

/*!
 * \brief Inplace transposition of the square matrix c
 * \param c The matrix to transpose
 */
template <typename C, cpp_enable_if(all_dma<C>::value&& all_floating<C>::value)>
void inplace_square_transpose(C&& c) {
    decltype(auto) handle = start_cublas();

    using T = value_t<C>;

    static constexpr bool row_major = decay_traits<C>::storage_order == order::RowMajor;

    auto alpha = T(1);
    auto beta  = T(0);

    auto a_gpu = cuda::cuda_allocate_only<T>(etl::size(c));

    auto c_gpu = c.direct();
    c_gpu.gpu_allocate_copy_if_necessary();

    cuda_check(cudaMemcpy(a_gpu.get(), c_gpu.gpu_memory(), etl::size(c) * sizeof(T), cudaMemcpyDeviceToDevice));

    if(row_major){
        cublas_geam(handle.get(), CUBLAS_OP_T, CUBLAS_OP_T, etl::dim<0>(c), etl::dim<1>(c), &alpha, a_gpu.get(), etl::dim<1>(c), &beta, a_gpu.get(), etl::dim<1>(c), c_gpu.gpu_memory(), etl::dim<0>(c));
    } else {
        cublas_geam(handle.get(), CUBLAS_OP_T, CUBLAS_OP_T, etl::dim<0>(c), etl::dim<1>(c), &alpha, a_gpu.get(), etl::dim<0>(c), &beta, a_gpu.get(), etl::dim<0>(c), c_gpu.gpu_memory(), etl::dim<1>(c));
    }
}

/*!
 * \brief Inplace transposition of the rectangular matrix c
 * \param c The matrix to transpose
 */
template <typename C, cpp_enable_if(all_dma<C>::value&& all_floating<C>::value)>
void inplace_rectangular_transpose(C&& c) {
    inplace_square_transpose(c);
}

/*!
 * \brief Transpose the matrix a and the store the result in c
 * \param a The matrix to transpose
 * \param c The target matrix
 */
template <typename A, typename C, cpp_enable_if(all_dma<A, C>::value&& all_floating<A, C>::value)>
void transpose(A&& a, C&& c) {
    decltype(auto) handle = start_cublas();

    static constexpr bool row_major = decay_traits<A>::storage_order == order::RowMajor;

    auto alpha = value_t<A>(1.0);
    auto beta  = value_t<A>(0.0);

    auto a_gpu = a.direct();
    auto c_gpu = c.direct();

    a_gpu.gpu_allocate_copy_if_necessary();
    c_gpu.gpu_allocate_if_necessary();

    if(row_major){
        cublas_geam(handle.get(), CUBLAS_OP_T, CUBLAS_OP_T, etl::dim<0>(a), etl::dim<1>(a), &alpha, a_gpu.gpu_memory(), etl::dim<1>(a), &beta, a_gpu.gpu_memory(), etl::dim<1>(a), c_gpu.gpu_memory(), etl::dim<1>(c));
    } else {
        cublas_geam(handle.get(), CUBLAS_OP_T, CUBLAS_OP_T, etl::dim<0>(a), etl::dim<1>(a), &alpha, a_gpu.gpu_memory(), etl::dim<0>(a), &beta, a_gpu.gpu_memory(), etl::dim<0>(a), c_gpu.gpu_memory(), etl::dim<0>(c));
    }
}

/*!
 * \brief Inplace transposition of the square matrix c
 * \param c The matrix to transpose
 */
template <typename C, cpp_disable_if(all_dma<C>::value&& all_floating<C>::value)>
void inplace_square_transpose(C&& /*c*/) {}

/*!
 * \brief Inplace transposition of the rectangular matrix c
 * \param c The matrix to transpose
 */ template <typename C, cpp_disable_if(all_dma<C>::value&& all_floating<C>::value)>
void inplace_rectangular_transpose(C&& /*c*/) {}

/*!
 * \brief Transpose the matrix a and the store the result in c
 * \param a The matrix to transpose
 * \param c The target matrix
 */
template <typename A, typename C, cpp_disable_if(all_dma<A, C>::value&& all_floating<A, C>::value)>
void transpose(A&& /*a*/, C&& /*c*/) {}

#else

//COVERAGE_EXCLUDE_BEGIN

/*!
 * \brief Inplace transposition of the square matrix c
 * \param c The matrix to transpose
 */
template <typename C>
void inplace_square_transpose(C&& c) {
    cpp_unused(c);
    cpp_unreachable("CUBLAS not enabled/available");
}

/*!
 * \brief Inplace transposition of the rectangular matrix c
 * \param c The matrix to transpose
 */
template <typename C>
void inplace_rectangular_transpose(C&& c) {
    cpp_unused(c);
    cpp_unreachable("CUBLAS not enabled/available");
}

/*!
 * \brief Transpose the matrix a and the store the result in c
 * \param a The matrix to transpose
 * \param c The target matrix
 */
template <typename A, typename C>
void transpose(A&& a, C&& c) {
    cpp_unused(a);
    cpp_unused(c);
    cpp_unreachable("CUBLAS not enabled/available");
}

//COVERAGE_EXCLUDE_END

#endif

} //end of namespace cublas

} //end of namespace impl

} //end of namespace etl
<|endoftext|>