<commit_before>
// .\Release\x64\benchmarks.exe --benchmark_repetitions=3 --benchmark_filter=Planetarium  // NOLINT(whitespace/line_length)

#include "ksp_plugin/planetarium.hpp"

#include <algorithm>

#include "astronomy/time_scales.hpp"
#include "base/status_utilities.hpp"
#include "benchmark/benchmark.h"
#include "physics/body_centred_non_rotating_dynamic_frame.hpp"
#include "physics/discrete_trajectory.hpp"
#include "physics/solar_system.hpp"
#include "testing_utilities/solar_system_factory.hpp"

namespace principia {
namespace geometry {

using astronomy::operator""_UT1;
using astronomy::operator""_TT;
using base::make_not_null_unique;
using base::not_null;
using geometry::Bivector;
using geometry::DeduceSignReversingOrientation;
using geometry::Perspective;
using geometry::RigidTransformation;
using geometry::RP2Lines;
using geometry::Signature;
using geometry::Vector;
using geometry::Velocity;
using integrators::SymmetricLinearMultistepIntegrator;
using integrators::methods::Quinlan1999Order8A;
using integrators::methods::QuinlanTremaine1990Order12;
using ksp_plugin::Camera;
using ksp_plugin::Barycentric;
using ksp_plugin::Navigation;
using ksp_plugin::NavigationFrame;
using ksp_plugin::Planetarium;
using ksp_plugin::ScaledSpacePoint;
using ksp_plugin::World;
using physics::BodyCentredNonRotatingDynamicFrame;
using physics::DegreesOfFreedom;
using physics::DiscreteTrajectory;
using physics::Ephemeris;
using physics::KeplerianElements;
using physics::KeplerOrbit;
using physics::MassiveBody;
using physics::MasslessBody;
using physics::SolarSystem;
using quantities::Angle;
using quantities::Cos;
using quantities::Infinity;
using quantities::Length;
using quantities::Sin;
using quantities::Time;
using quantities::si::ArcMinute;
using quantities::si::Day;
using quantities::si::Degree;
using quantities::si::Kilo;
using quantities::si::Metre;
using quantities::si::Milli;
using quantities::si::Minute;
using quantities::si::Radian;
using quantities::si::Second;
using testing_utilities::SolarSystemFactory;

namespace {

constexpr Length near = 40'000 * Kilo(Metre);
constexpr Length far = 400'000 * Kilo(Metre);
constexpr Length focal = 1 * Metre;

Perspective<Navigation, Camera> PolarPerspective(
    Length const distance_from_earth) {
  using LeftNavigation =
      Frame<enum class LeftNavigationTag, Arbitrary, Handedness::Left>;
  return {
      RigidTransformation<Navigation, Camera>(
          Navigation::origin + Displacement<Navigation>(
                                   {0 * Metre, 0 * Metre, distance_from_earth}),
          Camera::origin,
          Rotation<LeftNavigation, Camera>(
              Vector<double, LeftNavigation>({1, 0, 0}),
              Vector<double, LeftNavigation>({0, -1, 0}),
              Bivector<double, LeftNavigation>({0, 0, -1}))
              .Forget<OrthogonalMap>() *
          Signature<Navigation, LeftNavigation>(
              Sign::Positive(),
              Sign::Positive(),
              DeduceSignReversingOrientation{}).Forget<OrthogonalMap>()),
      focal};
}

Perspective<Navigation, Camera> EquatorialPerspective(
    Length const distance_from_earth) {
  using LeftNavigation =
      Frame<enum class LeftNavigationTag, Arbitrary, Handedness::Left>;
  return {
      RigidTransformation<Navigation, Camera>(
          Navigation::origin + Displacement<Navigation>(
                                   {0 * Metre, distance_from_earth, 0 * Metre}),
          Camera::origin,
          Rotation<LeftNavigation, Camera>(
              Vector<double, LeftNavigation>({1, 0, 0}),
              Vector<double, LeftNavigation>({0, 0, 1}),
              Bivector<double, LeftNavigation>({0, -1, 0}))
              .Forget<OrthogonalMap>() *
          Signature<Navigation, LeftNavigation>(
              Sign::Positive(),
              Sign::Positive(),
              DeduceSignReversingOrientation{}).Forget<OrthogonalMap>()),
      focal};
}

class Satellites {
 public:
  Satellites()
      : solar_system_(make_not_null_unique<SolarSystem<Barycentric>>(
            SOLUTION_DIR / "astronomy" / "sol_gravity_model.proto.txt",
            SOLUTION_DIR / "astronomy" /
                "sol_initial_state_jd_2451545_000000000.proto.txt",
            /*ignore_frame=*/true)),
        ephemeris_(solar_system_->MakeEphemeris(
            /*accuracy_parameters=*/{/*fitting_tolerance=*/1 * Milli(Metre),
                                     /*geopotential_tolerance=*/0x1p-24},
            EphemerisParameters())),
        earth_(solar_system_->massive_body(
            *ephemeris_,
            SolarSystemFactory::name(SolarSystemFactory::Earth))),
        earth_centred_inertial_(
            make_not_null_unique<
                BodyCentredNonRotatingDynamicFrame<Barycentric, Navigation>>(
                ephemeris_.get(),
                earth_)) {
    // Two-line elements for GOES-8:
    // 1 23051U 94022A   00004.06628221 -.00000243  00000-0  00000-0 0  9630
    // 2 23051   0.4232  97.7420 0004776 192.8349 121.5613  1.00264613 28364
    constexpr Instant goes_8_epoch = "JD2451548.56628221"_UT1;
    KeplerianElements<Barycentric> goes_8_elements;
    goes_8_elements.inclination = 0.4232 * Degree;
    goes_8_elements.longitude_of_ascending_node = 97.7420 * Degree;
    goes_8_elements.eccentricity = 0.0004776;
    goes_8_elements.argument_of_periapsis = 192.8349 * Degree;
    goes_8_elements.mean_anomaly = 121.5613 * Degree;
    goes_8_elements.mean_motion = 1.00264613 * (2 * π * Radian / Day);

    CHECK_OK(ephemeris_->Prolong(goes_8_epoch));
    KeplerOrbit<Barycentric> const goes_8_orbit(
        *earth_, MasslessBody{}, goes_8_elements, goes_8_epoch);
    CHECK_OK(goes_8_trajectory_.Append(
        goes_8_epoch,
        ephemeris_->trajectory(earth_)->EvaluateDegreesOfFreedom(goes_8_epoch) +
            goes_8_orbit.StateVectors(goes_8_epoch)));
    auto goes_8_instance = ephemeris_->NewInstance(
        {&goes_8_trajectory_},
        Ephemeris<Barycentric>::NoIntrinsicAccelerations,
        HistoryParameters());
    CHECK_OK(ephemeris_->FlowWithFixedStep(goes_8_epoch + 100 * Day,
                                           *goes_8_instance));
  }

  DiscreteTrajectory<Barycentric> const& goes_8_trajectory() const {
    return goes_8_trajectory_;
  }

  Planetarium MakePlanetarium(
      Perspective<Navigation, Camera> const& perspective) const {
    // No dark area, human visual acuity, wide field of view.
    Planetarium::Parameters parameters(
        /*sphere_radius_multiplier=*/1,
        /*angular_resolution=*/0.4 * ArcMinute,
        /*field_of_view=*/90 * Degree);
    return Planetarium(parameters,
                       perspective,
                       ephemeris_.get(),
                       earth_centred_inertial_.get(),
        [](Position<Navigation> const& plotted_point) {
          return ScaledSpacePoint::FromCoordinates(
              ((plotted_point - World::origin) *
               (1 / (6000 * Metre))).coordinates());
        });
  }

 private:
  Ephemeris<Barycentric>::FixedStepParameters EphemerisParameters() {
    return Ephemeris<Barycentric>::FixedStepParameters(
        SymmetricLinearMultistepIntegrator<QuinlanTremaine1990Order12,
                                           Position<Barycentric>>(),
        /*step=*/10 * Minute);
  }

  Ephemeris<Barycentric>::FixedStepParameters HistoryParameters() {
    return Ephemeris<Barycentric>::FixedStepParameters(
        SymmetricLinearMultistepIntegrator<Quinlan1999Order8A,
                                           Position<Barycentric>>(),
        /*step=*/10 * Second);
  }

  not_null<std::unique_ptr<SolarSystem<Barycentric>>> const solar_system_;
  not_null<std::unique_ptr<Ephemeris<Barycentric>>> const ephemeris_;
  not_null<MassiveBody const*> const earth_;
  not_null<std::unique_ptr<NavigationFrame>> const earth_centred_inertial_;
  DiscreteTrajectory<Barycentric> goes_8_trajectory_;
};

}  // namespace

void RunBenchmark(benchmark::State& state,
                  Perspective<Navigation, Camera> const& perspective) {
  Satellites satellites;
  Planetarium planetarium = satellites.MakePlanetarium(perspective);
  RP2Lines<Length, Camera> lines;
  int total_lines = 0;
  int iterations = 0;
  // This is the time of a lunar eclipse in January 2000.
  constexpr Instant now = "2000-01-21T04:41:30,5"_TT;
  while (state.KeepRunning()) {
    lines = planetarium.PlotMethod2(satellites.goes_8_trajectory(),
                                    satellites.goes_8_trajectory().begin(),
                                    satellites.goes_8_trajectory().end(),
                                    now,
                                    /*reverse=*/false);
    total_lines += lines.size();
    ++iterations;
  }
  Length min_x = Infinity<Length>;
  Length min_y = Infinity<Length>;
  Length max_x = -Infinity<Length>;
  Length max_y = -Infinity<Length>;
  int points = 0;
  for (auto const& line : lines) {
    points += line.size();
    for (auto const& point : line) {
      min_x = std::min(min_x, point.x());
      min_y = std::min(min_y, point.y());
      max_x = std::max(max_x, point.x());
      max_y = std::max(max_y, point.y());
    }
  }
  state.SetLabel(std::to_string(points) + " points in " +
                 std::to_string(total_lines / iterations) + " lines within [" +
                 DebugString(min_x) + ", " + DebugString(max_x) + "] × [" +
                 DebugString(min_y) + ", " + DebugString(max_y) + "]");
}

void BM_PlanetariumPlotMethod2NearPolarPerspective(benchmark::State& state) {
  RunBenchmark(state, PolarPerspective(near));
}

void BM_PlanetariumPlotMethod2FarPolarPerspective(benchmark::State& state) {
  RunBenchmark(state, PolarPerspective(far));
}

void BM_PlanetariumPlotMethod2NearEquatorialPerspective(
    benchmark::State& state) {
  RunBenchmark(state, EquatorialPerspective(near));
}

void BM_PlanetariumPlotMethod2FarEquatorialPerspective(
    benchmark::State& state) {
  RunBenchmark(state, EquatorialPerspective(far));
}

BENCHMARK(BM_PlanetariumPlotMethod2NearPolarPerspective);
BENCHMARK(BM_PlanetariumPlotMethod2FarPolarPerspective);
BENCHMARK(BM_PlanetariumPlotMethod2NearEquatorialPerspective);
BENCHMARK(BM_PlanetariumPlotMethod2FarEquatorialPerspective);

}  // namespace geometry
}  // namespace principia
<commit_msg>navigation<commit_after>
// .\Release\x64\benchmarks.exe --benchmark_repetitions=3 --benchmark_filter=Planetarium  // NOLINT(whitespace/line_length)

#include "ksp_plugin/planetarium.hpp"

#include <algorithm>

#include "astronomy/time_scales.hpp"
#include "base/status_utilities.hpp"
#include "benchmark/benchmark.h"
#include "physics/body_centred_non_rotating_dynamic_frame.hpp"
#include "physics/discrete_trajectory.hpp"
#include "physics/solar_system.hpp"
#include "testing_utilities/solar_system_factory.hpp"

namespace principia {
namespace geometry {

using astronomy::operator""_UT1;
using astronomy::operator""_TT;
using base::make_not_null_unique;
using base::not_null;
using geometry::Bivector;
using geometry::DeduceSignReversingOrientation;
using geometry::Perspective;
using geometry::RigidTransformation;
using geometry::RP2Lines;
using geometry::Signature;
using geometry::Vector;
using geometry::Velocity;
using integrators::SymmetricLinearMultistepIntegrator;
using integrators::methods::Quinlan1999Order8A;
using integrators::methods::QuinlanTremaine1990Order12;
using ksp_plugin::Camera;
using ksp_plugin::Barycentric;
using ksp_plugin::Navigation;
using ksp_plugin::NavigationFrame;
using ksp_plugin::Planetarium;
using ksp_plugin::ScaledSpacePoint;
using ksp_plugin::World;
using physics::BodyCentredNonRotatingDynamicFrame;
using physics::DegreesOfFreedom;
using physics::DiscreteTrajectory;
using physics::Ephemeris;
using physics::KeplerianElements;
using physics::KeplerOrbit;
using physics::MassiveBody;
using physics::MasslessBody;
using physics::SolarSystem;
using quantities::Angle;
using quantities::Cos;
using quantities::Infinity;
using quantities::Length;
using quantities::Sin;
using quantities::Time;
using quantities::si::ArcMinute;
using quantities::si::Day;
using quantities::si::Degree;
using quantities::si::Kilo;
using quantities::si::Metre;
using quantities::si::Milli;
using quantities::si::Minute;
using quantities::si::Radian;
using quantities::si::Second;
using testing_utilities::SolarSystemFactory;

namespace {

constexpr Length near = 40'000 * Kilo(Metre);
constexpr Length far = 400'000 * Kilo(Metre);
constexpr Length focal = 1 * Metre;

Perspective<Navigation, Camera> PolarPerspective(
    Length const distance_from_earth) {
  using LeftNavigation =
      Frame<enum class LeftNavigationTag, Arbitrary, Handedness::Left>;
  return {
      RigidTransformation<Navigation, Camera>(
          Navigation::origin + Displacement<Navigation>(
                                   {0 * Metre, 0 * Metre, distance_from_earth}),
          Camera::origin,
          Rotation<LeftNavigation, Camera>(
              Vector<double, LeftNavigation>({1, 0, 0}),
              Vector<double, LeftNavigation>({0, -1, 0}),
              Bivector<double, LeftNavigation>({0, 0, -1}))
              .Forget<OrthogonalMap>() *
          Signature<Navigation, LeftNavigation>(
              Sign::Positive(),
              Sign::Positive(),
              DeduceSignReversingOrientation{}).Forget<OrthogonalMap>()),
      focal};
}

Perspective<Navigation, Camera> EquatorialPerspective(
    Length const distance_from_earth) {
  using LeftNavigation =
      Frame<enum class LeftNavigationTag, Arbitrary, Handedness::Left>;
  return {
      RigidTransformation<Navigation, Camera>(
          Navigation::origin + Displacement<Navigation>(
                                   {0 * Metre, distance_from_earth, 0 * Metre}),
          Camera::origin,
          Rotation<LeftNavigation, Camera>(
              Vector<double, LeftNavigation>({1, 0, 0}),
              Vector<double, LeftNavigation>({0, 0, 1}),
              Bivector<double, LeftNavigation>({0, -1, 0}))
              .Forget<OrthogonalMap>() *
          Signature<Navigation, LeftNavigation>(
              Sign::Positive(),
              Sign::Positive(),
              DeduceSignReversingOrientation{}).Forget<OrthogonalMap>()),
      focal};
}

class Satellites {
 public:
  Satellites()
      : solar_system_(make_not_null_unique<SolarSystem<Barycentric>>(
            SOLUTION_DIR / "astronomy" / "sol_gravity_model.proto.txt",
            SOLUTION_DIR / "astronomy" /
                "sol_initial_state_jd_2451545_000000000.proto.txt",
            /*ignore_frame=*/true)),
        ephemeris_(solar_system_->MakeEphemeris(
            /*accuracy_parameters=*/{/*fitting_tolerance=*/1 * Milli(Metre),
                                     /*geopotential_tolerance=*/0x1p-24},
            EphemerisParameters())),
        earth_(solar_system_->massive_body(
            *ephemeris_,
            SolarSystemFactory::name(SolarSystemFactory::Earth))),
        earth_centred_inertial_(
            make_not_null_unique<
                BodyCentredNonRotatingDynamicFrame<Barycentric, Navigation>>(
                ephemeris_.get(),
                earth_)) {
    // Two-line elements for GOES-8:
    // 1 23051U 94022A   00004.06628221 -.00000243  00000-0  00000-0 0  9630
    // 2 23051   0.4232  97.7420 0004776 192.8349 121.5613  1.00264613 28364
    constexpr Instant goes_8_epoch = "JD2451548.56628221"_UT1;
    KeplerianElements<Barycentric> goes_8_elements;
    goes_8_elements.inclination = 0.4232 * Degree;
    goes_8_elements.longitude_of_ascending_node = 97.7420 * Degree;
    goes_8_elements.eccentricity = 0.0004776;
    goes_8_elements.argument_of_periapsis = 192.8349 * Degree;
    goes_8_elements.mean_anomaly = 121.5613 * Degree;
    goes_8_elements.mean_motion = 1.00264613 * (2 * π * Radian / Day);

    CHECK_OK(ephemeris_->Prolong(goes_8_epoch));
    KeplerOrbit<Barycentric> const goes_8_orbit(
        *earth_, MasslessBody{}, goes_8_elements, goes_8_epoch);
    CHECK_OK(goes_8_trajectory_.Append(
        goes_8_epoch,
        ephemeris_->trajectory(earth_)->EvaluateDegreesOfFreedom(goes_8_epoch) +
            goes_8_orbit.StateVectors(goes_8_epoch)));
    auto goes_8_instance = ephemeris_->NewInstance(
        {&goes_8_trajectory_},
        Ephemeris<Barycentric>::NoIntrinsicAccelerations,
        HistoryParameters());
    CHECK_OK(ephemeris_->FlowWithFixedStep(goes_8_epoch + 100 * Day,
                                           *goes_8_instance));
  }

  DiscreteTrajectory<Barycentric> const& goes_8_trajectory() const {
    return goes_8_trajectory_;
  }

  Planetarium MakePlanetarium(
      Perspective<Navigation, Camera> const& perspective) const {
    // No dark area, human visual acuity, wide field of view.
    Planetarium::Parameters parameters(
        /*sphere_radius_multiplier=*/1,
        /*angular_resolution=*/0.4 * ArcMinute,
        /*field_of_view=*/90 * Degree);
    return Planetarium(parameters,
                       perspective,
                       ephemeris_.get(),
                       earth_centred_inertial_.get(),
        [](Position<Navigation> const& plotted_point) {
          return ScaledSpacePoint::FromCoordinates(
              ((plotted_point - Navigation::origin) *
               (1 / (6000 * Metre))).coordinates());
        });
  }

 private:
  Ephemeris<Barycentric>::FixedStepParameters EphemerisParameters() {
    return Ephemeris<Barycentric>::FixedStepParameters(
        SymmetricLinearMultistepIntegrator<QuinlanTremaine1990Order12,
                                           Position<Barycentric>>(),
        /*step=*/10 * Minute);
  }

  Ephemeris<Barycentric>::FixedStepParameters HistoryParameters() {
    return Ephemeris<Barycentric>::FixedStepParameters(
        SymmetricLinearMultistepIntegrator<Quinlan1999Order8A,
                                           Position<Barycentric>>(),
        /*step=*/10 * Second);
  }

  not_null<std::unique_ptr<SolarSystem<Barycentric>>> const solar_system_;
  not_null<std::unique_ptr<Ephemeris<Barycentric>>> const ephemeris_;
  not_null<MassiveBody const*> const earth_;
  not_null<std::unique_ptr<NavigationFrame>> const earth_centred_inertial_;
  DiscreteTrajectory<Barycentric> goes_8_trajectory_;
};

}  // namespace

void RunBenchmark(benchmark::State& state,
                  Perspective<Navigation, Camera> const& perspective) {
  Satellites satellites;
  Planetarium planetarium = satellites.MakePlanetarium(perspective);
  RP2Lines<Length, Camera> lines;
  int total_lines = 0;
  int iterations = 0;
  // This is the time of a lunar eclipse in January 2000.
  constexpr Instant now = "2000-01-21T04:41:30,5"_TT;
  while (state.KeepRunning()) {
    lines = planetarium.PlotMethod2(satellites.goes_8_trajectory(),
                                    satellites.goes_8_trajectory().begin(),
                                    satellites.goes_8_trajectory().end(),
                                    now,
                                    /*reverse=*/false);
    total_lines += lines.size();
    ++iterations;
  }
  Length min_x = Infinity<Length>;
  Length min_y = Infinity<Length>;
  Length max_x = -Infinity<Length>;
  Length max_y = -Infinity<Length>;
  int points = 0;
  for (auto const& line : lines) {
    points += line.size();
    for (auto const& point : line) {
      min_x = std::min(min_x, point.x());
      min_y = std::min(min_y, point.y());
      max_x = std::max(max_x, point.x());
      max_y = std::max(max_y, point.y());
    }
  }
  state.SetLabel(std::to_string(points) + " points in " +
                 std::to_string(total_lines / iterations) + " lines within [" +
                 DebugString(min_x) + ", " + DebugString(max_x) + "] × [" +
                 DebugString(min_y) + ", " + DebugString(max_y) + "]");
}

void BM_PlanetariumPlotMethod2NearPolarPerspective(benchmark::State& state) {
  RunBenchmark(state, PolarPerspective(near));
}

void BM_PlanetariumPlotMethod2FarPolarPerspective(benchmark::State& state) {
  RunBenchmark(state, PolarPerspective(far));
}

void BM_PlanetariumPlotMethod2NearEquatorialPerspective(
    benchmark::State& state) {
  RunBenchmark(state, EquatorialPerspective(near));
}

void BM_PlanetariumPlotMethod2FarEquatorialPerspective(
    benchmark::State& state) {
  RunBenchmark(state, EquatorialPerspective(far));
}

BENCHMARK(BM_PlanetariumPlotMethod2NearPolarPerspective);
BENCHMARK(BM_PlanetariumPlotMethod2FarPolarPerspective);
BENCHMARK(BM_PlanetariumPlotMethod2NearEquatorialPerspective);
BENCHMARK(BM_PlanetariumPlotMethod2FarEquatorialPerspective);

}  // namespace geometry
}  // namespace principia
<|endoftext|>


<commit_before>/*
    Copyright (C) 2001 by Jorrit Tyberghein

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "cssys/sysdriv.h"
#include "cstool/initapp.h"
#include "isys/system.h"
#include "iutil/eventh.h"
#include "iutil/comp.h"
#include "iutil/objreg.h"
#include "ivideo/graph3d.h"
#include "ivideo/graph2d.h"
#include "iengine/engine.h"
#include "ivaria/reporter.h"
#include "ivaria/stdrep.h"
#include "ivaria/conout.h"
#include "isys/vfs.h"
#include "igraphic/imageio.h"
#include "ivideo/fontserv.h"
#include "imap/parser.h"
#include "isys/plugin.h"
#include "iutil/eventq.h"
#include "iutil/evdefs.h"
#include "iutil/virtclk.h"
#include "iutil/eventq.h"
#include "iutil/cmdline.h"
#include "iutil/cfgmgr.h"

static SysSystemDriver* global_sys = NULL;
static char* global_config_name = NULL;
static int global_argc = 0;
static const char* const * global_argv = 0;
static iEventHandler* installed_event_handler = NULL;

iObjectRegistry* csInitializer::CreateEnvironment ()
{
  if (!InitializeSCF ()) return NULL;
  iObjectRegistry* object_reg = CreateObjectRegistry ();
  if (!object_reg) return NULL;
  if (!CreatePluginManager (object_reg)) return NULL;
  if (!CreateEventQueue (object_reg)) return NULL;
  if (!CreateVirtualClock (object_reg)) return NULL;
  if (!CreateCommandLineParser (object_reg)) return NULL;
  if (!CreateConfigManager (object_reg)) return NULL;
  return object_reg;
}

bool csInitializer::InitializeSCF ()
{
  global_sys = new SysSystemDriver ();
  return true;
}

iPluginManager* csInitializer::CreatePluginManager (
	iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iPluginManager);
}

iEventQueue* csInitializer::CreateEventQueue (iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iEventQueue);
}

iVirtualClock* csInitializer::CreateVirtualClock (iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iVirtualClock);
}

iCommandLineParser* csInitializer::CreateCommandLineParser (
  	iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iCommandLineParser);
}

iConfigManager* csInitializer::CreateConfigManager (
	iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iConfigManager);
}

iObjectRegistry* csInitializer::CreateObjectRegistry ()
{
  return global_sys->GetObjectRegistry ();
}

bool csInitializer::RequestPlugins (iObjectRegistry* /*object_reg*/,
	const char* config_name,
	int argc, const char* const argv[],
	...)
{
  if (config_name) global_config_name = csStrNew (config_name);
  else global_config_name = NULL;
  global_argc = argc;
  global_argv = argv;

  va_list arg;
  va_start (arg, argv);
  char* plugName = va_arg (arg, char*);
  while (plugName != NULL)
  {
    // scfId and version are unused for now.
    int scfId = va_arg (arg, scfInterfaceID);
    int version = va_arg (arg, int);
    global_sys->RequestPlugin (plugName);
    plugName = va_arg (arg, char*);
  }
  va_end (arg);
  return true;
}

bool csInitializer::Initialize (iObjectRegistry* object_reg)
{
  bool rc = global_sys->Initialize (global_argc, global_argv,
    global_config_name);
  if (!rc) return false;

  // Setup the object registry.
  iPluginManager* plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);

  iGraphics3D* g3d = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_VIDEO,
  	iGraphics3D);
  if (g3d)
  {
    object_reg->Register (g3d);
    if (g3d->GetDriver2D ())
      object_reg->Register (g3d->GetDriver2D ());
    g3d->DecRef ();
  }
  else
  {
    iGraphics2D* g2d = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_CANVAS,
  	iGraphics2D);
    if (g2d) { object_reg->Register (g2d); g2d->DecRef (); }
  }

  iEngine* engine = CS_QUERY_PLUGIN (plugin_mgr, iEngine);
  if (engine)
  {
    object_reg->Register (engine);
    engine->DecRef ();
  }

  iVFS* vfs = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_VFS, iVFS);
  if (vfs)
  {
    object_reg->Register (vfs);
    vfs->DecRef ();
  }

  iConsoleOutput* console = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_CONSOLE, iConsoleOutput);
  if (console)
  {
    object_reg->Register (console);
    console->DecRef ();
  }

  iLoader* loader = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_LVLLOADER, iLoader);
  if (loader)
  {
    object_reg->Register (loader);
    loader->DecRef ();
  }

  iImageIO* imloader = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_IMGLOADER, iImageIO);
  if (imloader)
  {
    object_reg->Register (imloader);
    imloader->DecRef ();
  }

  iFontServer* fntsvr = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_FONTSERVER, iFontServer);
  if (fntsvr)
  {
    object_reg->Register (fntsvr);
    fntsvr->DecRef ();
  }

  iReporter* reporter = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_REPORTER,
  	iReporter);
  if (reporter)
  {
    object_reg->Register (reporter);
    reporter->DecRef ();
  }

  iStandardReporterListener* stdrep = CS_QUERY_PLUGIN (plugin_mgr,
  	iStandardReporterListener);
  if (stdrep)
  {
    stdrep->SetDefaults ();
    object_reg->Register (stdrep);
    stdrep->DecRef ();
  }

  return true;
}

bool csInitializer::SetupEventHandler (iObjectRegistry* object_reg,
	iEventHandler* evhdlr, unsigned int eventmask)
{
  iEventQueue* q = CS_QUERY_REGISTRY (object_reg, iEventQueue);
  if (q)
  {
    q->RegisterListener (evhdlr, eventmask);
    installed_event_handler = evhdlr;
    return true;
  }
  else return false;
}

class csAppEventHandler : public iEventHandler
{
private:
  csEventHandlerFunc* evhdlr;

public:
  csAppEventHandler (csEventHandlerFunc* evhdlr)
  {
    SCF_CONSTRUCT_IBASE (NULL);
    csAppEventHandler::evhdlr = evhdlr;
  }
  SCF_DECLARE_IBASE;
  virtual bool HandleEvent (iEvent& ev)
  {
    return evhdlr (ev);
  }
};

SCF_IMPLEMENT_IBASE (csAppEventHandler)
  SCF_IMPLEMENTS_INTERFACE (iEventHandler)
SCF_IMPLEMENT_IBASE_END

bool csInitializer::SetupEventHandler (iObjectRegistry* object_reg,
	csEventHandlerFunc* evhdlr_func)
{
  csAppEventHandler* evhdlr = new csAppEventHandler (evhdlr_func);
  return SetupEventHandler (object_reg, evhdlr, CSMASK_Broadcast |
  	CSMASK_MouseUp | CSMASK_MouseDown | CSMASK_MouseMove |
	CSMASK_KeyDown | CSMASK_KeyUp | CSMASK_MouseClick |
	CSMASK_MouseDoubleClick | CSMASK_JoystickMove |
	CSMASK_JoystickDown | CSMASK_JoystickUp | CSMASK_Nothing);
}

bool csInitializer::OpenApplication (iObjectRegistry* object_reg)
{
  return global_sys->Open ();
}

void csInitializer::CloseApplication (iObjectRegistry* object_reg)
{
  global_sys->Close ();
}

void csInitializer::DestroyApplication (iObjectRegistry* object_reg)
{
  if (installed_event_handler)
  {
    iEventQueue* q = CS_QUERY_REGISTRY (object_reg, iEventQueue);
    q->RemoveListener (installed_event_handler);
  }
  delete global_sys;
}

bool csInitializeApplication (iObjectRegistry* object_reg, bool use_reporter,
	bool use_reporter_listener)
{
  iPluginManager* plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);

  iGraphics3D* g3d = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_VIDEO,
  	iGraphics3D);
  if (g3d)
  {
    object_reg->Register (g3d);
    if (g3d->GetDriver2D ())
      object_reg->Register (g3d->GetDriver2D ());
    g3d->DecRef ();
  }

  iEngine* engine = CS_QUERY_PLUGIN (plugin_mgr, iEngine);
  if (engine)
  {
    object_reg->Register (engine);
    engine->DecRef ();
  }

  iVFS* vfs = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_VFS, iVFS);
  if (vfs)
  {
    object_reg->Register (vfs);
    vfs->DecRef ();
  }

  iConsoleOutput* console = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_CONSOLE, iConsoleOutput);
  if (console)
  {
    object_reg->Register (console);
    console->DecRef ();
  }

  iLoader* loader = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_LVLLOADER, iLoader);
  if (loader)
  {
    object_reg->Register (loader);
    loader->DecRef ();
  }

  iReporter* reporter = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_REPORTER,
  	iReporter);
  if (!reporter && use_reporter)
  {
    reporter = CS_LOAD_PLUGIN (plugin_mgr, "crystalspace.utilities.reporter",
    	CS_FUNCID_REPORTER, iReporter);
    if (!reporter)
      return false;

  }
  if (reporter)
  {
    object_reg->Register (reporter);
    reporter->DecRef ();
  }

  iStandardReporterListener* stdrep = CS_QUERY_PLUGIN (plugin_mgr,
  	iStandardReporterListener);
  if (!stdrep && use_reporter_listener)
  {
    stdrep = CS_LOAD_PLUGIN (plugin_mgr, "crystalspace.utilities.stdrep",
    	"StdRep", iStandardReporterListener);
    if (!stdrep)
      return false;

  }
  if (stdrep)
  {
    stdrep->SetDefaults ();
    object_reg->Register (stdrep);
    stdrep->DecRef ();
  }

  return true;
}

bool csInitializer::MainLoop (iObjectRegistry* /*object_reg*/)
{
  global_sys->Loop ();
  return true;
}

<commit_msg>Applied the correct fix.<commit_after>/*
    Copyright (C) 2001 by Jorrit Tyberghein

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"
#include "cssys/sysdriv.h"
#include "cstool/initapp.h"
#include "isys/system.h"
#include "iutil/eventh.h"
#include "iutil/comp.h"
#include "iutil/objreg.h"
#include "ivideo/graph3d.h"
#include "ivideo/graph2d.h"
#include "iengine/engine.h"
#include "ivaria/reporter.h"
#include "ivaria/stdrep.h"
#include "ivaria/conout.h"
#include "isys/vfs.h"
#include "igraphic/imageio.h"
#include "ivideo/fontserv.h"
#include "imap/parser.h"
#include "isys/plugin.h"
#include "iutil/eventq.h"
#include "iutil/evdefs.h"
#include "iutil/virtclk.h"
#include "iutil/eventq.h"
#include "iutil/cmdline.h"
#include "iutil/cfgmgr.h"

static SysSystemDriver* global_sys = NULL;
static char* global_config_name = NULL;
static int global_argc = 0;
static const char* const * global_argv = 0;
static iEventHandler* installed_event_handler = NULL;

iObjectRegistry* csInitializer::CreateEnvironment ()
{
  if (!InitializeSCF ()) return NULL;
  iObjectRegistry* object_reg = CreateObjectRegistry ();
  if (!object_reg) return NULL;
  if (!CreatePluginManager (object_reg)) return NULL;
  if (!CreateEventQueue (object_reg)) return NULL;
  if (!CreateVirtualClock (object_reg)) return NULL;
  if (!CreateCommandLineParser (object_reg)) return NULL;
  if (!CreateConfigManager (object_reg)) return NULL;
  return object_reg;
}

bool csInitializer::InitializeSCF ()
{
  global_sys = new SysSystemDriver ();
  return true;
}

iPluginManager* csInitializer::CreatePluginManager (
	iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iPluginManager);
}

iEventQueue* csInitializer::CreateEventQueue (iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iEventQueue);
}

iVirtualClock* csInitializer::CreateVirtualClock (iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iVirtualClock);
}

iCommandLineParser* csInitializer::CreateCommandLineParser (
  	iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iCommandLineParser);
}

iConfigManager* csInitializer::CreateConfigManager (
	iObjectRegistry* object_reg)
{
  return CS_QUERY_REGISTRY (object_reg, iConfigManager);
}

iObjectRegistry* csInitializer::CreateObjectRegistry ()
{
  return global_sys->GetObjectRegistry ();
}

bool csInitializer::RequestPlugins (iObjectRegistry* /*object_reg*/,
	const char* config_name,
	int argc, const char* const argv[],
	...)
{
  if (config_name) global_config_name = csStrNew (config_name);
  else global_config_name = NULL;
  global_argc = argc;
  global_argv = argv;

  va_list arg;
  va_start (arg, argv);
  char* plugName = va_arg (arg, char*);
  while (plugName != NULL)
  {
    int scfId = va_arg (arg, scfInterfaceID);
    int version = va_arg (arg, int);
    // scfId and version are unused for now.
    (void)scfId; (void)version;
    global_sys->RequestPlugin (plugName);
    plugName = va_arg (arg, char*);
  }
  va_end (arg);
  return true;
}

bool csInitializer::Initialize (iObjectRegistry* object_reg)
{
  bool rc = global_sys->Initialize (global_argc, global_argv,
    global_config_name);
  if (!rc) return false;

  // Setup the object registry.
  iPluginManager* plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);

  iGraphics3D* g3d = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_VIDEO,
  	iGraphics3D);
  if (g3d)
  {
    object_reg->Register (g3d);
    if (g3d->GetDriver2D ())
      object_reg->Register (g3d->GetDriver2D ());
    g3d->DecRef ();
  }
  else
  {
    iGraphics2D* g2d = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_CANVAS,
  	iGraphics2D);
    if (g2d) { object_reg->Register (g2d); g2d->DecRef (); }
  }

  iEngine* engine = CS_QUERY_PLUGIN (plugin_mgr, iEngine);
  if (engine)
  {
    object_reg->Register (engine);
    engine->DecRef ();
  }

  iVFS* vfs = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_VFS, iVFS);
  if (vfs)
  {
    object_reg->Register (vfs);
    vfs->DecRef ();
  }

  iConsoleOutput* console = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_CONSOLE, iConsoleOutput);
  if (console)
  {
    object_reg->Register (console);
    console->DecRef ();
  }

  iLoader* loader = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_LVLLOADER, iLoader);
  if (loader)
  {
    object_reg->Register (loader);
    loader->DecRef ();
  }

  iImageIO* imloader = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_IMGLOADER, iImageIO);
  if (imloader)
  {
    object_reg->Register (imloader);
    imloader->DecRef ();
  }

  iFontServer* fntsvr = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_FONTSERVER, iFontServer);
  if (fntsvr)
  {
    object_reg->Register (fntsvr);
    fntsvr->DecRef ();
  }

  iReporter* reporter = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_REPORTER,
  	iReporter);
  if (reporter)
  {
    object_reg->Register (reporter);
    reporter->DecRef ();
  }

  iStandardReporterListener* stdrep = CS_QUERY_PLUGIN (plugin_mgr,
  	iStandardReporterListener);
  if (stdrep)
  {
    stdrep->SetDefaults ();
    object_reg->Register (stdrep);
    stdrep->DecRef ();
  }

  return true;
}

bool csInitializer::SetupEventHandler (iObjectRegistry* object_reg,
	iEventHandler* evhdlr, unsigned int eventmask)
{
  iEventQueue* q = CS_QUERY_REGISTRY (object_reg, iEventQueue);
  if (q)
  {
    q->RegisterListener (evhdlr, eventmask);
    installed_event_handler = evhdlr;
    return true;
  }
  else return false;
}

class csAppEventHandler : public iEventHandler
{
private:
  csEventHandlerFunc* evhdlr;

public:
  csAppEventHandler (csEventHandlerFunc* evhdlr)
  {
    SCF_CONSTRUCT_IBASE (NULL);
    csAppEventHandler::evhdlr = evhdlr;
  }
  SCF_DECLARE_IBASE;
  virtual bool HandleEvent (iEvent& ev)
  {
    return evhdlr (ev);
  }
};

SCF_IMPLEMENT_IBASE (csAppEventHandler)
  SCF_IMPLEMENTS_INTERFACE (iEventHandler)
SCF_IMPLEMENT_IBASE_END

bool csInitializer::SetupEventHandler (iObjectRegistry* object_reg,
	csEventHandlerFunc* evhdlr_func)
{
  csAppEventHandler* evhdlr = new csAppEventHandler (evhdlr_func);
  return SetupEventHandler (object_reg, evhdlr, CSMASK_Broadcast |
  	CSMASK_MouseUp | CSMASK_MouseDown | CSMASK_MouseMove |
	CSMASK_KeyDown | CSMASK_KeyUp | CSMASK_MouseClick |
	CSMASK_MouseDoubleClick | CSMASK_JoystickMove |
	CSMASK_JoystickDown | CSMASK_JoystickUp | CSMASK_Nothing);
}

bool csInitializer::OpenApplication (iObjectRegistry* object_reg)
{
  return global_sys->Open ();
}

void csInitializer::CloseApplication (iObjectRegistry* object_reg)
{
  global_sys->Close ();
}

void csInitializer::DestroyApplication (iObjectRegistry* object_reg)
{
  if (installed_event_handler)
  {
    iEventQueue* q = CS_QUERY_REGISTRY (object_reg, iEventQueue);
    q->RemoveListener (installed_event_handler);
  }
  delete global_sys;
}

bool csInitializeApplication (iObjectRegistry* object_reg, bool use_reporter,
	bool use_reporter_listener)
{
  iPluginManager* plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);

  iGraphics3D* g3d = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_VIDEO,
  	iGraphics3D);
  if (g3d)
  {
    object_reg->Register (g3d);
    if (g3d->GetDriver2D ())
      object_reg->Register (g3d->GetDriver2D ());
    g3d->DecRef ();
  }

  iEngine* engine = CS_QUERY_PLUGIN (plugin_mgr, iEngine);
  if (engine)
  {
    object_reg->Register (engine);
    engine->DecRef ();
  }

  iVFS* vfs = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_VFS, iVFS);
  if (vfs)
  {
    object_reg->Register (vfs);
    vfs->DecRef ();
  }

  iConsoleOutput* console = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_CONSOLE, iConsoleOutput);
  if (console)
  {
    object_reg->Register (console);
    console->DecRef ();
  }

  iLoader* loader = CS_QUERY_PLUGIN_ID (plugin_mgr,
  	CS_FUNCID_LVLLOADER, iLoader);
  if (loader)
  {
    object_reg->Register (loader);
    loader->DecRef ();
  }

  iReporter* reporter = CS_QUERY_PLUGIN_ID (plugin_mgr, CS_FUNCID_REPORTER,
  	iReporter);
  if (!reporter && use_reporter)
  {
    reporter = CS_LOAD_PLUGIN (plugin_mgr, "crystalspace.utilities.reporter",
    	CS_FUNCID_REPORTER, iReporter);
    if (!reporter)
      return false;

  }
  if (reporter)
  {
    object_reg->Register (reporter);
    reporter->DecRef ();
  }

  iStandardReporterListener* stdrep = CS_QUERY_PLUGIN (plugin_mgr,
  	iStandardReporterListener);
  if (!stdrep && use_reporter_listener)
  {
    stdrep = CS_LOAD_PLUGIN (plugin_mgr, "crystalspace.utilities.stdrep",
    	"StdRep", iStandardReporterListener);
    if (!stdrep)
      return false;

  }
  if (stdrep)
  {
    stdrep->SetDefaults ();
    object_reg->Register (stdrep);
    stdrep->DecRef ();
  }

  return true;
}

bool csInitializer::MainLoop (iObjectRegistry* /*object_reg*/)
{
  global_sys->Loop ();
  return true;
}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------
//
// CASROS: Component-based Architecture for Safe Robotic Systems
//
// Copyright (C) 2012-2014 Min Yang Jung and Peter Kazanzides
//
//------------------------------------------------------------------------
//
// Created on   : Jul 7, 2012
// Last revision: Jul 10, 2014
// Author       : Min Yang Jung (myj@jhu.edu)
// Github       : https://github.com/minyang/casros
//
#include "event.h"

using namespace SF;

Event::Event(const std::string     & name,
             unsigned int            severity,
             const TransitionsType & transitions)
    : Name(name), Severity(severity), Transitions(transitions)
{
    memset(TransitionMask, 0, TOTAL_NUMBER_OF_STATES * TOTAL_NUMBER_OF_STATES);
#define N 0
#define W 1
#define E 2
    // Counters to check if this event has multiple next states for one current state
    int fromN = 0, fromW = 0, fromE = 0;
    for (size_t i = 0; i < Transitions.size(); ++i) {
        switch (Transitions[i]) {
        case State::NORMAL_TO_WARNING: TransitionMask[N][W] = true; ++fromN; break;
        case State::NORMAL_TO_ERROR  : TransitionMask[N][E] = true; ++fromN; break;
        case State::WARNING_TO_NORMAL: TransitionMask[W][N] = true; ++fromW; break;
        case State::WARNING_TO_ERROR : TransitionMask[W][E] = true; ++fromW; break;
        case State::ERROR_TO_WARNING : TransitionMask[E][W] = true; ++fromE; break;
        case State::ERROR_TO_NORMAL  : TransitionMask[E][N] = true; ++fromE; break;
        default: continue;
        }
    }
    if (fromN >= 2 || fromW >= 2 || fromE >= 2) {
        SFLOG_ERROR << "Event transition error: event \"" << Name << "\" has multiple next states" << std::endl;
        SFLOG_ERROR << (int) TransitionMask[N][N] << " " << (int) TransitionMask[N][W] << " " << (int) TransitionMask[N][E] << std::endl;
        SFLOG_ERROR << (int) TransitionMask[W][N] << " " << (int) TransitionMask[W][W] << " " << (int) TransitionMask[W][E] << std::endl;
        SFLOG_ERROR << (int) TransitionMask[E][N] << " " << (int) TransitionMask[E][W] << " " << (int) TransitionMask[E][E] << std::endl;
        throw("Event error");
    }
#undef N
#undef W
#undef E
}

State::TransitionType Event::GetTransition(State::StateType currentState) const
{
    switch (currentState) {
    case State::NORMAL:
        if (TransitionMask[State::NORMAL][State::WARNING])
            return State::NORMAL_TO_WARNING;
        else if (TransitionMask[State::NORMAL][State::ERROR])
            return State::NORMAL_TO_ERROR;
        break;
    case State::WARNING:
        if (TransitionMask[State::WARNING][State::NORMAL])
            return State::WARNING_TO_NORMAL;
        else if (TransitionMask[State::WARNING][State::ERROR])
            return State::WARNING_TO_ERROR;
        break;
    case State::ERROR:
        if (TransitionMask[State::ERROR][State::NORMAL])
            return State::ERROR_TO_NORMAL;
        else if (TransitionMask[State::ERROR][State::WARNING])
            return State::ERROR_TO_WARNING;
        break;
    default:
        return State::INVALID_TRANSITION;
    }
}

void Event::ToStream(std::ostream & os) const
{
    os << Name << ": " << Severity << ", [ ";

    for (size_t i = 0; i < Transitions.size(); ++i) {
        switch (Transitions[i]) {
        case State::NORMAL_TO_ERROR  : os << "N2E "; break;
        case State::ERROR_TO_NORMAL  : os << "E2N "; break;
        case State::NORMAL_TO_WARNING: os << "N2W "; break;
        case State::WARNING_TO_NORMAL: os << "W2N "; break;
        case State::WARNING_TO_ERROR : os << "W2E "; break;
        case State::ERROR_TO_WARNING : os << "E2W "; break;
        default:                       os << "INVALID ";
        }
    }
    os << "], ";
}
<commit_msg>added missing return value for invalid transitions<commit_after>//------------------------------------------------------------------------
//
// CASROS: Component-based Architecture for Safe Robotic Systems
//
// Copyright (C) 2012-2014 Min Yang Jung and Peter Kazanzides
//
//------------------------------------------------------------------------
//
// Created on   : Jul 7, 2012
// Last revision: Jul 10, 2014
// Author       : Min Yang Jung (myj@jhu.edu)
// Github       : https://github.com/minyang/casros
//
#include "event.h"

using namespace SF;

Event::Event(const std::string     & name,
             unsigned int            severity,
             const TransitionsType & transitions)
    : Name(name), Severity(severity), Transitions(transitions)
{
    memset(TransitionMask, 0, TOTAL_NUMBER_OF_STATES * TOTAL_NUMBER_OF_STATES);
#define N 0
#define W 1
#define E 2
    // Counters to check if this event has multiple next states for one current state
    int fromN = 0, fromW = 0, fromE = 0;
    for (size_t i = 0; i < Transitions.size(); ++i) {
        switch (Transitions[i]) {
        case State::NORMAL_TO_WARNING: TransitionMask[N][W] = true; ++fromN; break;
        case State::NORMAL_TO_ERROR  : TransitionMask[N][E] = true; ++fromN; break;
        case State::WARNING_TO_NORMAL: TransitionMask[W][N] = true; ++fromW; break;
        case State::WARNING_TO_ERROR : TransitionMask[W][E] = true; ++fromW; break;
        case State::ERROR_TO_WARNING : TransitionMask[E][W] = true; ++fromE; break;
        case State::ERROR_TO_NORMAL  : TransitionMask[E][N] = true; ++fromE; break;
        default: continue;
        }
    }
    if (fromN >= 2 || fromW >= 2 || fromE >= 2) {
        SFLOG_ERROR << "Event transition error: event \"" << Name << "\" has multiple next states" << std::endl;
        SFLOG_ERROR << (int) TransitionMask[N][N] << " " << (int) TransitionMask[N][W] << " " << (int) TransitionMask[N][E] << std::endl;
        SFLOG_ERROR << (int) TransitionMask[W][N] << " " << (int) TransitionMask[W][W] << " " << (int) TransitionMask[W][E] << std::endl;
        SFLOG_ERROR << (int) TransitionMask[E][N] << " " << (int) TransitionMask[E][W] << " " << (int) TransitionMask[E][E] << std::endl;
        throw("Event error");
    }
#undef N
#undef W
#undef E
}

State::TransitionType Event::GetTransition(State::StateType currentState) const
{
    switch (currentState) {
    case State::NORMAL:
        if (TransitionMask[State::NORMAL][State::WARNING])
            return State::NORMAL_TO_WARNING;
        else if (TransitionMask[State::NORMAL][State::ERROR])
            return State::NORMAL_TO_ERROR;
        break;
    case State::WARNING:
        if (TransitionMask[State::WARNING][State::NORMAL])
            return State::WARNING_TO_NORMAL;
        else if (TransitionMask[State::WARNING][State::ERROR])
            return State::WARNING_TO_ERROR;
        break;
    case State::ERROR:
        if (TransitionMask[State::ERROR][State::NORMAL])
            return State::ERROR_TO_NORMAL;
        else if (TransitionMask[State::ERROR][State::WARNING])
            return State::ERROR_TO_WARNING;
        break;
    default:
        return State::INVALID_TRANSITION;
    }

    return State::INVALID_TRANSITION;
}

void Event::ToStream(std::ostream & os) const
{
    os << Name << ": " << Severity << ", [ ";

    for (size_t i = 0; i < Transitions.size(); ++i) {
        switch (Transitions[i]) {
        case State::NORMAL_TO_ERROR  : os << "N2E "; break;
        case State::ERROR_TO_NORMAL  : os << "E2N "; break;
        case State::NORMAL_TO_WARNING: os << "N2W "; break;
        case State::WARNING_TO_NORMAL: os << "W2N "; break;
        case State::WARNING_TO_ERROR : os << "W2E "; break;
        case State::ERROR_TO_WARNING : os << "E2W "; break;
        default:                       os << "INVALID ";
        }
    }
    os << "], ";
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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.

// For information about interceptions as a whole see
// http://wiki/Main/ChromeSandboxInterceptionDesign

#include <set>

#include "sandbox/src/interception.h"

#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "sandbox/src/interception_internal.h"
#include "sandbox/src/sandbox.h"
#include "sandbox/src/sandbox_utils.h"
#include "sandbox/src/service_resolver.h"
#include "sandbox/src/target_interceptions.h"
#include "sandbox/src/target_process.h"
#include "sandbox/src/wow64.h"

namespace {

const char kMapViewOfSectionName[] = "NtMapViewOfSection";
const char kUnmapViewOfSectionName[] = "NtUnmapViewOfSection";

}  // namespace

namespace sandbox {

SANDBOX_INTERCEPT SharedMemory* g_interceptions;

InterceptionManager::InterceptionManager(TargetProcess* child_process,
                                         bool relaxed)
    : child_(child_process), names_used_(false), relaxed_(relaxed) {
  child_->AddRef();
}
InterceptionManager::~InterceptionManager() {
  child_->Release();
}

bool InterceptionManager::AddToPatchedFunctions(
    const wchar_t* dll_name, const char* function_name,
    InterceptionType interception_type, const void* replacement_code_address) {
  InterceptionData function;
  function.type = interception_type;
  function.dll = dll_name;
  function.function = function_name;
  function.interceptor_address = replacement_code_address;

  interceptions_.push_back(function);

  return true;
}

bool InterceptionManager::AddToPatchedFunctions(
    const wchar_t* dll_name, const char* function_name,
    InterceptionType interception_type, const char* replacement_function_name) {
  InterceptionData function;
  function.type = interception_type;
  function.dll = dll_name;
  function.function = function_name;
  function.interceptor = replacement_function_name;
  function.interceptor_address = NULL;

  interceptions_.push_back(function);
  names_used_ = true;

  return true;
}

bool InterceptionManager::InitializeInterceptions() {
  if (interceptions_.empty())
    return true;  // Nothing to do here

  size_t buffer_bytes = GetBufferSize();
  scoped_ptr<char> local_buffer(new char[buffer_bytes]);

  if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes))
    return false;

  void* remote_buffer;
  if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer))
    return false;

  bool hot_patch_needed = (0 != buffer_bytes);
  if (!PatchNtdll(hot_patch_needed))
    return false;

  g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer);
  child_->TransferVariable("sandbox::g_interceptions", &g_interceptions,
                           sizeof(g_interceptions));
  return true;
}

size_t InterceptionManager::GetBufferSize() const {
  std::set<std::wstring> dlls;
  size_t buffer_bytes = 0;

  std::list<InterceptionData>::const_iterator it = interceptions_.begin();
  for (; it != interceptions_.end(); ++it) {
    // skip interceptions that are performed from the parent
    if (!IsInterceptionPerformedByChild(*it))
      continue;

    if (!dlls.count(it->dll)) {
      // NULL terminate the dll name on the structure
      size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t);

      // include the dll related size
      buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) +
                                            dll_name_bytes, sizeof(size_t));
      dlls.insert(it->dll);
    }

    // we have to NULL terminate the strings on the structure
    size_t strings_chars = it->function.size() + it->interceptor.size() + 2;

    // a new FunctionInfo is required per function
    size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars;
    record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t));
    buffer_bytes += record_bytes;
  }

  if (0 != buffer_bytes)
    // add the part of SharedMemory that we have not counted yet
    buffer_bytes += offsetof(SharedMemory, dll_list);

  return buffer_bytes;
}

// Basically, walk the list of interceptions moving them to the config buffer,
// but keeping together all interceptions that belong to the same dll.
// The config buffer is a local buffer, not the one allocated on the child.
bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) {
  if (0 == buffer_bytes)
    return true;

  DCHECK(buffer_bytes > sizeof(SharedMemory));

  SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer);
  DllPatchInfo* dll_info = shared_memory->dll_list;
  int num_dlls = 0;

  shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL;

  buffer_bytes -= offsetof(SharedMemory, dll_list);
  buffer = dll_info;

  std::list<InterceptionData>::iterator it = interceptions_.begin();
  for (; it != interceptions_.end();) {
    // skip interceptions that are performed from the parent
    if (!IsInterceptionPerformedByChild(*it)) {
      ++it;
      continue;
    }

    const std::wstring dll = it->dll;
    if (!SetupDllInfo(*it, &buffer, &buffer_bytes))
      return false;

    // walk the interceptions from this point, saving the ones that are
    // performed on this dll, and removing the entry from the list.
    // advance the iterator before removing the element from the list
    std::list<InterceptionData>::iterator rest = it;
    for (; rest != interceptions_.end();) {
      if (rest->dll == dll) {
        if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info))
          return false;
        if (it == rest)
          ++it;
        rest = interceptions_.erase(rest);
      } else {
        ++rest;
      }
    }
    dll_info = reinterpret_cast<DllPatchInfo*>(buffer);
    ++num_dlls;
  }

  shared_memory->num_intercepted_dlls = num_dlls;
  return true;
}

// Fills up just the part that depends on the dll, not the info that depends on
// the actual interception.
bool InterceptionManager::SetupDllInfo(const InterceptionData& data,
                                       void** buffer,
                                       size_t* buffer_bytes) const {
  DCHECK(buffer_bytes);
  DCHECK(buffer);
  DCHECK(*buffer);

  DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer);

  // the strings have to be zero terminated
  size_t required = offsetof(DllPatchInfo, dll_name) +
                    (data.dll.size() + 1) * sizeof(wchar_t);
  required = RoundUpToMultiple(required, sizeof(size_t));
  if (*buffer_bytes < required)
    return false;

  *buffer_bytes -= required;
  *buffer = reinterpret_cast<char*>(*buffer) + required;

  // set up the dll info to be what we know about it at this time
  dll_info->record_bytes = required;
  dll_info->offset_to_functions = required;
  dll_info->num_functions = 0;
  data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size());
  dll_info->dll_name[data.dll.size()] = L'\0';

  return true;
}

bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data,
                                                void** buffer,
                                                size_t* buffer_bytes,
                                                DllPatchInfo* dll_info) const {
  DCHECK(buffer_bytes);
  DCHECK(buffer);
  DCHECK(*buffer);

  FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer);

  size_t name_bytes = data.function.size();
  size_t interceptor_bytes = data.interceptor.size();

  // the strings at the end of the structure are zero terminated
  size_t required = offsetof(FunctionInfo, function) +
                    name_bytes + interceptor_bytes + 2;
  required = RoundUpToMultiple(required, sizeof(size_t));
  if (*buffer_bytes < required)
    return false;

  // update the caller's values
  *buffer_bytes -= required;
  *buffer = reinterpret_cast<char*>(*buffer) + required;

  function->record_bytes = required;
  function->type = data.type;
  function->interceptor_address = data.interceptor_address;
  char* names = function->function;

  data.function._Copy_s(names, name_bytes, name_bytes);
  names += name_bytes;
  *names++ = '\0';

  // interceptor follows the function_name
  data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes);
  names += interceptor_bytes;
  *names++ = '\0';

  // update the dll table
  dll_info->num_functions++;
  dll_info->record_bytes += required;

  return true;
}

bool InterceptionManager::CopyDataToChild(const void* local_buffer,
                                          size_t buffer_bytes,
                                          void** remote_buffer) const {
  DCHECK(NULL != remote_buffer);
  if (0 == buffer_bytes) {
    *remote_buffer = NULL;
    return true;
  }

  HANDLE child = child_->Process();

  // Allocate memory on the target process without specifying the address
  void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes,
                                       MEM_COMMIT, PAGE_READWRITE);
  if (NULL == remote_data)
    return false;

  SIZE_T bytes_written;
  BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer,
                                      buffer_bytes, &bytes_written);
  if (FALSE == success || bytes_written != buffer_bytes) {
    ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
    return false;
  }

  *remote_buffer = remote_data;

  return true;
}

// Only return true if the child should be able to perform this interception.
bool InterceptionManager::IsInterceptionPerformedByChild(
    const InterceptionData& data) const {
  if (INTERCEPTION_INVALID == data.type)
    return false;

  if (INTERCEPTION_SERVICE_CALL == data.type)
    return false;

  if (data.type >= INTERCEPTION_LAST)
    return false;

  std::wstring ntdll(kNtdllName);
  if (ntdll == data.dll)
    return false;  // ntdll has to be intercepted from the parent

  return true;
}

bool InterceptionManager::PatchNtdll(bool hot_patch_needed) {
  // Maybe there is nothing to do
  if (!hot_patch_needed && interceptions_.empty())
    return true;

  if (hot_patch_needed) {
#if SANDBOX_EXPORTS
    AddToPatchedFunctions(kNtdllName, kMapViewOfSectionName,
                          INTERCEPTION_SERVICE_CALL,
                          "_TargetNtMapViewOfSection@44");
    AddToPatchedFunctions(kNtdllName, kUnmapViewOfSectionName,
                          INTERCEPTION_SERVICE_CALL,
                          "_TargetNtUnmapViewOfSection@12");
#else
    AddToPatchedFunctions(kNtdllName, kMapViewOfSectionName,
                          INTERCEPTION_SERVICE_CALL, &TargetNtMapViewOfSection);
    AddToPatchedFunctions(kNtdllName, kUnmapViewOfSectionName,
                          INTERCEPTION_SERVICE_CALL,
                          &TargetNtUnmapViewOfSection);
#endif
  }

  size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) +
                       sizeof(DllInterceptionData);

  // Allocate memory on the child, without specifying the desired address
  HANDLE child = child_->Process();
  DllInterceptionData* thunks = reinterpret_cast<DllInterceptionData*>(
                                    ::VirtualAllocEx(child, NULL, thunk_bytes,
                                                     MEM_COMMIT,
                                                     PAGE_EXECUTE_READWRITE));

  DllInterceptionData dll_data;
  dll_data.data_bytes = thunk_bytes;
  dll_data.num_thunks = 0;
  dll_data.used_bytes = offsetof(DllInterceptionData, thunks);

  // this should write all the individual thunks to the child's memory
  if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data))
    return false;

  // and now write the first part of the table to the child's memory
  DWORD written;
  bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data,
                                          offsetof(DllInterceptionData, thunks),
                                          &written);

  if (!ok || (offsetof(DllInterceptionData, thunks) != written))
    return false;

  // Attempt to protect all the thunks, but ignore failure
  DWORD old_protection;
  ::VirtualProtectEx(child, thunks, thunk_bytes,
                     PAGE_EXECUTE_READ, &old_protection);

  return true;
}

bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks,
                                               size_t thunk_bytes,
                                               DllInterceptionData* dll_data) {
  DCHECK(NULL != thunks);
  DCHECK(NULL != dll_data);

  HMODULE ntdll_base = ::GetModuleHandle(kNtdllName);
  if (!ntdll_base)
    return false;

  // Bypass purify's interception.
  wchar_t* loader_get = reinterpret_cast<wchar_t*>(
                            ::GetProcAddress(ntdll_base, "LdrGetDllHandle"));
  if (loader_get) {
    if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
                                   GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                               loader_get, &ntdll_base))
      return false;
  }

  Wow64 WowHelper(child_, ntdll_base);
  if (!WowHelper.WaitForNtdll(INFINITE))
    return false;

  char* interceptor_base = NULL;

#if SANDBOX_EXPORTS
  interceptor_base = reinterpret_cast<char*>(child_->MainModule());
  HMODULE local_interceptor = ::LoadLibrary(child_->Name());
#endif

  ServiceResolverThunk* thunk;
  if (WowHelper.IsWow64())
    thunk = new Wow64ResolverThunk(child_->Process(), relaxed_);
  else if (!IsXPSP2OrLater())
    thunk = new Win2kResolverThunk(child_->Process(), relaxed_);
  else
    thunk = new ServiceResolverThunk(child_->Process(), relaxed_);

  std::list<InterceptionData>::iterator it = interceptions_.begin();
  for (; it != interceptions_.end(); ++it) {
    const std::wstring ntdll(kNtdllName);
    if (it->dll != ntdll)
      break;

    if (INTERCEPTION_SERVICE_CALL != it->type)
      break;

#if SANDBOX_EXPORTS
    // We may be trying to patch by function name.
    if (NULL == it->interceptor_address) {
      const char* address;
      NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor,
                                               it->interceptor.c_str(),
                                               reinterpret_cast<const void**>(
                                               &address));
      if (!NT_SUCCESS(ret))
        break;

      // Translate the local address to an address on the child.
      it->interceptor_address = interceptor_base + (address -
                                    reinterpret_cast<char*>(local_interceptor));
    }
#endif
    NTSTATUS ret = thunk->Setup(ntdll_base,
                                interceptor_base,
                                it->function.c_str(),
                                it->interceptor.c_str(),
                                it->interceptor_address,
                                &thunks->thunks[dll_data->num_thunks],
                                thunk_bytes - dll_data->used_bytes,
                                NULL);
    if (!NT_SUCCESS(ret))
      break;

    dll_data->num_thunks++;
    dll_data->used_bytes += sizeof(ThunkData);
  }

  delete(thunk);

#if SANDBOX_EXPORTS
  if (NULL != local_interceptor)
    ::FreeLibrary(local_interceptor);
#endif

  if (it != interceptions_.end())
    return false;

  return true;
}

}  // namespace sandbox

<commit_msg>For some reasons the linker excludes 2 of our hooks when linking in SANDBOX_EXPORTS mode. Add pragmas to be sure these hooks are included.<commit_after>// Copyright (c) 2006-2008 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.

// For information about interceptions as a whole see
// http://wiki/Main/ChromeSandboxInterceptionDesign

#include <set>

#include "sandbox/src/interception.h"

#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "sandbox/src/interception_internal.h"
#include "sandbox/src/sandbox.h"
#include "sandbox/src/sandbox_utils.h"
#include "sandbox/src/service_resolver.h"
#include "sandbox/src/target_interceptions.h"
#include "sandbox/src/target_process.h"
#include "sandbox/src/wow64.h"

namespace {

const char kMapViewOfSectionName[] = "NtMapViewOfSection";
const char kUnmapViewOfSectionName[] = "NtUnmapViewOfSection";

}  // namespace

namespace sandbox {

SANDBOX_INTERCEPT SharedMemory* g_interceptions;

InterceptionManager::InterceptionManager(TargetProcess* child_process,
                                         bool relaxed)
    : child_(child_process), names_used_(false), relaxed_(relaxed) {
  child_->AddRef();
}
InterceptionManager::~InterceptionManager() {
  child_->Release();
}

bool InterceptionManager::AddToPatchedFunctions(
    const wchar_t* dll_name, const char* function_name,
    InterceptionType interception_type, const void* replacement_code_address) {
  InterceptionData function;
  function.type = interception_type;
  function.dll = dll_name;
  function.function = function_name;
  function.interceptor_address = replacement_code_address;

  interceptions_.push_back(function);

  return true;
}

bool InterceptionManager::AddToPatchedFunctions(
    const wchar_t* dll_name, const char* function_name,
    InterceptionType interception_type, const char* replacement_function_name) {
  InterceptionData function;
  function.type = interception_type;
  function.dll = dll_name;
  function.function = function_name;
  function.interceptor = replacement_function_name;
  function.interceptor_address = NULL;

  interceptions_.push_back(function);
  names_used_ = true;

  return true;
}

bool InterceptionManager::InitializeInterceptions() {
  if (interceptions_.empty())
    return true;  // Nothing to do here

  size_t buffer_bytes = GetBufferSize();
  scoped_ptr<char> local_buffer(new char[buffer_bytes]);

  if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes))
    return false;

  void* remote_buffer;
  if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer))
    return false;

  bool hot_patch_needed = (0 != buffer_bytes);
  if (!PatchNtdll(hot_patch_needed))
    return false;

  g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer);
  child_->TransferVariable("sandbox::g_interceptions", &g_interceptions,
                           sizeof(g_interceptions));
  return true;
}

size_t InterceptionManager::GetBufferSize() const {
  std::set<std::wstring> dlls;
  size_t buffer_bytes = 0;

  std::list<InterceptionData>::const_iterator it = interceptions_.begin();
  for (; it != interceptions_.end(); ++it) {
    // skip interceptions that are performed from the parent
    if (!IsInterceptionPerformedByChild(*it))
      continue;

    if (!dlls.count(it->dll)) {
      // NULL terminate the dll name on the structure
      size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t);

      // include the dll related size
      buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) +
                                            dll_name_bytes, sizeof(size_t));
      dlls.insert(it->dll);
    }

    // we have to NULL terminate the strings on the structure
    size_t strings_chars = it->function.size() + it->interceptor.size() + 2;

    // a new FunctionInfo is required per function
    size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars;
    record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t));
    buffer_bytes += record_bytes;
  }

  if (0 != buffer_bytes)
    // add the part of SharedMemory that we have not counted yet
    buffer_bytes += offsetof(SharedMemory, dll_list);

  return buffer_bytes;
}

// Basically, walk the list of interceptions moving them to the config buffer,
// but keeping together all interceptions that belong to the same dll.
// The config buffer is a local buffer, not the one allocated on the child.
bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) {
  if (0 == buffer_bytes)
    return true;

  DCHECK(buffer_bytes > sizeof(SharedMemory));

  SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer);
  DllPatchInfo* dll_info = shared_memory->dll_list;
  int num_dlls = 0;

  shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL;

  buffer_bytes -= offsetof(SharedMemory, dll_list);
  buffer = dll_info;

  std::list<InterceptionData>::iterator it = interceptions_.begin();
  for (; it != interceptions_.end();) {
    // skip interceptions that are performed from the parent
    if (!IsInterceptionPerformedByChild(*it)) {
      ++it;
      continue;
    }

    const std::wstring dll = it->dll;
    if (!SetupDllInfo(*it, &buffer, &buffer_bytes))
      return false;

    // walk the interceptions from this point, saving the ones that are
    // performed on this dll, and removing the entry from the list.
    // advance the iterator before removing the element from the list
    std::list<InterceptionData>::iterator rest = it;
    for (; rest != interceptions_.end();) {
      if (rest->dll == dll) {
        if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info))
          return false;
        if (it == rest)
          ++it;
        rest = interceptions_.erase(rest);
      } else {
        ++rest;
      }
    }
    dll_info = reinterpret_cast<DllPatchInfo*>(buffer);
    ++num_dlls;
  }

  shared_memory->num_intercepted_dlls = num_dlls;
  return true;
}

// Fills up just the part that depends on the dll, not the info that depends on
// the actual interception.
bool InterceptionManager::SetupDllInfo(const InterceptionData& data,
                                       void** buffer,
                                       size_t* buffer_bytes) const {
  DCHECK(buffer_bytes);
  DCHECK(buffer);
  DCHECK(*buffer);

  DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer);

  // the strings have to be zero terminated
  size_t required = offsetof(DllPatchInfo, dll_name) +
                    (data.dll.size() + 1) * sizeof(wchar_t);
  required = RoundUpToMultiple(required, sizeof(size_t));
  if (*buffer_bytes < required)
    return false;

  *buffer_bytes -= required;
  *buffer = reinterpret_cast<char*>(*buffer) + required;

  // set up the dll info to be what we know about it at this time
  dll_info->record_bytes = required;
  dll_info->offset_to_functions = required;
  dll_info->num_functions = 0;
  data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size());
  dll_info->dll_name[data.dll.size()] = L'\0';

  return true;
}

bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data,
                                                void** buffer,
                                                size_t* buffer_bytes,
                                                DllPatchInfo* dll_info) const {
  DCHECK(buffer_bytes);
  DCHECK(buffer);
  DCHECK(*buffer);

  FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer);

  size_t name_bytes = data.function.size();
  size_t interceptor_bytes = data.interceptor.size();

  // the strings at the end of the structure are zero terminated
  size_t required = offsetof(FunctionInfo, function) +
                    name_bytes + interceptor_bytes + 2;
  required = RoundUpToMultiple(required, sizeof(size_t));
  if (*buffer_bytes < required)
    return false;

  // update the caller's values
  *buffer_bytes -= required;
  *buffer = reinterpret_cast<char*>(*buffer) + required;

  function->record_bytes = required;
  function->type = data.type;
  function->interceptor_address = data.interceptor_address;
  char* names = function->function;

  data.function._Copy_s(names, name_bytes, name_bytes);
  names += name_bytes;
  *names++ = '\0';

  // interceptor follows the function_name
  data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes);
  names += interceptor_bytes;
  *names++ = '\0';

  // update the dll table
  dll_info->num_functions++;
  dll_info->record_bytes += required;

  return true;
}

bool InterceptionManager::CopyDataToChild(const void* local_buffer,
                                          size_t buffer_bytes,
                                          void** remote_buffer) const {
  DCHECK(NULL != remote_buffer);
  if (0 == buffer_bytes) {
    *remote_buffer = NULL;
    return true;
  }

  HANDLE child = child_->Process();

  // Allocate memory on the target process without specifying the address
  void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes,
                                       MEM_COMMIT, PAGE_READWRITE);
  if (NULL == remote_data)
    return false;

  SIZE_T bytes_written;
  BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer,
                                      buffer_bytes, &bytes_written);
  if (FALSE == success || bytes_written != buffer_bytes) {
    ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
    return false;
  }

  *remote_buffer = remote_data;

  return true;
}

// Only return true if the child should be able to perform this interception.
bool InterceptionManager::IsInterceptionPerformedByChild(
    const InterceptionData& data) const {
  if (INTERCEPTION_INVALID == data.type)
    return false;

  if (INTERCEPTION_SERVICE_CALL == data.type)
    return false;

  if (data.type >= INTERCEPTION_LAST)
    return false;

  std::wstring ntdll(kNtdllName);
  if (ntdll == data.dll)
    return false;  // ntdll has to be intercepted from the parent

  return true;
}

bool InterceptionManager::PatchNtdll(bool hot_patch_needed) {
  // Maybe there is nothing to do
  if (!hot_patch_needed && interceptions_.empty())
    return true;

  if (hot_patch_needed) {
#if SANDBOX_EXPORTS
    // Make sure the functions are not excluded by the linker.
    #pragma comment(linker, "/include:_TargetNtMapViewOfSection@44")
    #pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12")

    AddToPatchedFunctions(kNtdllName, kMapViewOfSectionName,
                          INTERCEPTION_SERVICE_CALL,
                          "_TargetNtMapViewOfSection@44");
    AddToPatchedFunctions(kNtdllName, kUnmapViewOfSectionName,
                          INTERCEPTION_SERVICE_CALL,
                          "_TargetNtUnmapViewOfSection@12");
#else
    AddToPatchedFunctions(kNtdllName, kMapViewOfSectionName,
                          INTERCEPTION_SERVICE_CALL, &TargetNtMapViewOfSection);
    AddToPatchedFunctions(kNtdllName, kUnmapViewOfSectionName,
                          INTERCEPTION_SERVICE_CALL,
                          &TargetNtUnmapViewOfSection);
#endif
  }

  size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) +
                       sizeof(DllInterceptionData);

  // Allocate memory on the child, without specifying the desired address
  HANDLE child = child_->Process();
  DllInterceptionData* thunks = reinterpret_cast<DllInterceptionData*>(
                                    ::VirtualAllocEx(child, NULL, thunk_bytes,
                                                     MEM_COMMIT,
                                                     PAGE_EXECUTE_READWRITE));

  DllInterceptionData dll_data;
  dll_data.data_bytes = thunk_bytes;
  dll_data.num_thunks = 0;
  dll_data.used_bytes = offsetof(DllInterceptionData, thunks);

  // this should write all the individual thunks to the child's memory
  if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data))
    return false;

  // and now write the first part of the table to the child's memory
  DWORD written;
  bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data,
                                          offsetof(DllInterceptionData, thunks),
                                          &written);

  if (!ok || (offsetof(DllInterceptionData, thunks) != written))
    return false;

  // Attempt to protect all the thunks, but ignore failure
  DWORD old_protection;
  ::VirtualProtectEx(child, thunks, thunk_bytes,
                     PAGE_EXECUTE_READ, &old_protection);

  return true;
}

bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks,
                                               size_t thunk_bytes,
                                               DllInterceptionData* dll_data) {
  DCHECK(NULL != thunks);
  DCHECK(NULL != dll_data);

  HMODULE ntdll_base = ::GetModuleHandle(kNtdllName);
  if (!ntdll_base)
    return false;

  // Bypass purify's interception.
  wchar_t* loader_get = reinterpret_cast<wchar_t*>(
                            ::GetProcAddress(ntdll_base, "LdrGetDllHandle"));
  if (loader_get) {
    if (!GetModuleHandleHelper(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
                                   GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
                               loader_get, &ntdll_base))
      return false;
  }

  Wow64 WowHelper(child_, ntdll_base);
  if (!WowHelper.WaitForNtdll(INFINITE))
    return false;

  char* interceptor_base = NULL;

#if SANDBOX_EXPORTS
  interceptor_base = reinterpret_cast<char*>(child_->MainModule());
  HMODULE local_interceptor = ::LoadLibrary(child_->Name());
#endif

  ServiceResolverThunk* thunk;
  if (WowHelper.IsWow64())
    thunk = new Wow64ResolverThunk(child_->Process(), relaxed_);
  else if (!IsXPSP2OrLater())
    thunk = new Win2kResolverThunk(child_->Process(), relaxed_);
  else
    thunk = new ServiceResolverThunk(child_->Process(), relaxed_);

  std::list<InterceptionData>::iterator it = interceptions_.begin();
  for (; it != interceptions_.end(); ++it) {
    const std::wstring ntdll(kNtdllName);
    if (it->dll != ntdll)
      break;

    if (INTERCEPTION_SERVICE_CALL != it->type)
      break;

#if SANDBOX_EXPORTS
    // We may be trying to patch by function name.
    if (NULL == it->interceptor_address) {
      const char* address;
      NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor,
                                               it->interceptor.c_str(),
                                               reinterpret_cast<const void**>(
                                               &address));
      if (!NT_SUCCESS(ret))
        break;

      // Translate the local address to an address on the child.
      it->interceptor_address = interceptor_base + (address -
                                    reinterpret_cast<char*>(local_interceptor));
    }
#endif
    NTSTATUS ret = thunk->Setup(ntdll_base,
                                interceptor_base,
                                it->function.c_str(),
                                it->interceptor.c_str(),
                                it->interceptor_address,
                                &thunks->thunks[dll_data->num_thunks],
                                thunk_bytes - dll_data->used_bytes,
                                NULL);
    if (!NT_SUCCESS(ret))
      break;

    dll_data->num_thunks++;
    dll_data->used_bytes += sizeof(ThunkData);
  }

  delete(thunk);

#if SANDBOX_EXPORTS
  if (NULL != local_interceptor)
    ::FreeLibrary(local_interceptor);
#endif

  if (it != interceptions_.end())
    return false;

  return true;
}

}  // namespace sandbox

<|endoftext|>


<commit_before>/**
 * \file
 * \brief staticThreadBlinker example application
 *
 * \author Copyright (C) 2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "distortos/distortosConfiguration.h"

#if defined(CONFIG_BOARD_LEDS_ENABLE) && CONFIG_BOARD_TOTAL_LEDS >= 1

#include "distortos/board/leds.hpp"

#include "distortos/chip/ChipOutputPin.hpp"

#endif	// defined(CONFIG_BOARD_LEDS_ENABLE) && CONFIG_BOARD_TOTAL_LEDS >= 1

#include "distortos/StaticThread.hpp"
#include "distortos/ThisThread.hpp"

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

#if defined(CONFIG_BOARD_LEDS_ENABLE) && CONFIG_BOARD_TOTAL_LEDS >= 1

/**
 * \brief LED blinking function
 *
 * Constantly toggles state of \a led and waits for half of \a periodMs.
 *
 * \param [in] led is a reference to distortos::devices::OutputPin object which will be toggled by this function
 * \param [in] periodMs is a full (on -> off -> on) period of toggling, milliseconds
 */

void ledBlinkerFunction(distortos::devices::OutputPin& led, const std::chrono::milliseconds periodMs)
{
	while (1)
	{
		led.set(!led.get());	// invert state of LED
		distortos::ThisThread::sleepFor(periodMs / 2);
	}
}

#endif	// defined(CONFIG_BOARD_LEDS_ENABLE) && CONFIG_BOARD_TOTAL_LEDS >= 1

/**
 * \brief Boolean variable "blinking" function
 *
 * Constantly toggles value of \a variable and waits for half of \a periodMs. This function is meant as a demonstration
 * for configuration which either has no LEDs or doesn't enable them.
 *
 * \param [in] variable is a reference to bool variable which will be toggled by this function
 * \param [in] periodMs is a full (true -> false -> true) period of toggling, milliseconds
 */

void variableBlinkerFunction(bool& variable, const std::chrono::milliseconds periodMs)
{
	while (1)
	{
		variable = !variable;	// invert state of variable
		distortos::ThisThread::sleepFor(periodMs / 2);
	}
}

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Main code block of staticThreadBlinker application
 *
 * This example application tries to demonstrate the most basic aspects of static threads:
 * - creating and starting them,
 * - passing arguments to thread's function by reference and by value,
 * - joining them.
 *
 * If the configured board has no LEDs or if they are not enabled in the configuration, only one dummy thread is
 * created - it "blinks" a boolean variable instead of a real LED. Otherwise up to four additional threads are created
 * (but no more than the number of LEDs on the board - CONFIG_BOARD_TOTAL_LEDS) - each one blinks its own LED (which was
 * passed to the thread's function by reference) with provided period (passed by value). The periods of blinking are
 * slightly different for each thread, so if there are multiple LEDs they are not in sync with each other. The periods
 * are actually prime numbers, so they create very long "global" period (in which the whole pattern repeats).
 *
 * Even though all created threads never terminate (their functions have infinite loops and never return), main thread
 * calls Thread::join() for each of them anyway. This is a good and safe practice, which also happens to be the easiest
 * way to suspend main thread in this example application.
 *
 * \return doesn't return
 */

int main()
{
	bool variable {};
	// create and immediately start static thread with 1024 bytes of stack, low priority (1), variableBlinkerFunction()
	// will get variable by reference and period by value
	auto variableBlinkerThread = distortos::makeAndStartStaticThread<1024>(1, variableBlinkerFunction,
			std::ref(variable), std::chrono::milliseconds{401});

#if defined(CONFIG_BOARD_LEDS_ENABLE) && CONFIG_BOARD_TOTAL_LEDS >= 1

	// create and immediately start static thread with 1024 bytes of stack, low priority (1), ledBlinkerFunction() will
	// get its own LED by reference and period by value
	auto ledBlinkerThread0 = distortos::makeAndStartStaticThread<1024>(1, ledBlinkerFunction,
			std::ref(distortos::board::leds[0]), std::chrono::milliseconds{397});

#	if CONFIG_BOARD_TOTAL_LEDS >= 2

	auto ledBlinkerThread1 = distortos::makeAndStartStaticThread<1024>(1, ledBlinkerFunction,
			std::ref(distortos::board::leds[1]), std::chrono::milliseconds{389});

#		if CONFIG_BOARD_TOTAL_LEDS >= 3

	auto ledBlinkerThread2 = distortos::makeAndStartStaticThread<1024>(1, ledBlinkerFunction,
			std::ref(distortos::board::leds[2]), std::chrono::milliseconds{383});

#			if CONFIG_BOARD_TOTAL_LEDS >= 4

	auto ledBlinkerThread3 = distortos::makeAndStartStaticThread<1024>(1, ledBlinkerFunction,
			std::ref(distortos::board::leds[3]), std::chrono::milliseconds{379});

	ledBlinkerThread3.join();

#			endif	// CONFIG_BOARD_TOTAL_LEDS >= 4

	ledBlinkerThread2.join();

#		endif	// CONFIG_BOARD_TOTAL_LEDS >= 3

	ledBlinkerThread1.join();

#	endif	// CONFIG_BOARD_TOTAL_LEDS >= 2

	ledBlinkerThread0.join();

#endif	// defined(CONFIG_BOARD_LEDS_ENABLE) && CONFIG_BOARD_TOTAL_LEDS >= 1

	variableBlinkerThread.join();

	return 0;
}
<commit_msg>Simplify preprocessor conditions in staticThreadBlinker<commit_after>/**
 * \file
 * \brief staticThreadBlinker example application
 *
 * \author Copyright (C) 2016-2017 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "distortos/distortosConfiguration.h"

#if defined(CONFIG_BOARD_LEDS_ENABLE)

#include "distortos/board/leds.hpp"

#include "distortos/chip/ChipOutputPin.hpp"

#endif	// defined(CONFIG_BOARD_LEDS_ENABLE)

#include "distortos/StaticThread.hpp"
#include "distortos/ThisThread.hpp"

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

#if defined(CONFIG_BOARD_LEDS_ENABLE)

/**
 * \brief LED blinking function
 *
 * Constantly toggles state of \a led and waits for half of \a periodMs.
 *
 * \param [in] led is a reference to distortos::devices::OutputPin object which will be toggled by this function
 * \param [in] periodMs is a full (on -> off -> on) period of toggling, milliseconds
 */

void ledBlinkerFunction(distortos::devices::OutputPin& led, const std::chrono::milliseconds periodMs)
{
	while (1)
	{
		led.set(!led.get());	// invert state of LED
		distortos::ThisThread::sleepFor(periodMs / 2);
	}
}

#endif	// defined(CONFIG_BOARD_LEDS_ENABLE)

/**
 * \brief Boolean variable "blinking" function
 *
 * Constantly toggles value of \a variable and waits for half of \a periodMs. This function is meant as a demonstration
 * for configuration which either has no LEDs or doesn't enable them.
 *
 * \param [in] variable is a reference to bool variable which will be toggled by this function
 * \param [in] periodMs is a full (true -> false -> true) period of toggling, milliseconds
 */

void variableBlinkerFunction(bool& variable, const std::chrono::milliseconds periodMs)
{
	while (1)
	{
		variable = !variable;	// invert state of variable
		distortos::ThisThread::sleepFor(periodMs / 2);
	}
}

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Main code block of staticThreadBlinker application
 *
 * This example application tries to demonstrate the most basic aspects of static threads:
 * - creating and starting them,
 * - passing arguments to thread's function by reference and by value,
 * - joining them.
 *
 * If the configured board has no LEDs or if they are not enabled in the configuration, only one dummy thread is
 * created - it "blinks" a boolean variable instead of a real LED. Otherwise up to four additional threads are created
 * (but no more than the number of LEDs on the board - CONFIG_BOARD_TOTAL_LEDS) - each one blinks its own LED (which was
 * passed to the thread's function by reference) with provided period (passed by value). The periods of blinking are
 * slightly different for each thread, so if there are multiple LEDs they are not in sync with each other. The periods
 * are actually prime numbers, so they create very long "global" period (in which the whole pattern repeats).
 *
 * Even though all created threads never terminate (their functions have infinite loops and never return), main thread
 * calls Thread::join() for each of them anyway. This is a good and safe practice, which also happens to be the easiest
 * way to suspend main thread in this example application.
 *
 * \return doesn't return
 */

int main()
{
	bool variable {};
	// create and immediately start static thread with 1024 bytes of stack, low priority (1), variableBlinkerFunction()
	// will get variable by reference and period by value
	auto variableBlinkerThread = distortos::makeAndStartStaticThread<1024>(1, variableBlinkerFunction,
			std::ref(variable), std::chrono::milliseconds{401});

#if defined(CONFIG_BOARD_LEDS_ENABLE) && CONFIG_BOARD_TOTAL_LEDS >= 1

	// create and immediately start static thread with 1024 bytes of stack, low priority (1), ledBlinkerFunction() will
	// get its own LED by reference and period by value
	auto ledBlinkerThread0 = distortos::makeAndStartStaticThread<1024>(1, ledBlinkerFunction,
			std::ref(distortos::board::leds[0]), std::chrono::milliseconds{397});

#	if CONFIG_BOARD_TOTAL_LEDS >= 2

	auto ledBlinkerThread1 = distortos::makeAndStartStaticThread<1024>(1, ledBlinkerFunction,
			std::ref(distortos::board::leds[1]), std::chrono::milliseconds{389});

#		if CONFIG_BOARD_TOTAL_LEDS >= 3

	auto ledBlinkerThread2 = distortos::makeAndStartStaticThread<1024>(1, ledBlinkerFunction,
			std::ref(distortos::board::leds[2]), std::chrono::milliseconds{383});

#			if CONFIG_BOARD_TOTAL_LEDS >= 4

	auto ledBlinkerThread3 = distortos::makeAndStartStaticThread<1024>(1, ledBlinkerFunction,
			std::ref(distortos::board::leds[3]), std::chrono::milliseconds{379});

	ledBlinkerThread3.join();

#			endif	// CONFIG_BOARD_TOTAL_LEDS >= 4

	ledBlinkerThread2.join();

#		endif	// CONFIG_BOARD_TOTAL_LEDS >= 3

	ledBlinkerThread1.join();

#	endif	// CONFIG_BOARD_TOTAL_LEDS >= 2

	ledBlinkerThread0.join();

#endif	// defined(CONFIG_BOARD_LEDS_ENABLE) && CONFIG_BOARD_TOTAL_LEDS >= 1

	variableBlinkerThread.join();

	return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "browser/linux/libnotify_notification.h"

#include "base/files/file_enumerator.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "browser/notification_delegate.h"
#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
#include "common/application_info.h"
#include "third_party/skia/include/core/SkBitmap.h"

namespace brightray {

namespace {
LibNotifyLoader libnotify_loader_;

bool HasCapability(const std::string& capability) {
  bool result = false;
  GList* capabilities = libnotify_loader_.notify_get_server_caps();

  if (g_list_find_custom(capabilities, capability.c_str(), (GCompareFunc) g_strcmp0) != NULL)
    result = true;

  g_list_free_full(capabilities, g_free);

  return result;
}

bool NotifierSupportsActions() {
  if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER"))
    return false;

  static bool notify_has_result = false;
  static bool notify_result = false;
  GList *capabilities = NULL;

  if (notify_has_result)
    return notify_result;

  notify_result = HasCapability("actions");
  return notify_result;
}

void log_and_clear_error(GError* error, const char* context) {
  LOG(ERROR) << context
             << ": domain=" << error->domain
             << " code=" << error->code
             << " message=\"" << error->message << '"';
  g_error_free(error);
}

}  // namespace

// static
Notification* Notification::Create(NotificationDelegate* delegate,
                                   NotificationPresenter* presenter) {
  return new LibnotifyNotification(delegate, presenter);
}

// static
bool LibnotifyNotification::Initialize() {
  if (!libnotify_loader_.Load("libnotify.so.4") &&
      !libnotify_loader_.Load("libnotify.so.1") &&
      !libnotify_loader_.Load("libnotify.so")) {
    return false;
  }
  if (!libnotify_loader_.notify_is_initted() &&
      !libnotify_loader_.notify_init(GetApplicationName().c_str())) {
    return false;
  }
  return true;
}

LibnotifyNotification::LibnotifyNotification(NotificationDelegate* delegate,
                                             NotificationPresenter* presenter)
    : Notification(delegate, presenter),
      notification_(nullptr) {
}

LibnotifyNotification::~LibnotifyNotification() {
  g_signal_handlers_disconnect_by_data(notification_, this);
  g_object_unref(notification_);
}

void LibnotifyNotification::Show(const base::string16& title,
                                 const base::string16& body,
                                 const std::string& tag,
                                 const GURL& icon_url,
                                 const SkBitmap& icon,
                                 const bool silent) {
  notification_ = libnotify_loader_.notify_notification_new(
      base::UTF16ToUTF8(title).c_str(),
      base::UTF16ToUTF8(body).c_str(),
      nullptr);

  g_signal_connect(
      notification_, "closed", G_CALLBACK(OnNotificationClosedThunk), this);

  // NB: On Unity and on any other DE using Notify-OSD, adding a notification
  // action will cause the notification to display as a modal dialog box.
  if (NotifierSupportsActions()) {
    libnotify_loader_.notify_notification_add_action(
        notification_, "default", "View", OnNotificationViewThunk, this,
        nullptr);
  }

  if (!icon.drawsNothing()) {
    GdkPixbuf* pixbuf = libgtk2ui::GdkPixbufFromSkBitmap(icon);
    libnotify_loader_.notify_notification_set_image_from_pixbuf(
        notification_, pixbuf);
    libnotify_loader_.notify_notification_set_timeout(
        notification_, NOTIFY_EXPIRES_DEFAULT);
    g_object_unref(pixbuf);
  }

  if (!tag.empty()) {
    GQuark id = g_quark_from_string(tag.c_str());
    g_object_set(G_OBJECT(notification_), "id", id, NULL);
  }

  GError* error = nullptr;
  libnotify_loader_.notify_notification_show(notification_, &error);
  if (error) {
    log_and_clear_error(error, "notify_notification_show");
    NotificationFailed();
    return;
  }

  delegate()->NotificationDisplayed();
}

void LibnotifyNotification::Dismiss() {
  GError* error = nullptr;
  libnotify_loader_.notify_notification_close(notification_, &error);
  if (error) {
    log_and_clear_error(error, "notify_notification_close");
    Destroy();
  }
}

void LibnotifyNotification::OnNotificationClosed(
    NotifyNotification* notification) {
  delegate()->NotificationClosed();
  Destroy();
}

void LibnotifyNotification::OnNotificationView(
    NotifyNotification* notification, char* action) {
  delegate()->NotificationClick();
  Destroy();
}

void LibnotifyNotification::NotificationFailed() {
  delegate()->NotificationFailed();
  Destroy();
}

}  // namespace brightray
<commit_msg>LibnotifyNotification: use "append" when supported<commit_after>// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "browser/linux/libnotify_notification.h"

#include "base/files/file_enumerator.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "browser/notification_delegate.h"
#include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
#include "common/application_info.h"
#include "third_party/skia/include/core/SkBitmap.h"

namespace brightray {

namespace {
LibNotifyLoader libnotify_loader_;

bool HasCapability(const std::string& capability) {
  bool result = false;
  GList* capabilities = libnotify_loader_.notify_get_server_caps();

  if (g_list_find_custom(capabilities, capability.c_str(), (GCompareFunc) g_strcmp0) != NULL)
    result = true;

  g_list_free_full(capabilities, g_free);

  return result;
}

bool NotifierSupportsActions() {
  if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER"))
    return false;

  static bool notify_has_result = false;
  static bool notify_result = false;
  GList *capabilities = NULL;

  if (notify_has_result)
    return notify_result;

  notify_result = HasCapability("actions");
  return notify_result;
}

void log_and_clear_error(GError* error, const char* context) {
  LOG(ERROR) << context
             << ": domain=" << error->domain
             << " code=" << error->code
             << " message=\"" << error->message << '"';
  g_error_free(error);
}

}  // namespace

// static
Notification* Notification::Create(NotificationDelegate* delegate,
                                   NotificationPresenter* presenter) {
  return new LibnotifyNotification(delegate, presenter);
}

// static
bool LibnotifyNotification::Initialize() {
  if (!libnotify_loader_.Load("libnotify.so.4") &&
      !libnotify_loader_.Load("libnotify.so.1") &&
      !libnotify_loader_.Load("libnotify.so")) {
    return false;
  }
  if (!libnotify_loader_.notify_is_initted() &&
      !libnotify_loader_.notify_init(GetApplicationName().c_str())) {
    return false;
  }
  return true;
}

LibnotifyNotification::LibnotifyNotification(NotificationDelegate* delegate,
                                             NotificationPresenter* presenter)
    : Notification(delegate, presenter),
      notification_(nullptr) {
}

LibnotifyNotification::~LibnotifyNotification() {
  g_signal_handlers_disconnect_by_data(notification_, this);
  g_object_unref(notification_);
}

void LibnotifyNotification::Show(const base::string16& title,
                                 const base::string16& body,
                                 const std::string& tag,
                                 const GURL& icon_url,
                                 const SkBitmap& icon,
                                 const bool silent) {
  notification_ = libnotify_loader_.notify_notification_new(
      base::UTF16ToUTF8(title).c_str(),
      base::UTF16ToUTF8(body).c_str(),
      nullptr);

  g_signal_connect(
      notification_, "closed", G_CALLBACK(OnNotificationClosedThunk), this);

  // NB: On Unity and on any other DE using Notify-OSD, adding a notification
  // action will cause the notification to display as a modal dialog box.
  if (NotifierSupportsActions()) {
    libnotify_loader_.notify_notification_add_action(
        notification_, "default", "View", OnNotificationViewThunk, this,
        nullptr);
  }

  if (!icon.drawsNothing()) {
    GdkPixbuf* pixbuf = libgtk2ui::GdkPixbufFromSkBitmap(icon);
    libnotify_loader_.notify_notification_set_image_from_pixbuf(
        notification_, pixbuf);
    libnotify_loader_.notify_notification_set_timeout(
        notification_, NOTIFY_EXPIRES_DEFAULT);
    g_object_unref(pixbuf);
  }

  if (!tag.empty()) {
    GQuark id = g_quark_from_string(tag.c_str());
    g_object_set(G_OBJECT(notification_), "id", id, NULL);
  }

  // Always try to append notifications.
  // Unique tags can be used to prevent this.
  if (HasCapability("append")) {
    libnotify_loader_.notify_notification_set_hint_string(
        notification_, "append", "true");
  } else if (HasCapability("x-canonical-append")) {
    libnotify_loader_.notify_notification_set_hint_string(
        notification_, "x-canonical-append", "true");
  }

  GError* error = nullptr;
  libnotify_loader_.notify_notification_show(notification_, &error);
  if (error) {
    log_and_clear_error(error, "notify_notification_show");
    NotificationFailed();
    return;
  }

  delegate()->NotificationDisplayed();
}

void LibnotifyNotification::Dismiss() {
  GError* error = nullptr;
  libnotify_loader_.notify_notification_close(notification_, &error);
  if (error) {
    log_and_clear_error(error, "notify_notification_close");
    Destroy();
  }
}

void LibnotifyNotification::OnNotificationClosed(
    NotifyNotification* notification) {
  delegate()->NotificationClosed();
  Destroy();
}

void LibnotifyNotification::OnNotificationView(
    NotifyNotification* notification, char* action) {
  delegate()->NotificationClick();
  Destroy();
}

void LibnotifyNotification::NotificationFailed() {
  delegate()->NotificationFailed();
  Destroy();
}

}  // namespace brightray
<|endoftext|>