<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "win8/metro_driver/stdafx.h"
#include "win8/metro_driver/chrome_app_view_ash.h"

#include <windows.foundation.h>

#include "base/bind.h"
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "base/win/metro.h"
#include "base/win/win_util.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_sender.h"
#include "ui/base/events/event_constants.h"
#include "ui/metro_viewer/metro_viewer_messages.h"
#include "win8/metro_driver/metro_driver.h"
#include "win8/metro_driver/winrt_utils.h"

typedef winfoundtn::ITypedEventHandler<
    winapp::Core::CoreApplicationView*,
    winapp::Activation::IActivatedEventArgs*> ActivatedHandler;

typedef winfoundtn::ITypedEventHandler<
    winui::Core::CoreWindow*,
    winui::Core::PointerEventArgs*> PointerEventHandler;

typedef winfoundtn::ITypedEventHandler<
    winui::Core::CoreWindow*,
    winui::Core::KeyEventArgs*> KeyEventHandler;

typedef winfoundtn::ITypedEventHandler<
    winui::Core::CoreWindow*,
    winui::Core::CharacterReceivedEventArgs*> CharEventHandler;

// This function is exported by chrome.exe.
typedef int (__cdecl *BreakpadExceptionHandler)(EXCEPTION_POINTERS* info);

// Global information used across the metro driver.
struct Globals {
  LPTHREAD_START_ROUTINE host_main;
  HWND core_window;
  DWORD main_thread_id;
  winapp::Activation::ApplicationExecutionState previous_state;
  winapp::Core::ICoreApplicationExit* app_exit;
  BreakpadExceptionHandler breakpad_exception_handler;
} globals;

namespace {

class ChromeChannelListener : public IPC::Listener {
 public:
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
    DVLOG(1) << "Received ipc message " << message.type();
    return true;
  }

  virtual void OnChannelError() OVERRIDE {
    DVLOG(1) << "Channel error";
    MessageLoop::current()->Quit();
  }

  void Init(IPC::Sender* s) {
    sender_ = s;
  }

 private:
  IPC::Sender* sender_;
};

// This class helps decoding the pointer properties of an event.
class PointerInfoHandler {
 public:
  PointerInfoHandler() : x_(0), y_(0), is_mouse_(false) {};

  HRESULT Init(winui::Core::IPointerEventArgs* args) {
    mswr::ComPtr<winui::Input::IPointerPoint> pointer_point;
    HRESULT hr = args->get_CurrentPoint(&pointer_point);
    if (FAILED(hr))
      return hr;

    mswr::ComPtr<windevs::Input::IPointerDevice> pointer_device;
    hr = pointer_point->get_PointerDevice(&pointer_device);
    if (FAILED(hr))
      return hr;

    windevs::Input::PointerDeviceType device_type;
    hr = pointer_device->get_PointerDeviceType(&device_type);
    if (FAILED(hr))
      return hr;

    is_mouse_ =  (device_type == windevs::Input::PointerDeviceType_Mouse);
    winfoundtn::Point point;
    hr = pointer_point->get_Position(&point);
    if (FAILED(hr))
      return hr;

    x_ = point.X;
    y_ = point.Y;
    return S_OK;
  }

  bool is_mouse() const { return is_mouse_; }
  int x() const { return x_; }
  int y() const { return y_; }

 private:
  int x_;
  int y_;
  bool is_mouse_;
};

void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) {
  // We're entering a nested message loop, let's allow dispatching
  // tasks while we're in there.
  MessageLoop::current()->SetNestableTasksAllowed(true);

  // Enter main core message loop. There are several ways to exit it
  // Nicely:
  // 1 - User action like ALT-F4.
  // 2 - Calling ICoreApplicationExit::Exit().
  // 3-  Posting WM_CLOSE to the core window.
  HRESULT hr = dispatcher->ProcessEvents(
      winui::Core::CoreProcessEventsOption
          ::CoreProcessEventsOption_ProcessUntilQuit);

  // Wind down the thread's chrome message loop.
  MessageLoop::current()->Quit();
}

// Helper to return the state of the shift/control/alt keys.
uint32 GetKeyboardEventFlags() {
  uint32 flags = 0;
  if (base::win::IsShiftPressed())
    flags |= ui::EF_SHIFT_DOWN;
  if (base::win::IsCtrlPressed())
    flags |= ui::EF_CONTROL_DOWN;
  if (base::win::IsAltPressed())
    flags |= ui::EF_ALT_DOWN;
  return flags;
}

}  // namespace

ChromeAppViewAsh::ChromeAppViewAsh()
    : ui_channel_(nullptr),
      ui_channel_listener_(nullptr) {
  globals.previous_state =
      winapp::Activation::ApplicationExecutionState_NotRunning;
}

ChromeAppViewAsh::~ChromeAppViewAsh() {
  DVLOG(1) << __FUNCTION__;
}

IFACEMETHODIMP
ChromeAppViewAsh::Initialize(winapp::Core::ICoreApplicationView* view) {
  view_ = view;
  DVLOG(1) << __FUNCTION__;
  globals.main_thread_id = ::GetCurrentThreadId();

  HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>(
      this, &ChromeAppViewAsh::OnActivate).Get(),
      &activated_token_);
  CheckHR(hr);
  return hr;
}

IFACEMETHODIMP
ChromeAppViewAsh::SetWindow(winui::Core::ICoreWindow* window) {
  window_ = window;
  DVLOG(1) << __FUNCTION__;
  // Register for pointer and keyboard notifications. We forward
  // them to the browser process via IPC.
  HRESULT hr = window_->add_PointerMoved(mswr::Callback<PointerEventHandler>(
      this, &ChromeAppViewAsh::OnPointerMoved).Get(),
      &pointermoved_token_);
  CheckHR(hr);

  hr = window_->add_PointerPressed(mswr::Callback<PointerEventHandler>(
      this, &ChromeAppViewAsh::OnPointerPressed).Get(),
      &pointerpressed_token_);
  CheckHR(hr);

  hr = window_->add_PointerReleased(mswr::Callback<PointerEventHandler>(
      this, &ChromeAppViewAsh::OnPointerReleased).Get(),
      &pointerreleased_token_);
  CheckHR(hr);

  hr = window_->add_KeyDown(mswr::Callback<KeyEventHandler>(
      this, &ChromeAppViewAsh::OnKeyDown).Get(),
      &keydown_token_);
  CheckHR(hr);

  hr = window_->add_KeyUp(mswr::Callback<KeyEventHandler>(
      this, &ChromeAppViewAsh::OnKeyUp).Get(),
      &keyup_token_);
  CheckHR(hr);

  hr = window_->add_CharacterReceived(mswr::Callback<CharEventHandler>(
      this, &ChromeAppViewAsh::OnCharacterReceived).Get(),
      &character_received_token_);
  CheckHR(hr);

  // By initializing the direct 3D swap chain with the corewindow
  // we can now directly blit to it from the browser process.
  direct3d_helper_.Initialize(window);
  DVLOG(1) << "Initialized Direct3D.";
  return S_OK;
}

IFACEMETHODIMP
ChromeAppViewAsh::Load(HSTRING entryPoint) {
  DVLOG(1) << __FUNCTION__;
  return S_OK;
}

IFACEMETHODIMP
ChromeAppViewAsh::Run() {
  DVLOG(1) << __FUNCTION__;
  mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
  HRESULT hr = window_->get_Dispatcher(&dispatcher);
  CheckHR(hr, "Dispatcher failed.");

  hr = window_->Activate();
  if (SUCCEEDED(hr)) {
    // TODO(cpu): Draw something here.
  } else {
    DVLOG(1) << "Activate failed, hr=" << hr;
  }

  // Create a message loop to allow message passing into this thread.
  MessageLoop msg_loop(MessageLoop::TYPE_UI);

  // Create the IPC channel IO thread. It needs to out-live the ChannelProxy.
  base::Thread thread("metro_IO_thread");
  base::Thread::Options options;
  options.message_loop_type = MessageLoop::TYPE_IO;
  thread.StartWithOptions(options);

  // In Aura mode we create an IPC channel to the browser which should
  // be already running.
  ChromeChannelListener ui_channel_listener;
  IPC::ChannelProxy ui_channel("viewer",
                               IPC::Channel::MODE_NAMED_CLIENT,
                               &ui_channel_listener,
                               thread.message_loop_proxy());
  ui_channel_listener.Init(&ui_channel);

  ui_channel_listener_ = &ui_channel_listener;
  ui_channel_ = &ui_channel;

  ui_channel_->Send(new MetroViewerHostMsg_SetTargetSurface(
                    gfx::NativeViewId(globals.core_window)));

  DVLOG(1) << "ICoreWindow sent " << globals.core_window;

  // And post the task that'll do the inner Metro message pumping to it.
  msg_loop.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get()));
  msg_loop.Run();

  DVLOG(0) << "ProcessEvents done, hr=" << hr;
  return hr;
}

IFACEMETHODIMP
ChromeAppViewAsh::Uninitialize() {
  DVLOG(1) << __FUNCTION__;
  window_ = nullptr;
  view_ = nullptr;
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnActivate(winapp::Core::ICoreApplicationView*,
    winapp::Activation::IActivatedEventArgs* args) {
  DVLOG(1) << __FUNCTION__;

  args->get_PreviousExecutionState(&globals.previous_state);
  DVLOG(1) << "Previous Execution State: " << globals.previous_state;

  window_->Activate();

  if (globals.previous_state ==
      winapp::Activation::ApplicationExecutionState_Running) {
    DVLOG(1) << "Already running. Skipping rest of OnActivate.";
    return S_OK;
  }

  globals.core_window =
      winrt_utils::FindCoreWindow(globals.main_thread_id, 10);

  DVLOG(1) << "CoreWindow found: " << std::hex << globals.core_window;
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnPointerMoved(winui::Core::ICoreWindow* sender,
                                      winui::Core::IPointerEventArgs* args) {
  PointerInfoHandler pointer;
  HRESULT hr = pointer.Init(args);
  if (FAILED(hr))
    return hr;
  if (!pointer.is_mouse())
    return S_OK;

  ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(pointer.x(),
                                                      pointer.y(),
                                                      0));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnPointerPressed(winui::Core::ICoreWindow* sender,
                                        winui::Core::IPointerEventArgs* args) {
  PointerInfoHandler pointer;
  HRESULT hr = pointer.Init(args);
  if (FAILED(hr))
    return hr;
  if (!pointer.is_mouse())
    return S_OK;

  ui_channel_->Send(new MetroViewerHostMsg_MouseButton(pointer.x(),
                                                       pointer.y(),
                                                       1));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnPointerReleased(winui::Core::ICoreWindow* sender,
                                         winui::Core::IPointerEventArgs* args) {
  PointerInfoHandler pointer;
  HRESULT hr = pointer.Init(args);
  if (FAILED(hr))
    return hr;
  if (!pointer.is_mouse())
    return S_OK;

  ui_channel_->Send(new MetroViewerHostMsg_MouseButton(pointer.x(),
                                                       pointer.y(),
                                                       0));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnKeyDown(winui::Core::ICoreWindow* sender,
                                 winui::Core::IKeyEventArgs* args) {
  winsys::VirtualKey virtual_key;
  HRESULT hr = args->get_VirtualKey(&virtual_key);
  if (FAILED(hr))
    return hr;
  winui::Core::CorePhysicalKeyStatus status;
  hr = args->get_KeyStatus(&status);
  if (FAILED(hr))
    return hr;

  ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
                                                   status.RepeatCount,
                                                   status.ScanCode,
                                                   GetKeyboardEventFlags()));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnKeyUp(winui::Core::ICoreWindow* sender,
                               winui::Core::IKeyEventArgs* args) {
  winsys::VirtualKey virtual_key;
  HRESULT hr = args->get_VirtualKey(&virtual_key);
  if (FAILED(hr))
    return hr;
  winui::Core::CorePhysicalKeyStatus status;
  hr = args->get_KeyStatus(&status);
  if (FAILED(hr))
    return hr;

  ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key,
                                                 status.RepeatCount,
                                                 status.ScanCode,
                                                 GetKeyboardEventFlags()));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnCharacterReceived(
  winui::Core::ICoreWindow* sender,
  winui::Core::ICharacterReceivedEventArgs* args) {
  unsigned int char_code = 0;
  HRESULT hr = args->get_KeyCode(&char_code);
  if (FAILED(hr))
    return hr;

  winui::Core::CorePhysicalKeyStatus status;
  hr = args->get_KeyStatus(&status);
  if (FAILED(hr))
    return hr;

  ui_channel_->Send(new MetroViewerHostMsg_Character(char_code,
                                                     status.RepeatCount,
                                                     status.ScanCode,
                                                     GetKeyboardEventFlags()));
  return S_OK;
}

///////////////////////////////////////////////////////////////////////////////

ChromeAppViewFactory::ChromeAppViewFactory(
    winapp::Core::ICoreApplication* icore_app,
    LPTHREAD_START_ROUTINE host_main,
    void* host_context) {
  mswr::ComPtr<winapp::Core::ICoreApplication> core_app(icore_app);
  mswr::ComPtr<winapp::Core::ICoreApplicationExit> app_exit;
  CheckHR(core_app.As(&app_exit));
  globals.app_exit = app_exit.Detach();
}

IFACEMETHODIMP
ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView** view) {
  *view = mswr::Make<ChromeAppViewAsh>().Detach();
  return (*view) ? S_OK :  E_OUTOFMEMORY;
}
<commit_msg>Allow Ash/Metro to exit cleanly when the last Chrome window is closed.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "win8/metro_driver/stdafx.h"
#include "win8/metro_driver/chrome_app_view_ash.h"

#include <windows.foundation.h>

#include "base/bind.h"
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "base/win/metro.h"
#include "base/win/win_util.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_sender.h"
#include "ui/base/events/event_constants.h"
#include "ui/metro_viewer/metro_viewer_messages.h"
#include "win8/metro_driver/metro_driver.h"
#include "win8/metro_driver/winrt_utils.h"

typedef winfoundtn::ITypedEventHandler<
    winapp::Core::CoreApplicationView*,
    winapp::Activation::IActivatedEventArgs*> ActivatedHandler;

typedef winfoundtn::ITypedEventHandler<
    winui::Core::CoreWindow*,
    winui::Core::PointerEventArgs*> PointerEventHandler;

typedef winfoundtn::ITypedEventHandler<
    winui::Core::CoreWindow*,
    winui::Core::KeyEventArgs*> KeyEventHandler;

typedef winfoundtn::ITypedEventHandler<
    winui::Core::CoreWindow*,
    winui::Core::CharacterReceivedEventArgs*> CharEventHandler;

// This function is exported by chrome.exe.
typedef int (__cdecl *BreakpadExceptionHandler)(EXCEPTION_POINTERS* info);

// Global information used across the metro driver.
struct Globals {
  LPTHREAD_START_ROUTINE host_main;
  HWND core_window;
  DWORD main_thread_id;
  winapp::Activation::ApplicationExecutionState previous_state;
  winapp::Core::ICoreApplicationExit* app_exit;
  BreakpadExceptionHandler breakpad_exception_handler;
} globals;

namespace {

// TODO(robertshield): Share this with chrome_app_view.cc
void MetroExit() {
  globals.app_exit->Exit();
  globals.core_window = NULL;
}

class ChromeChannelListener : public IPC::Listener {
 public:
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
    DVLOG(1) << "Received ipc message " << message.type();
    return true;
  }

  virtual void OnChannelError() OVERRIDE {
    DVLOG(1) << "Channel error";
    MetroExit();
  }

  void Init(IPC::Sender* s) {
    sender_ = s;
  }

 private:
  IPC::Sender* sender_;
};

// This class helps decoding the pointer properties of an event.
class PointerInfoHandler {
 public:
  PointerInfoHandler() : x_(0), y_(0), is_mouse_(false) {};

  HRESULT Init(winui::Core::IPointerEventArgs* args) {
    mswr::ComPtr<winui::Input::IPointerPoint> pointer_point;
    HRESULT hr = args->get_CurrentPoint(&pointer_point);
    if (FAILED(hr))
      return hr;

    mswr::ComPtr<windevs::Input::IPointerDevice> pointer_device;
    hr = pointer_point->get_PointerDevice(&pointer_device);
    if (FAILED(hr))
      return hr;

    windevs::Input::PointerDeviceType device_type;
    hr = pointer_device->get_PointerDeviceType(&device_type);
    if (FAILED(hr))
      return hr;

    is_mouse_ =  (device_type == windevs::Input::PointerDeviceType_Mouse);
    winfoundtn::Point point;
    hr = pointer_point->get_Position(&point);
    if (FAILED(hr))
      return hr;

    x_ = point.X;
    y_ = point.Y;
    return S_OK;
  }

  bool is_mouse() const { return is_mouse_; }
  int x() const { return x_; }
  int y() const { return y_; }

 private:
  int x_;
  int y_;
  bool is_mouse_;
};

void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) {
  // We're entering a nested message loop, let's allow dispatching
  // tasks while we're in there.
  MessageLoop::current()->SetNestableTasksAllowed(true);

  // Enter main core message loop. There are several ways to exit it
  // Nicely:
  // 1 - User action like ALT-F4.
  // 2 - Calling ICoreApplicationExit::Exit().
  // 3-  Posting WM_CLOSE to the core window.
  HRESULT hr = dispatcher->ProcessEvents(
      winui::Core::CoreProcessEventsOption
          ::CoreProcessEventsOption_ProcessUntilQuit);

  // Wind down the thread's chrome message loop.
  MessageLoop::current()->Quit();
}

// Helper to return the state of the shift/control/alt keys.
uint32 GetKeyboardEventFlags() {
  uint32 flags = 0;
  if (base::win::IsShiftPressed())
    flags |= ui::EF_SHIFT_DOWN;
  if (base::win::IsCtrlPressed())
    flags |= ui::EF_CONTROL_DOWN;
  if (base::win::IsAltPressed())
    flags |= ui::EF_ALT_DOWN;
  return flags;
}

}  // namespace

ChromeAppViewAsh::ChromeAppViewAsh()
    : ui_channel_(nullptr),
      ui_channel_listener_(nullptr) {
  globals.previous_state =
      winapp::Activation::ApplicationExecutionState_NotRunning;
}

ChromeAppViewAsh::~ChromeAppViewAsh() {
  DVLOG(1) << __FUNCTION__;
}

IFACEMETHODIMP
ChromeAppViewAsh::Initialize(winapp::Core::ICoreApplicationView* view) {
  view_ = view;
  DVLOG(1) << __FUNCTION__;
  globals.main_thread_id = ::GetCurrentThreadId();

  HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>(
      this, &ChromeAppViewAsh::OnActivate).Get(),
      &activated_token_);
  CheckHR(hr);
  return hr;
}

IFACEMETHODIMP
ChromeAppViewAsh::SetWindow(winui::Core::ICoreWindow* window) {
  window_ = window;
  DVLOG(1) << __FUNCTION__;
  // Register for pointer and keyboard notifications. We forward
  // them to the browser process via IPC.
  HRESULT hr = window_->add_PointerMoved(mswr::Callback<PointerEventHandler>(
      this, &ChromeAppViewAsh::OnPointerMoved).Get(),
      &pointermoved_token_);
  CheckHR(hr);

  hr = window_->add_PointerPressed(mswr::Callback<PointerEventHandler>(
      this, &ChromeAppViewAsh::OnPointerPressed).Get(),
      &pointerpressed_token_);
  CheckHR(hr);

  hr = window_->add_PointerReleased(mswr::Callback<PointerEventHandler>(
      this, &ChromeAppViewAsh::OnPointerReleased).Get(),
      &pointerreleased_token_);
  CheckHR(hr);

  hr = window_->add_KeyDown(mswr::Callback<KeyEventHandler>(
      this, &ChromeAppViewAsh::OnKeyDown).Get(),
      &keydown_token_);
  CheckHR(hr);

  hr = window_->add_KeyUp(mswr::Callback<KeyEventHandler>(
      this, &ChromeAppViewAsh::OnKeyUp).Get(),
      &keyup_token_);
  CheckHR(hr);

  hr = window_->add_CharacterReceived(mswr::Callback<CharEventHandler>(
      this, &ChromeAppViewAsh::OnCharacterReceived).Get(),
      &character_received_token_);
  CheckHR(hr);

  // By initializing the direct 3D swap chain with the corewindow
  // we can now directly blit to it from the browser process.
  direct3d_helper_.Initialize(window);
  DVLOG(1) << "Initialized Direct3D.";
  return S_OK;
}

IFACEMETHODIMP
ChromeAppViewAsh::Load(HSTRING entryPoint) {
  DVLOG(1) << __FUNCTION__;
  return S_OK;
}

IFACEMETHODIMP
ChromeAppViewAsh::Run() {
  DVLOG(1) << __FUNCTION__;
  mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
  HRESULT hr = window_->get_Dispatcher(&dispatcher);
  CheckHR(hr, "Dispatcher failed.");

  hr = window_->Activate();
  if (SUCCEEDED(hr)) {
    // TODO(cpu): Draw something here.
  } else {
    DVLOG(1) << "Activate failed, hr=" << hr;
  }

  // Create a message loop to allow message passing into this thread.
  MessageLoop msg_loop(MessageLoop::TYPE_UI);

  // Create the IPC channel IO thread. It needs to out-live the ChannelProxy.
  base::Thread thread("metro_IO_thread");
  base::Thread::Options options;
  options.message_loop_type = MessageLoop::TYPE_IO;
  thread.StartWithOptions(options);

  // In Aura mode we create an IPC channel to the browser which should
  // be already running.
  ChromeChannelListener ui_channel_listener;
  IPC::ChannelProxy ui_channel("viewer",
                               IPC::Channel::MODE_NAMED_CLIENT,
                               &ui_channel_listener,
                               thread.message_loop_proxy());
  ui_channel_listener.Init(&ui_channel);

  ui_channel_listener_ = &ui_channel_listener;
  ui_channel_ = &ui_channel;

  ui_channel_->Send(new MetroViewerHostMsg_SetTargetSurface(
                    gfx::NativeViewId(globals.core_window)));

  DVLOG(1) << "ICoreWindow sent " << globals.core_window;

  // And post the task that'll do the inner Metro message pumping to it.
  msg_loop.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get()));
  msg_loop.Run();

  DVLOG(0) << "ProcessEvents done, hr=" << hr;
  return hr;
}

IFACEMETHODIMP
ChromeAppViewAsh::Uninitialize() {
  DVLOG(1) << __FUNCTION__;
  window_ = nullptr;
  view_ = nullptr;
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnActivate(winapp::Core::ICoreApplicationView*,
    winapp::Activation::IActivatedEventArgs* args) {
  DVLOG(1) << __FUNCTION__;

  args->get_PreviousExecutionState(&globals.previous_state);
  DVLOG(1) << "Previous Execution State: " << globals.previous_state;

  window_->Activate();

  if (globals.previous_state ==
      winapp::Activation::ApplicationExecutionState_Running) {
    DVLOG(1) << "Already running. Skipping rest of OnActivate.";
    return S_OK;
  }

  globals.core_window =
      winrt_utils::FindCoreWindow(globals.main_thread_id, 10);

  DVLOG(1) << "CoreWindow found: " << std::hex << globals.core_window;
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnPointerMoved(winui::Core::ICoreWindow* sender,
                                      winui::Core::IPointerEventArgs* args) {
  PointerInfoHandler pointer;
  HRESULT hr = pointer.Init(args);
  if (FAILED(hr))
    return hr;
  if (!pointer.is_mouse())
    return S_OK;

  ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(pointer.x(),
                                                      pointer.y(),
                                                      0));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnPointerPressed(winui::Core::ICoreWindow* sender,
                                        winui::Core::IPointerEventArgs* args) {
  PointerInfoHandler pointer;
  HRESULT hr = pointer.Init(args);
  if (FAILED(hr))
    return hr;
  if (!pointer.is_mouse())
    return S_OK;

  ui_channel_->Send(new MetroViewerHostMsg_MouseButton(pointer.x(),
                                                       pointer.y(),
                                                       1));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnPointerReleased(winui::Core::ICoreWindow* sender,
                                         winui::Core::IPointerEventArgs* args) {
  PointerInfoHandler pointer;
  HRESULT hr = pointer.Init(args);
  if (FAILED(hr))
    return hr;
  if (!pointer.is_mouse())
    return S_OK;

  ui_channel_->Send(new MetroViewerHostMsg_MouseButton(pointer.x(),
                                                       pointer.y(),
                                                       0));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnKeyDown(winui::Core::ICoreWindow* sender,
                                 winui::Core::IKeyEventArgs* args) {
  winsys::VirtualKey virtual_key;
  HRESULT hr = args->get_VirtualKey(&virtual_key);
  if (FAILED(hr))
    return hr;
  winui::Core::CorePhysicalKeyStatus status;
  hr = args->get_KeyStatus(&status);
  if (FAILED(hr))
    return hr;

  ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
                                                   status.RepeatCount,
                                                   status.ScanCode,
                                                   GetKeyboardEventFlags()));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnKeyUp(winui::Core::ICoreWindow* sender,
                               winui::Core::IKeyEventArgs* args) {
  winsys::VirtualKey virtual_key;
  HRESULT hr = args->get_VirtualKey(&virtual_key);
  if (FAILED(hr))
    return hr;
  winui::Core::CorePhysicalKeyStatus status;
  hr = args->get_KeyStatus(&status);
  if (FAILED(hr))
    return hr;

  ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key,
                                                 status.RepeatCount,
                                                 status.ScanCode,
                                                 GetKeyboardEventFlags()));
  return S_OK;
}

HRESULT ChromeAppViewAsh::OnCharacterReceived(
  winui::Core::ICoreWindow* sender,
  winui::Core::ICharacterReceivedEventArgs* args) {
  unsigned int char_code = 0;
  HRESULT hr = args->get_KeyCode(&char_code);
  if (FAILED(hr))
    return hr;

  winui::Core::CorePhysicalKeyStatus status;
  hr = args->get_KeyStatus(&status);
  if (FAILED(hr))
    return hr;

  ui_channel_->Send(new MetroViewerHostMsg_Character(char_code,
                                                     status.RepeatCount,
                                                     status.ScanCode,
                                                     GetKeyboardEventFlags()));
  return S_OK;
}

///////////////////////////////////////////////////////////////////////////////

ChromeAppViewFactory::ChromeAppViewFactory(
    winapp::Core::ICoreApplication* icore_app,
    LPTHREAD_START_ROUTINE host_main,
    void* host_context) {
  mswr::ComPtr<winapp::Core::ICoreApplication> core_app(icore_app);
  mswr::ComPtr<winapp::Core::ICoreApplicationExit> app_exit;
  CheckHR(core_app.As(&app_exit));
  globals.app_exit = app_exit.Detach();
}

IFACEMETHODIMP
ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView** view) {
  *view = mswr::Make<ChromeAppViewAsh>().Detach();
  return (*view) ? S_OK :  E_OUTOFMEMORY;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: XMLPageExport.cxx,v $
 *
 *  $Revision: 1.1 $
 *
 *  last change: $Author: mib $ $Date: 2000-10-11 07:49:38 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif

#ifndef _XMLOFF_XMLNMSPE_HXX
#include "xmlnmspe.hxx"
#endif
#ifndef _XMLOFF_XMLKYWD_HXX
#include "xmlkywd.hxx"
#endif

#ifndef _COM_SUN_STAR_STYLE_XSTYLEFAMILIESSUPPLIER_HPP_
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#endif
#ifndef _COM_SUN_STAR_STYLE_XSTYLE_HPP_
#include <com/sun/star/style/XStyle.hpp>
#endif

#ifndef _COM_SUN_STAR_CONTAINER_XNAMECONTAINER_HPP_
#include <com/sun/star/container/XNameContainer.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XINDEXREPLACE_HPP_
#include <com/sun/star/container/XIndexReplace.hpp>
#endif

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif

#ifndef _XMLOFF_FAMILIES_HXX_
#include "families.hxx"
#endif
#ifndef _XMLOFF_XMLEXP_HXX
#include "xmlexp.hxx"
#endif

#ifndef _XMLOFF_XMLPAGEEXPORT_HXX
#include "XMLPageExport.hxx"
#endif

using namespace ::rtl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::beans;

void XMLPageExport::collectPageMasterAutoStyle(
            const Reference < XPropertySet > & rPropSet )
{
}

void XMLPageExport::exportMasterPageContent(
                const Reference < XPropertySet > & rPropSet,
                sal_Bool bAutoStyles )
{
}

sal_Bool XMLPageExport::exportStyle(
            const Reference< XStyle >& rStyle,
            sal_Bool bAutoStyles )
{
    Reference< XPropertySet > xPropSet( rStyle, UNO_QUERY );
    Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();

    Any aAny;

    // Don't export styles that aren't existing really. This may be the
    // case for StarOffice Writer's pool styles.
    if( xPropSetInfo->hasPropertyByName( sIsPhysical ) )
    {
        aAny = xPropSet->getPropertyValue( sIsPhysical );
        if( !*(sal_Bool *)aAny.getValue() )
            return sal_False;
    }

    if( bAutoStyles )
    {
        collectPageMasterAutoStyle( xPropSet );
        exportMasterPageContent( xPropSet, sal_True );
    }
    else
    {
        OUString sName( rStyle->getName() );
        GetExport().AddAttribute( XML_NAMESPACE_STYLE, sXML_name, sName );

        aAny = xPropSet->getPropertyValue( sFollowStyle );
        OUString sNextName;
        aAny >>= sNextName;
        if( sName != sNextName )
        {
            GetExport().AddAttribute( XML_NAMESPACE_STYLE, sXML_next_style_name,
                          sNextName );
        }
//      OUString sPageMaster = GetExport().GetAutoStylePool()->Find(
//                                          XML_STYLE_FAMILY_PAGE_MASTER,
//                                          xPropSet );
//      if( sPageMaster.getLength() )
//          GetExport().AddAttribute( XML_NAMESPACE_STYLE,
//                                    sXML_page_master_name,
//                                    sPageMaster );

        SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_STYLE,
                                  sXML_master_page, sal_True, sal_True );

        exportMasterPageContent( xPropSet, sal_False );
    }

    return sal_True;
}

XMLPageExport::XMLPageExport( SvXMLExport& rExp ) :
    rExport( rExp ),
    sIsPhysical( RTL_CONSTASCII_USTRINGPARAM( "IsPhysical" ) ),
    sFollowStyle( RTL_CONSTASCII_USTRINGPARAM( "FollowStyle" ) )
{
    Reference< XStyleFamiliesSupplier > xFamiliesSupp( GetExport().GetModel(),
                                                       UNO_QUERY );
    DBG_ASSERT( xFamiliesSupp.is(),
                "No XStyleFamiliesSupplier from XModel for export!" );
    if( xFamiliesSupp.is() )
    {
        Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() );
        DBG_ASSERT( xFamiliesSupp.is(),
                    "getStyleFamilies() from XModel failed for export!" );
        if( xFamilies.is() )
        {
            const OUString aPageStyleName(
                        RTL_CONSTASCII_USTRINGPARAM( "PageStyles" ));

            if( xFamilies->hasByName( aPageStyleName ) )
            {
                Reference < XNameContainer > xStyleCont;
                xFamilies->getByName( aPageStyleName ) >>= xStyleCont;

                xPageStyles = Reference< XIndexAccess >( xStyleCont,
                                                          UNO_QUERY );

                DBG_ASSERT( xPageStyles.is(),
                            "Page Styles not found for export!" );
            }
        }
    }
}

XMLPageExport::~XMLPageExport()
{
}

void XMLPageExport::exportStyles( sal_Bool bUsed, sal_Bool bAutoStyles )
{
    if( xPageStyles.is() )
    {
        const sal_Int32 nStyles = xPageStyles->getCount();

        for( sal_Int32 i=0; i < nStyles; i++ )
        {
            Reference< XStyle > xStyle;
            xPageStyles->getByIndex( i ) >>= xStyle;

            if( !bUsed || xStyle->isInUse() )
                exportStyle( xStyle, bAutoStyles );
        }
    }
}

<commit_msg>add: page-master export<commit_after>/*************************************************************************
 *
 *  $RCSfile: XMLPageExport.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: dr $ $Date: 2000-10-18 11:32:20 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _XMLOFF_XMLPAGEEXPORT_HXX
#include "XMLPageExport.hxx"
#endif

#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif

#ifndef _XMLOFF_XMLNMSPE_HXX
#include "xmlnmspe.hxx"
#endif
#ifndef _XMLOFF_XMLKYWD_HXX
#include "xmlkywd.hxx"
#endif

#ifndef _COM_SUN_STAR_STYLE_XSTYLEFAMILIESSUPPLIER_HPP_
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#endif
#ifndef _COM_SUN_STAR_STYLE_XSTYLE_HPP_
#include <com/sun/star/style/XStyle.hpp>
#endif

#ifndef _COM_SUN_STAR_CONTAINER_XNAMECONTAINER_HPP_
#include <com/sun/star/container/XNameContainer.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XINDEXREPLACE_HPP_
#include <com/sun/star/container/XIndexReplace.hpp>
#endif

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif

#ifndef _XMLOFF_FAMILIES_HXX_
#include "families.hxx"
#endif
#ifndef _XMLOFF_XMLEXP_HXX
#include "xmlexp.hxx"
#endif
#ifndef _XMLOFF_PAGEMASTERSTYLEMAP_HXX
#include "PageMasterStyleMap.hxx"
#endif
#ifndef _XMLOFF_PAGEMASTERPROPMAPPER_HXX
#include "PageMasterPropMapper.hxx"
#endif

using namespace ::rtl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::beans;

void XMLPageExport::collectPageMasterAutoStyle(
            const Reference < XPropertySet > & rPropSet )
{
    std::vector<XMLPropertyState> xPropStates = rExport.GetPageMasterPropSetMapper()->Filter( rPropSet );
    if(xPropStates.size())
    {
        rtl::OUString sParent;
        rtl::OUString sName( rExport.GetAutoStylePool()->Find( XML_STYLE_FAMILY_PAGE_MASTER, sParent, xPropStates) );
        if (!sName.getLength())
        {
            sName = rExport.GetAutoStylePool()->Add(XML_STYLE_FAMILY_PAGE_MASTER, sParent, xPropStates);
        }
    }
}

void XMLPageExport::exportMasterPageContent(
                const Reference < XPropertySet > & rPropSet,
                sal_Bool bAutoStyles )
{

}

sal_Bool XMLPageExport::exportStyle(
            const Reference< XStyle >& rStyle,
            sal_Bool bAutoStyles )
{
    Reference< XPropertySet > xPropSet( rStyle, UNO_QUERY );
    Reference< XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();

    Any aAny;

    // Don't export styles that aren't existing really. This may be the
    // case for StarOffice Writer's pool styles.
    if( xPropSetInfo->hasPropertyByName( sIsPhysical ) )
    {
        aAny = xPropSet->getPropertyValue( sIsPhysical );
        if( !*(sal_Bool *)aAny.getValue() )
            return sal_False;
    }

    if( bAutoStyles )
    {
        collectPageMasterAutoStyle( xPropSet );
        exportMasterPageContent( xPropSet, sal_True );
    }
    else
    {
        OUString sName( rStyle->getName() );
        GetExport().AddAttribute( XML_NAMESPACE_STYLE, sXML_name, sName );

        aAny = xPropSet->getPropertyValue( sFollowStyle );
        OUString sNextName;
        aAny >>= sNextName;
        if( sName != sNextName )
        {
            GetExport().AddAttribute( XML_NAMESPACE_STYLE, sXML_next_style_name,
                          sNextName );
        }
//      OUString sPageMaster = GetExport().GetAutoStylePool()->Find(
//                                          XML_STYLE_FAMILY_PAGE_MASTER,
//                                          xPropSet );
//      if( sPageMaster.getLength() )
//          GetExport().AddAttribute( XML_NAMESPACE_STYLE,
//                                    sXML_page_master_name,
//                                    sPageMaster );

        SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_STYLE,
                                  sXML_master_page, sal_True, sal_True );

        exportMasterPageContent( xPropSet, sal_False );
    }

    return sal_True;
}

XMLPageExport::XMLPageExport( SvXMLExport& rExp ) :
    rExport( rExp ),
    sIsPhysical( RTL_CONSTASCII_USTRINGPARAM( "IsPhysical" ) ),
    sFollowStyle( RTL_CONSTASCII_USTRINGPARAM( "FollowStyle" ) )
{
    Reference< XStyleFamiliesSupplier > xFamiliesSupp( GetExport().GetModel(),
                                                       UNO_QUERY );
    DBG_ASSERT( xFamiliesSupp.is(),
                "No XStyleFamiliesSupplier from XModel for export!" );
    if( xFamiliesSupp.is() )
    {
        Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() );
        DBG_ASSERT( xFamiliesSupp.is(),
                    "getStyleFamilies() from XModel failed for export!" );
        if( xFamilies.is() )
        {
            const OUString aPageStyleName(
                        RTL_CONSTASCII_USTRINGPARAM( "PageStyles" ));

            if( xFamilies->hasByName( aPageStyleName ) )
            {
                Reference < XNameContainer > xStyleCont;
                xFamilies->getByName( aPageStyleName ) >>= xStyleCont;

                xPageStyles = Reference< XIndexAccess >( xStyleCont,
                                                          UNO_QUERY );

                DBG_ASSERT( xPageStyles.is(),
                            "Page Styles not found for export!" );
            }
        }
    }
}

XMLPageExport::~XMLPageExport()
{
}

void XMLPageExport::exportStyles( sal_Bool bUsed, sal_Bool bAutoStyles )
{
    if( xPageStyles.is() )
    {
        const sal_Int32 nStyles = xPageStyles->getCount();

        for( sal_Int32 i=0; i < nStyles; i++ )
        {
            Reference< XStyle > xStyle;
            xPageStyles->getByIndex( i ) >>= xStyle;

            if( !bUsed || xStyle->isInUse() )
                exportStyle( xStyle, bAutoStyles );
        }
    }
}

<|endoftext|>


<commit_before>int main()
<commit_msg>修改1.cpp，添加一些头文件<commit_after>#include <iostream>

using namespace std;

int main()
{
	return 0;
}
<|endoftext|>


<commit_before>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#include "core/Setup.h"

#if OUZEL_COMPILE_COREAUDIO

#include <system_error>
#include "CAAudioDevice.hpp"
#include "core/Engine.hpp"
#include "utils/Log.hpp"

#if TARGET_OS_IOS || TARGET_OS_TV
#  include <objc/message.h>
extern "C" id const AVAudioSessionCategoryAmbient;
#elif TARGET_OS_MAC
static OSStatus deviceListChanged(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void*)
{
    // TODO: implement
    return 0;
}

static OSStatus deviceUnplugged(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void*)
{
    // TODO: implement
    return noErr;
}
#endif

static OSStatus outputCallback(void* inRefCon,
                               AudioUnitRenderActionFlags*,
                               const AudioTimeStamp*,
                               UInt32, UInt32,
                               AudioBufferList* ioData)
{
    ouzel::audio::CAAudioDevice* audioDeviceCA = static_cast<ouzel::audio::CAAudioDevice*>(inRefCon);

    try
    {
        audioDeviceCA->outputCallback(ioData);
    }
    catch (const std::exception& e)
    {
        ouzel::engine->log(ouzel::Log::Level::ERR) << e.what();
        return -1;
    }

    return noErr;
}

namespace ouzel
{
    namespace audio
    {
        class CoreAudioErrorCategory final: public std::error_category
        {
        public:
            const char* name() const noexcept override
            {
                return "CoreAudio";
            }

            std::string message(int condition) const override
            {
                switch (condition)
                {
                    case kAudio_UnimplementedError: return "kAudio_UnimplementedError";
                    case kAudio_FileNotFoundError: return "kAudio_FileNotFoundError";
                    case kAudio_FilePermissionError: return "kAudio_FilePermissionError";
                    case kAudio_TooManyFilesOpenError: return "kAudio_TooManyFilesOpenError";
                    case kAudio_BadFilePathError: return "kAudio_BadFilePathError";
                    case kAudio_ParamError: return "kAudio_ParamError";
                    case kAudio_MemFullError: return "kAudio_MemFullError";
                    default: return "Unknown error (" + std::to_string(condition) + ")";
                }
            }
        };

        const CoreAudioErrorCategory coreAudioErrorCategory {};

        CAAudioDevice::CAAudioDevice(const std::function<void(uint32_t frames, uint16_t channels, uint32_t sampleRate, std::vector<float>& samples)>& initDataGetter):
            AudioDevice(Driver::COREAUDIO, initDataGetter)
        {
            OSStatus result;

#if TARGET_OS_IOS || TARGET_OS_TV
            id audioSession = reinterpret_cast<id (*)(Class, SEL)>(&objc_msgSend)(objc_getClass("AVAudioSession"), sel_getUid("sharedInstance"));
            reinterpret_cast<BOOL (*)(id, SEL, id, id)>(&objc_msgSend)(audioSession, sel_getUid("setCategory:error:"), AVAudioSessionCategoryAmbient, nil);
#elif TARGET_OS_MAC
            static constexpr AudioObjectPropertyAddress deviceListAddress = {
                kAudioHardwarePropertyDevices,
                kAudioObjectPropertyScopeGlobal,
                kAudioObjectPropertyElementMaster
            };

            if ((result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &deviceListAddress, deviceListChanged, this)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to add CoreAudio property listener");

            static constexpr AudioObjectPropertyAddress defaultDeviceAddress = {
                kAudioHardwarePropertyDefaultOutputDevice,
                kAudioObjectPropertyScopeGlobal,
                kAudioObjectPropertyElementMaster
            };

            UInt32 size = sizeof(AudioDeviceID);
            if ((result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress,
                                                     0, nullptr, &size, &deviceId)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to get CoreAudio output device");

            static constexpr AudioObjectPropertyAddress aliveAddress = {
                kAudioDevicePropertyDeviceIsAlive,
                kAudioDevicePropertyScopeOutput,
                kAudioObjectPropertyElementMaster
            };

            UInt32 alive = 0;
            size = sizeof(alive);
            if ((result = AudioObjectGetPropertyData(deviceId, &aliveAddress, 0, nullptr, &size, &alive)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to get CoreAudio device status");

            if (!alive)
                throw std::system_error(result, coreAudioErrorCategory, "Requested CoreAudio device is not alive");

            static constexpr AudioObjectPropertyAddress hogModeAddress = {
                kAudioDevicePropertyHogMode,
                kAudioDevicePropertyScopeOutput,
                kAudioObjectPropertyElementMaster
            };

            pid_t pid = 0;
            size = sizeof(pid);
            if ((result = AudioObjectGetPropertyData(deviceId, &hogModeAddress, 0, nullptr, &size, &pid)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to check if CoreAudio device is in hog mode");

            if (pid != -1)
                throw std::runtime_error("Requested CoreAudio device is being hogged");

            static constexpr AudioObjectPropertyAddress nameAddress = {
                kAudioObjectPropertyName,
                kAudioDevicePropertyScopeOutput,
                kAudioObjectPropertyElementMaster
            };

            CFStringRef tempStringRef = nullptr;
            size = sizeof(CFStringRef);

            if ((result = AudioObjectGetPropertyData(deviceId, &nameAddress,
                                                     0, nullptr, &size, &tempStringRef)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to get CoreAudio device name");

            if (tempStringRef)
            {
                std::string name;
                if (const char* deviceName = CFStringGetCStringPtr(tempStringRef, kCFStringEncodingUTF8))
                    name = deviceName;
                else
                {
                    CFIndex stringLength = CFStringGetLength(tempStringRef);
                    std::vector<char> temp(static_cast<size_t>(CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8)) + 1);
                    if (CFStringGetCString(tempStringRef, temp.data(), static_cast<CFIndex>(temp.size()), kCFStringEncodingUTF8))
                        for (auto i = temp.begin(); i != temp.end() && *i; ++i)
                            name.push_back(*i);
                }
                CFRelease(tempStringRef);

                engine->log(Log::Level::INFO) << "Using " << name << " for audio";
            }
#endif

            AudioComponentDescription desc;
            desc.componentType = kAudioUnitType_Output;

#if TARGET_OS_IOS || TARGET_OS_TV
            desc.componentSubType = kAudioUnitSubType_RemoteIO;
#elif TARGET_OS_MAC
            desc.componentSubType = kAudioUnitSubType_DefaultOutput;
#endif

            desc.componentManufacturer = kAudioUnitManufacturer_Apple;
            desc.componentFlags = 0;
            desc.componentFlagsMask = 0;

            audioComponent = AudioComponentFindNext(nullptr, &desc);

            if (!audioComponent)
                throw std::runtime_error("Failed to find requested CoreAudio component");

#if TARGET_OS_MAC && !TARGET_OS_IOS && !TARGET_OS_TV
            if ((result = AudioObjectAddPropertyListener(deviceId, &aliveAddress, deviceUnplugged, this)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to add CoreAudio property listener");
#endif

            if ((result = AudioComponentInstanceNew(audioComponent, &audioUnit)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to create CoreAudio component instance");

#if TARGET_OS_MAC && !TARGET_OS_IOS && !TARGET_OS_TV
            if ((result = AudioUnitSetProperty(audioUnit,
                                               kAudioOutputUnitProperty_CurrentDevice,
                                               kAudioUnitScope_Global, 0,
                                               &deviceId,
                                               sizeof(AudioDeviceID))) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to set CoreAudio unit property");
#endif

            constexpr AudioUnitElement bus = 0;

            AudioStreamBasicDescription streamDescription;
            streamDescription.mSampleRate = sampleRate;
            streamDescription.mFormatID = kAudioFormatLinearPCM;
            streamDescription.mFormatFlags = kLinearPCMFormatFlagIsFloat;
            streamDescription.mChannelsPerFrame = channels;
            streamDescription.mFramesPerPacket = 1;
            streamDescription.mBitsPerChannel = sizeof(float) * 8;
            streamDescription.mBytesPerFrame = streamDescription.mBitsPerChannel * streamDescription.mChannelsPerFrame / 8;
            streamDescription.mBytesPerPacket = streamDescription.mBytesPerFrame * streamDescription.mFramesPerPacket;
            streamDescription.mReserved = 0;

            sampleFormat = SampleFormat::FLOAT32;
            sampleSize = sizeof(float);

            if ((result = AudioUnitSetProperty(audioUnit,
                                               kAudioUnitProperty_StreamFormat,
                                               kAudioUnitScope_Input, bus, &streamDescription, sizeof(streamDescription))) != noErr)
            {
                engine->log(Log::Level::WARN) << "Failed to set CoreAudio unit stream format to float, error: " << result;

                streamDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
                streamDescription.mBitsPerChannel = sizeof(int16_t) * 8;

                if ((result = AudioUnitSetProperty(audioUnit,
                                                   kAudioUnitProperty_StreamFormat,
                                                   kAudioUnitScope_Input, bus, &streamDescription, sizeof(streamDescription))) != noErr)
                    throw std::system_error(result, coreAudioErrorCategory, "Failed to set CoreAudio unit stream format");

                sampleFormat = SampleFormat::SINT16;
                sampleSize = sizeof(int16_t);
            }

            AURenderCallbackStruct callback;
            callback.inputProc = ::outputCallback;
            callback.inputProcRefCon = this;
            if ((result = AudioUnitSetProperty(audioUnit,
                                               kAudioUnitProperty_SetRenderCallback,
                                               kAudioUnitScope_Input, bus, &callback, sizeof(callback))) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to set CoreAudio unit output callback");

            if ((result = AudioUnitInitialize(audioUnit)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to initialize CoreAudio unit");

            if ((result = AudioOutputUnitStart(audioUnit)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to start CoreAudio output unit");
        }

        CAAudioDevice::~CAAudioDevice()
        {
            if (audioUnit)
            {
                AudioOutputUnitStop(audioUnit);

                AURenderCallbackStruct callback;
                callback.inputProc = nullptr;
                callback.inputProcRefCon = nullptr;

                constexpr AudioUnitElement bus = 0;

                AudioUnitSetProperty(audioUnit,
                                     kAudioUnitProperty_SetRenderCallback,
                                     kAudioUnitScope_Input, bus, &callback, sizeof(callback));

                AudioComponentInstanceDispose(audioUnit);
            }

#if TARGET_OS_MAC && !TARGET_OS_IOS && !TARGET_OS_TV
            static constexpr AudioObjectPropertyAddress deviceListAddress = {
                kAudioHardwarePropertyDevices,
                kAudioObjectPropertyScopeGlobal,
                kAudioObjectPropertyElementMaster
            };

            AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &deviceListAddress, deviceListChanged, this);

            if (deviceId)
            {
                static constexpr AudioObjectPropertyAddress aliveAddress = {
                    kAudioDevicePropertyDeviceIsAlive,
                    kAudioDevicePropertyScopeOutput,
                    kAudioObjectPropertyElementMaster
                };

                AudioObjectRemovePropertyListener(deviceId, &aliveAddress, deviceUnplugged, this);
            }
#endif
        }

        void CAAudioDevice::outputCallback(AudioBufferList* ioData)
        {
            for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i)
            {
                AudioBuffer* buffer = &ioData->mBuffers[i];

                getData(buffer->mDataByteSize / (sampleSize * channels), data);

                std::copy(data.begin(), data.end(), static_cast<uint8_t*>(buffer->mData));
            }
        }
    } // namespace audio
} // namespace ouzel

#endif
<commit_msg>Set CoreAudio buffer size manually<commit_after>// Copyright 2015-2019 Elviss Strazdins. All rights reserved.

#include "core/Setup.h"

#if OUZEL_COMPILE_COREAUDIO

#include <system_error>
#include "CAAudioDevice.hpp"
#include "core/Engine.hpp"
#include "utils/Log.hpp"

#if TARGET_OS_IOS || TARGET_OS_TV
#  include <objc/message.h>
extern "C" id const AVAudioSessionCategoryAmbient;
#elif TARGET_OS_MAC
static OSStatus deviceListChanged(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void*)
{
    // TODO: implement
    return 0;
}

static OSStatus deviceUnplugged(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void*)
{
    // TODO: implement
    return noErr;
}
#endif

static OSStatus outputCallback(void* inRefCon,
                               AudioUnitRenderActionFlags*,
                               const AudioTimeStamp*,
                               UInt32, UInt32,
                               AudioBufferList* ioData)
{
    ouzel::audio::CAAudioDevice* audioDeviceCA = static_cast<ouzel::audio::CAAudioDevice*>(inRefCon);

    try
    {
        audioDeviceCA->outputCallback(ioData);
    }
    catch (const std::exception& e)
    {
        ouzel::engine->log(ouzel::Log::Level::ERR) << e.what();
        return -1;
    }

    return noErr;
}

namespace ouzel
{
    namespace audio
    {
        class CoreAudioErrorCategory final: public std::error_category
        {
        public:
            const char* name() const noexcept override
            {
                return "CoreAudio";
            }

            std::string message(int condition) const override
            {
                switch (condition)
                {
                    case kAudio_UnimplementedError: return "kAudio_UnimplementedError";
                    case kAudio_FileNotFoundError: return "kAudio_FileNotFoundError";
                    case kAudio_FilePermissionError: return "kAudio_FilePermissionError";
                    case kAudio_TooManyFilesOpenError: return "kAudio_TooManyFilesOpenError";
                    case kAudio_BadFilePathError: return "kAudio_BadFilePathError";
                    case kAudio_ParamError: return "kAudio_ParamError";
                    case kAudio_MemFullError: return "kAudio_MemFullError";
                    default: return "Unknown error (" + std::to_string(condition) + ")";
                }
            }
        };

        const CoreAudioErrorCategory coreAudioErrorCategory {};

        CAAudioDevice::CAAudioDevice(const std::function<void(uint32_t frames, uint16_t channels, uint32_t sampleRate, std::vector<float>& samples)>& initDataGetter):
            AudioDevice(Driver::COREAUDIO, initDataGetter)
        {
            OSStatus result;

#if TARGET_OS_IOS || TARGET_OS_TV
            id audioSession = reinterpret_cast<id (*)(Class, SEL)>(&objc_msgSend)(objc_getClass("AVAudioSession"), sel_getUid("sharedInstance"));
            reinterpret_cast<BOOL (*)(id, SEL, id, id)>(&objc_msgSend)(audioSession, sel_getUid("setCategory:error:"), AVAudioSessionCategoryAmbient, nil);
#elif TARGET_OS_MAC
            static constexpr AudioObjectPropertyAddress deviceListAddress = {
                kAudioHardwarePropertyDevices,
                kAudioObjectPropertyScopeGlobal,
                kAudioObjectPropertyElementMaster
            };

            if ((result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &deviceListAddress, deviceListChanged, this)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to add CoreAudio property listener");

            static constexpr AudioObjectPropertyAddress defaultDeviceAddress = {
                kAudioHardwarePropertyDefaultOutputDevice,
                kAudioObjectPropertyScopeGlobal,
                kAudioObjectPropertyElementMaster
            };

            UInt32 size = sizeof(AudioDeviceID);
            if ((result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultDeviceAddress,
                                                     0, nullptr, &size, &deviceId)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to get CoreAudio output device");

            static constexpr AudioObjectPropertyAddress aliveAddress = {
                kAudioDevicePropertyDeviceIsAlive,
                kAudioDevicePropertyScopeOutput,
                kAudioObjectPropertyElementMaster
            };

            UInt32 alive = 0;
            size = sizeof(alive);
            if ((result = AudioObjectGetPropertyData(deviceId, &aliveAddress, 0, nullptr, &size, &alive)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to get CoreAudio device status");

            if (!alive)
                throw std::system_error(result, coreAudioErrorCategory, "Requested CoreAudio device is not alive");

            static constexpr AudioObjectPropertyAddress hogModeAddress = {
                kAudioDevicePropertyHogMode,
                kAudioDevicePropertyScopeOutput,
                kAudioObjectPropertyElementMaster
            };

            pid_t pid = 0;
            size = sizeof(pid);
            if ((result = AudioObjectGetPropertyData(deviceId, &hogModeAddress, 0, nullptr, &size, &pid)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to check if CoreAudio device is in hog mode");

            if (pid != -1)
                throw std::runtime_error("Requested CoreAudio device is being hogged");

            static constexpr AudioObjectPropertyAddress nameAddress = {
                kAudioObjectPropertyName,
                kAudioDevicePropertyScopeOutput,
                kAudioObjectPropertyElementMaster
            };

            CFStringRef tempStringRef = nullptr;
            size = sizeof(CFStringRef);

            if ((result = AudioObjectGetPropertyData(deviceId, &nameAddress,
                                                     0, nullptr, &size, &tempStringRef)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to get CoreAudio device name");

            if (tempStringRef)
            {
                std::string name;
                if (const char* deviceName = CFStringGetCStringPtr(tempStringRef, kCFStringEncodingUTF8))
                    name = deviceName;
                else
                {
                    CFIndex stringLength = CFStringGetLength(tempStringRef);
                    std::vector<char> temp(static_cast<size_t>(CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8)) + 1);
                    if (CFStringGetCString(tempStringRef, temp.data(), static_cast<CFIndex>(temp.size()), kCFStringEncodingUTF8))
                        for (auto i = temp.begin(); i != temp.end() && *i; ++i)
                            name.push_back(*i);
                }
                CFRelease(tempStringRef);

                engine->log(Log::Level::INFO) << "Using " << name << " for audio";
            }
#endif

            AudioComponentDescription desc;
            desc.componentType = kAudioUnitType_Output;

#if TARGET_OS_IOS || TARGET_OS_TV
            desc.componentSubType = kAudioUnitSubType_RemoteIO;
#elif TARGET_OS_MAC
            desc.componentSubType = kAudioUnitSubType_DefaultOutput;
#endif

            desc.componentManufacturer = kAudioUnitManufacturer_Apple;
            desc.componentFlags = 0;
            desc.componentFlagsMask = 0;

            audioComponent = AudioComponentFindNext(nullptr, &desc);

            if (!audioComponent)
                throw std::runtime_error("Failed to find requested CoreAudio component");

#if TARGET_OS_MAC && !TARGET_OS_IOS && !TARGET_OS_TV
            if ((result = AudioObjectAddPropertyListener(deviceId, &aliveAddress, deviceUnplugged, this)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to add CoreAudio property listener");
#endif

            if ((result = AudioComponentInstanceNew(audioComponent, &audioUnit)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to create CoreAudio component instance");

#if TARGET_OS_MAC && !TARGET_OS_IOS && !TARGET_OS_TV
            if ((result = AudioUnitSetProperty(audioUnit,
                                               kAudioOutputUnitProperty_CurrentDevice,
                                               kAudioUnitScope_Global, 0,
                                               &deviceId,
                                               sizeof(AudioDeviceID))) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to set CoreAudio unit property");
#endif

            constexpr AudioUnitElement bus = 0;

            AudioStreamBasicDescription streamDescription;
            streamDescription.mSampleRate = sampleRate;
            streamDescription.mFormatID = kAudioFormatLinearPCM;
            streamDescription.mFormatFlags = kLinearPCMFormatFlagIsFloat;
            streamDescription.mChannelsPerFrame = channels;
            streamDescription.mFramesPerPacket = 1;
            streamDescription.mBitsPerChannel = sizeof(float) * 8;
            streamDescription.mBytesPerFrame = streamDescription.mBitsPerChannel * streamDescription.mChannelsPerFrame / 8;
            streamDescription.mBytesPerPacket = streamDescription.mBytesPerFrame * streamDescription.mFramesPerPacket;
            streamDescription.mReserved = 0;

            sampleFormat = SampleFormat::FLOAT32;
            sampleSize = sizeof(float);

            if ((result = AudioUnitSetProperty(audioUnit,
                                               kAudioUnitProperty_StreamFormat,
                                               kAudioUnitScope_Input, bus, &streamDescription, sizeof(streamDescription))) != noErr)
            {
                engine->log(Log::Level::WARN) << "Failed to set CoreAudio unit stream format to float, error: " << result;

                streamDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
                streamDescription.mBitsPerChannel = sizeof(int16_t) * 8;

                if ((result = AudioUnitSetProperty(audioUnit,
                                                   kAudioUnitProperty_StreamFormat,
                                                   kAudioUnitScope_Input, bus, &streamDescription, sizeof(streamDescription))) != noErr)
                    throw std::system_error(result, coreAudioErrorCategory, "Failed to set CoreAudio unit stream format");

                sampleFormat = SampleFormat::SINT16;
                sampleSize = sizeof(int16_t);
            }

            AURenderCallbackStruct callback;
            callback.inputProc = ::outputCallback;
            callback.inputProcRefCon = this;
            if ((result = AudioUnitSetProperty(audioUnit,
                                               kAudioUnitProperty_SetRenderCallback,
                                               kAudioUnitScope_Input, bus, &callback, sizeof(callback))) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to set CoreAudio unit output callback");

            UInt32 inIOBufferFrameSize = 512;
            if ((result = AudioUnitSetProperty(audioUnit,
                                               kAudioDevicePropertyBufferFrameSize,
                                               kAudioUnitScope_Global,
                                               0,
                                               &inIOBufferFrameSize, sizeof(UInt32))) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to set CoreAudio buffer size");

            if ((result = AudioUnitInitialize(audioUnit)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to initialize CoreAudio unit");

            if ((result = AudioOutputUnitStart(audioUnit)) != noErr)
                throw std::system_error(result, coreAudioErrorCategory, "Failed to start CoreAudio output unit");
        }

        CAAudioDevice::~CAAudioDevice()
        {
            if (audioUnit)
            {
                AudioOutputUnitStop(audioUnit);

                AURenderCallbackStruct callback;
                callback.inputProc = nullptr;
                callback.inputProcRefCon = nullptr;

                constexpr AudioUnitElement bus = 0;

                AudioUnitSetProperty(audioUnit,
                                     kAudioUnitProperty_SetRenderCallback,
                                     kAudioUnitScope_Input, bus, &callback, sizeof(callback));

                AudioComponentInstanceDispose(audioUnit);
            }

#if TARGET_OS_MAC && !TARGET_OS_IOS && !TARGET_OS_TV
            static constexpr AudioObjectPropertyAddress deviceListAddress = {
                kAudioHardwarePropertyDevices,
                kAudioObjectPropertyScopeGlobal,
                kAudioObjectPropertyElementMaster
            };

            AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &deviceListAddress, deviceListChanged, this);

            if (deviceId)
            {
                static constexpr AudioObjectPropertyAddress aliveAddress = {
                    kAudioDevicePropertyDeviceIsAlive,
                    kAudioDevicePropertyScopeOutput,
                    kAudioObjectPropertyElementMaster
                };

                AudioObjectRemovePropertyListener(deviceId, &aliveAddress, deviceUnplugged, this);
            }
#endif
        }

        void CAAudioDevice::outputCallback(AudioBufferList* ioData)
        {
            for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i)
            {
                AudioBuffer& buffer = ioData->mBuffers[i];
                getData(buffer.mDataByteSize / (sampleSize * channels), data);
                std::copy(data.begin(), data.end(), static_cast<uint8_t*>(buffer.mData));
            }
        }
    } // namespace audio
} // namespace ouzel

#endif
<|endoftext|>


<commit_before>// Copyright © 2013 the Search Authors under the MIT license. See AUTHORS for the list of authors.
#pragma once

#include <cstdio>
#include <vector>
#include <signal.h>

#include "../structs/intpq.hpp"
#include "../structs/binheap.hpp"

#include "closedlist.hpp"

double walltime();
double cputime();
void dfpair(FILE *f, const char *key, const char *fmt, ...);

// A SearchStats structure contains statistical information
// collected during a run of a search algorithm.
class SearchStats {
public:
	double wallstart, cpustart;
	double wallend, cpuend;
	unsigned long expd, gend, reopnd, dups;

	SearchStats();

	// start must be called by the search in order to
	// begin collecting statistical information.
	void start();

	// finish must be called by the search in order to
	// stop collecting information.
	void finish();

	// output prints the statistical information to the given
	// file in datafile format.
	void output(FILE*);

	// add accumulated the statistical information from
	// the given search statistics in the receiver.
	void add(SearchStats &other) {
		expd += other.expd;
		gend += other.gend;
		reopnd += other.reopnd;
		dups += other.dups;

		double wt = wallend - wallstart;
		double ct = cpuend - cpustart;
		wallstart = cpustart = 0;
		wallend = wt + (other.wallend - other.wallstart);
		cpuend = ct + (other.cpuend - other.cpustart);
	}
};

// A Limit structure contains information about when a
// search algorithm should be stopped because it has
// hit a user specified limit.
class Limit {
public:

	Limit();

	Limit(int, const char*[]);

	// reached returns true when the given statistics
	// reports that the given limit has been reached.
	bool reached(SearchStats &r) {
		return timeup ||
			(expd > 0 && r.expd >= expd) ||
			(gend > 0 && r.gend >= gend);
	}

	// output prints the limit to the given file in datafile
	// format.
	void output(FILE*);

	// start must be called at the beginning of a search
	// that would like to use the limit.
	void start();

	// finish must be called when a search, using the limit
	// has finished.
	void finish();

	unsigned long expd, gend, mem, cputime, walltime;
	volatile sig_atomic_t timeup;
private:
	void memlimit(const char*);
	void timelimit(const char*);
};

// An OpenList holds nodes and returns them ordered by some
// priority.  The Ops class has a pred method which accepts
// two Nodes and returns true if the 1st node is a predecessor
// of the second.
template <class Ops, class Node, class Cost>
class OpenList {
public:
	const char *kind() {
		return "binary heap";
	}

	void push(Node *n) {
		heap.push(n);
	}

	Node *pop() {
		boost::optional<Node*> p = heap.pop();
		if (!p)
			return NULL;
		return *p;
	}

	void pre_update(Node *n) {
	}

	void post_update(Node *n) {
		if (Ops::getind(n) < 0)
			heap.push(n);
		else
			heap.update(Ops::getind(n));
	}

	bool empty() {
		return heap.empty();
	}

	bool mem(Node *n) {
		return Ops::getind(n) != -1;
	}

	void clear() {
		heap.clear();
	}

private:
	struct Heapops {
 		static bool pred(Node *a, Node *b) { return Ops::pred(a, b); }

		static void setind(Node *n, int i) { Ops::setind(n, i); }
	};
	BinHeap<Heapops, Node*> heap;
};

typedef int IntOpenCost;

// The Ops struct has prio and tieprio methods,
// both of which return ints.  The list is sorted in increasing order
// on the prio key then secondary sorted in decreasing order on
// the tieprio key.
template <class Ops, class Node>
class OpenList <Ops, Node, IntOpenCost> {

	struct Maxq {
		Maxq() : fill(0), max(0), bkts(100)  { }

		void push(Node *n, unsigned long p) {
			if (bkts.size() <= p)
				bkts.resize(p+1);

			if (p > max)
				max = p;

			Ops::setind(n, bkts[p].size());
			bkts[p].push_back(n);
			fill++;
		}

		Node *pop() {
			for ( ; bkts[max].empty() && max > 0; max--)
				;
			Node *n = bkts[max].back();
			bkts[max].pop_back();
			Ops::setind(n, -1);
			fill--;
			return n;
		}

		void rm(Node *n, unsigned long p) {
			assert (p < bkts.size());
			std::vector<Node*> &bkt = bkts[p];

			unsigned int i = Ops::getind(n);
			assert (i < bkt.size());

			if (bkt.size() > 1) {
				bkt[i] = bkt[bkt.size() - 1];
				Ops::setind(bkt[i], i);
			}

			bkt.pop_back();
			Ops::setind(n, -1);
			fill--;
		}

		bool empty() { return fill == 0; }

		unsigned long fill;
		unsigned int max;
		std::vector< std::vector<Node*> > bkts;
	};

	unsigned long fill;
	unsigned long min;
	std::vector<Maxq> qs;

public:
	OpenList() : fill(0), min(0), qs(100) { }

	static const char *kind() { return "2d bucketed"; }

	void push(Node *n) {
		unsigned long p0 = Ops::prio(n);

		if (qs.size() <= p0)
			qs.resize(p0+1);

		if (p0 < min)
			min = p0;

		qs[p0].push(n, Ops::tieprio(n));
		fill++;
	}

	Node *pop() {
		for ( ; min < qs.size() && qs[min].empty(); min++)
			;
		fill--;
		auto n = qs[min].pop();
		assert ((unsigned long) Ops::prio(n) == min);
		return n;
	}

	void pre_update(Node*n) {
		if (Ops::getind(n) < 0)
			return;
		assert ((unsigned long) Ops::prio(n) < qs.size());
		qs[Ops::prio(n)].rm(n, Ops::tieprio(n));
		fill--;
	}

	void post_update(Node *n) {
		assert (Ops::getind(n) < 0);
		push(n);
	}

	bool empty() {
		return fill == 0;
	}

	bool mem(Node *n) {
		return Ops::getind(n) >= 0;
	}

	void clear() {
		qs.clear();
		min = 0;
	}
};

// A Result is returned from a completed search.  It contains
// statistical information about the search along with the
// solution cost and solution path if a goal was found.
template <class D>
class Result : public SearchStats {
public:
	std::vector<typename D::State> path;
	std::vector<typename D::Oper> ops;

	Result() { }

	// output writes information to the given file in
	// datafile format.
	void output(FILE *f) {
		dfpair(f, "state size", "%u", sizeof(typename D::State));
		dfpair(f, "packed state size", "%u", sizeof(typename D::PackedState));
		dfpair(f, "final sol length", "%lu", (unsigned long) path.size());
		SearchStats::output(f);
	}

	// add accumulates information from another Result
	// in the receiver.  The path infromation is not accumulated.
	void add(Result<D> &other) {
		SearchStats::add(other);
	}
};

// Solpath fills in the path and ops fields of the given search result
// by walking back the parent pointers from the given node.
//
// The Node type must have a parent field pointing to the preceeding
// node on the solution path, an op field containing the operator that
// generated the node, and a state field containing the packed state
// representation.
template <class D, class Node>
void solpath(D &d, Node *goal, Result<D> &res) {
	res.ops.clear();
	res.path.clear();
	for (Node *n = goal; n; n = n->parent) {
		typename D::State buf, &state = d.unpack(buf, n->state);
		res.path.push_back(state);
		if (n->parent)
			res.ops.push_back(n->op);
	}
}

template <class D>
class SearchAlgorithm {
public:
	SearchAlgorithm(int argc, const char *argv[]) : lim(argc, argv) { }

	virtual ~SearchAlgorithm() { }

	virtual void search(D &, typename D::State &) = 0;

	virtual void reset() {
		res = Result<D>();
	}

	virtual void output(FILE *f) {
		lim.output(f);
		res.output(f);
	}

	void start() {
		res.start();
		lim.start();
	}

	void finish() {
		res.finish();
		lim.finish();
	}

	bool limit() {
		return lim.reached(res);
	}

	Result<D> res;
	Limit lim;
};
<commit_msg>Fixed bug where open list fill variable didn't reset when list was cleared.<commit_after>// Copyright © 2013 the Search Authors under the MIT license. See AUTHORS for the list of authors.
#pragma once

#include <cstdio>
#include <vector>
#include <signal.h>

#include "../structs/intpq.hpp"
#include "../structs/binheap.hpp"

#include "closedlist.hpp"

double walltime();
double cputime();
void dfpair(FILE *f, const char *key, const char *fmt, ...);

// A SearchStats structure contains statistical information
// collected during a run of a search algorithm.
class SearchStats {
public:
	double wallstart, cpustart;
	double wallend, cpuend;
	unsigned long expd, gend, reopnd, dups;

	SearchStats();

	// start must be called by the search in order to
	// begin collecting statistical information.
	void start();

	// finish must be called by the search in order to
	// stop collecting information.
	void finish();

	// output prints the statistical information to the given
	// file in datafile format.
	void output(FILE*);

	// add accumulated the statistical information from
	// the given search statistics in the receiver.
	void add(SearchStats &other) {
		expd += other.expd;
		gend += other.gend;
		reopnd += other.reopnd;
		dups += other.dups;

		double wt = wallend - wallstart;
		double ct = cpuend - cpustart;
		wallstart = cpustart = 0;
		wallend = wt + (other.wallend - other.wallstart);
		cpuend = ct + (other.cpuend - other.cpustart);
	}
};

// A Limit structure contains information about when a
// search algorithm should be stopped because it has
// hit a user specified limit.
class Limit {
public:

	Limit();

	Limit(int, const char*[]);

	// reached returns true when the given statistics
	// reports that the given limit has been reached.
	bool reached(SearchStats &r) {
		return timeup ||
			(expd > 0 && r.expd >= expd) ||
			(gend > 0 && r.gend >= gend);
	}

	// output prints the limit to the given file in datafile
	// format.
	void output(FILE*);

	// start must be called at the beginning of a search
	// that would like to use the limit.
	void start();

	// finish must be called when a search, using the limit
	// has finished.
	void finish();

	unsigned long expd, gend, mem, cputime, walltime;
	volatile sig_atomic_t timeup;
private:
	void memlimit(const char*);
	void timelimit(const char*);
};

// An OpenList holds nodes and returns them ordered by some
// priority.  The Ops class has a pred method which accepts
// two Nodes and returns true if the 1st node is a predecessor
// of the second.
template <class Ops, class Node, class Cost>
class OpenList {
public:
	const char *kind() {
		return "binary heap";
	}

	void push(Node *n) {
		heap.push(n);
	}

	Node *pop() {
		boost::optional<Node*> p = heap.pop();
		if (!p)
			return NULL;
		return *p;
	}

	void pre_update(Node *n) {
	}

	void post_update(Node *n) {
		if (Ops::getind(n) < 0)
			heap.push(n);
		else
			heap.update(Ops::getind(n));
	}

	bool empty() {
		return heap.empty();
	}

	bool mem(Node *n) {
		return Ops::getind(n) != -1;
	}

	void clear() {
		heap.clear();
	}

private:
	struct Heapops {
 		static bool pred(Node *a, Node *b) { return Ops::pred(a, b); }

		static void setind(Node *n, int i) { Ops::setind(n, i); }
	};
	BinHeap<Heapops, Node*> heap;
};

typedef int IntOpenCost;

// The Ops struct has prio and tieprio methods,
// both of which return ints.  The list is sorted in increasing order
// on the prio key then secondary sorted in decreasing order on
// the tieprio key.
template <class Ops, class Node>
class OpenList <Ops, Node, IntOpenCost> {

	struct Maxq {
		Maxq() : fill(0), max(0), bkts(100)  { }

		void push(Node *n, unsigned long p) {
			if (bkts.size() <= p)
				bkts.resize(p+1);

			if (p > max)
				max = p;

			Ops::setind(n, bkts[p].size());
			bkts[p].push_back(n);
			fill++;
		}

		Node *pop() {
			for ( ; bkts[max].empty() && max > 0; max--)
				;
			Node *n = bkts[max].back();
			bkts[max].pop_back();
			Ops::setind(n, -1);
			fill--;
			return n;
		}

		void rm(Node *n, unsigned long p) {
			assert (p < bkts.size());
			std::vector<Node*> &bkt = bkts[p];

			unsigned int i = Ops::getind(n);
			assert (i < bkt.size());

			if (bkt.size() > 1) {
				bkt[i] = bkt[bkt.size() - 1];
				Ops::setind(bkt[i], i);
			}

			bkt.pop_back();
			Ops::setind(n, -1);
			fill--;
		}

		bool empty() { return fill == 0; }

		unsigned long fill;
		unsigned int max;
		std::vector< std::vector<Node*> > bkts;
	};

	unsigned long fill;
	unsigned long min;
	std::vector<Maxq> qs;

public:
	OpenList() : fill(0), min(0), qs(100) { }

	static const char *kind() { return "2d bucketed"; }

	void push(Node *n) {
		unsigned long p0 = Ops::prio(n);

		if (qs.size() <= p0)
			qs.resize(p0+1);

		if (p0 < min)
			min = p0;

		qs[p0].push(n, Ops::tieprio(n));
		fill++;
	}

	Node *pop() {
		for ( ; min < qs.size() && qs[min].empty(); min++)
			;
		fill--;
		auto n = qs[min].pop();
		assert ((unsigned long) Ops::prio(n) == min);
		return n;
	}

	void pre_update(Node*n) {
		if (Ops::getind(n) < 0)
			return;
		assert ((unsigned long) Ops::prio(n) < qs.size());
		qs[Ops::prio(n)].rm(n, Ops::tieprio(n));
		fill--;
	}

	void post_update(Node *n) {
		assert (Ops::getind(n) < 0);
		push(n);
	}

	bool empty() {
		return fill == 0;
	}

	bool mem(Node *n) {
		return Ops::getind(n) >= 0;
	}

	void clear() {
		qs.clear();
		min = 0;
		fill = 0;
	}
};

// A Result is returned from a completed search.  It contains
// statistical information about the search along with the
// solution cost and solution path if a goal was found.
template <class D>
class Result : public SearchStats {
public:
	std::vector<typename D::State> path;
	std::vector<typename D::Oper> ops;

	Result() { }

	// output writes information to the given file in
	// datafile format.
	void output(FILE *f) {
		dfpair(f, "state size", "%u", sizeof(typename D::State));
		dfpair(f, "packed state size", "%u", sizeof(typename D::PackedState));
		dfpair(f, "final sol length", "%lu", (unsigned long) path.size());
		SearchStats::output(f);
	}

	// add accumulates information from another Result
	// in the receiver.  The path infromation is not accumulated.
	void add(Result<D> &other) {
		SearchStats::add(other);
	}
};

// Solpath fills in the path and ops fields of the given search result
// by walking back the parent pointers from the given node.
//
// The Node type must have a parent field pointing to the preceeding
// node on the solution path, an op field containing the operator that
// generated the node, and a state field containing the packed state
// representation.
template <class D, class Node>
void solpath(D &d, Node *goal, Result<D> &res) {
	res.ops.clear();
	res.path.clear();
	for (Node *n = goal; n; n = n->parent) {
		typename D::State buf, &state = d.unpack(buf, n->state);
		res.path.push_back(state);
		if (n->parent)
			res.ops.push_back(n->op);
	}
}

template <class D>
class SearchAlgorithm {
public:
	SearchAlgorithm(int argc, const char *argv[]) : lim(argc, argv) { }

	virtual ~SearchAlgorithm() { }

	virtual void search(D &, typename D::State &) = 0;

	virtual void reset() {
		res = Result<D>();
	}

	virtual void output(FILE *f) {
		lim.output(f);
		res.output(f);
	}

	void start() {
		res.start();
		lim.start();
	}

	void finish() {
		res.finish();
		lim.finish();
	}

	bool limit() {
		return lim.reached(res);
	}

	Result<D> res;
	Limit lim;
};
<|endoftext|>