<commit_before>// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp

#include <Nazara/Core/Error.hpp>
#include <type_traits>

namespace Ndk
{
	/*!
	* \brief Constructs a World object
	*
	* \param addDefaultSystems Should default provided systems be used
	*/

	inline World::World(bool addDefaultSystems)
	{
		if (addDefaultSystems)
			AddDefaultSystems();
	}

	/*!
	* \brief Constructs a World object by move semantic
	*
	* \param world World to move into this
	*/

	inline World::World(World&& world) noexcept :
	HandledObject(std::move(world))
	{
		operator=(std::move(world));
	}

	/*!
	* \brief Adds a system to the world
	* \return A reference to the newly created system
	*
	* \param system System to add to the world
	*/

	inline BaseSystem& World::AddSystem(std::unique_ptr<BaseSystem>&& system)
	{
		NazaraAssert(system, "System must be valid");

		SystemIndex index = system->GetIndex();

		// We must ensure that the vector is big enough to hold the new system
		if (index >= m_systems.size())
			m_systems.resize(index + 1);

		// Affectation and return of system
		m_systems[index] = std::move(system);
		m_systems[index]->SetWorld(this);

		Invalidate(); // We force an update for every entities
		InvalidateSystemOrder(); // And regenerate the system update list

		return *m_systems[index].get();
	}

	/*!
	* \brief Adds a system to the world
	* \return A reference to the newly created system
	*
	* \param args Arguments used to create the system
	*/

	template<typename SystemType, typename... Args>
	SystemType& World::AddSystem(Args&&... args)
	{
		static_assert(std::is_base_of<BaseSystem, SystemType>::value, "SystemType is not a component");

		// Allocation and affectation of the system
		std::unique_ptr<SystemType> ptr(new SystemType(std::forward<Args>(args)...));
		return static_cast<SystemType&>(AddSystem(std::move(ptr)));
	}

	/*!
	* \brief Creates multiple entities in the world
	* \return The set of entities created
	*
	* \param count Number of entities to create
	*/

	inline World::EntityVector World::CreateEntities(unsigned int count)
	{
		EntityVector list;
		list.reserve(count);

		for (unsigned int i = 0; i < count; ++i)
			list.emplace_back(CreateEntity());

		return list;
	}

	/*!
	* \brief Gets every entities in the world
	* \return A constant reference to the entities
	*/

	inline const EntityList& World::GetEntities() const
	{
		return m_aliveEntities;
	}

	/*!
	* \brief Gets a system in the world by index
	* \return A reference to the system
	*
	* \param index Index of the system
	*
	* \remark Produces a NazaraAssert if system is not available in this world
	*/

	inline BaseSystem& World::GetSystem(SystemIndex index)
	{
		NazaraAssert(HasSystem(index), "This system is not part of the world");

		BaseSystem* system = m_systems[index].get();
		NazaraAssert(system, "Invalid system pointer");

		return *system;
	}

	/*!
	* \brief Gets a system in the world by type
	* \return A reference to the system
	*
	* \remark Produces a NazaraAssert if system is not available in this world
	*/

	template<typename SystemType>
	SystemType& World::GetSystem()
	{
		static_assert(std::is_base_of<BaseSystem, SystemType>::value, "SystemType is not a system");

		SystemIndex index = GetSystemIndex<SystemType>();
		return static_cast<SystemType&>(GetSystem(index));
	}

	/*!
	* \brief Checks whether or not a system is present in the world by index
	* \return true If it is the case
	*
	* \param index Index of the system
	*/

	inline bool World::HasSystem(SystemIndex index) const
	{
		return index < m_systems.size() && m_systems[index];
	}

	/*!
	* \brief Checks whether or not a system is present in the world by type
	* \return true If it is the case
	*/

	template<typename SystemType>
	bool World::HasSystem() const
	{
		static_assert(std::is_base_of<BaseSystem, SystemType>::value, "SystemType is not a component");

		SystemIndex index = GetSystemIndex<SystemType>();
		return HasSystem(index);
	}

	/*!
	* \brief Marks an entity for deletion
	*
	* \param Pointer to the entity
	*
	* \remark If the entity pointer is invalid, nothing is done
	* \remark For safety, entities are not killed until the next world update
	*/
	inline void World::KillEntity(Entity* entity)
	{
		if (IsEntityValid(entity))
			m_killedEntities.UnboundedSet(entity->GetId(), true);
	}

	/*!
	* \brief Kills a set of entities
	*
	* This function has the same effect as calling KillEntity for every entity contained in the vector
	*
	* \param list Set of entities to kill
	*/
	inline void World::KillEntities(const EntityVector& list)
	{
		for (const EntityHandle& entity : list)
			KillEntity(entity);
	}

	/*!
	* \brief Gets an entity
	* \return A constant reference to a handle of the entity
	*
	* \param id Identifier of the entity
	*
	* \remark Handle referenced by this function can move in memory when updating the world, do not keep a reference to a handle from a world update to another
	* \remark If an invalid identifier is provided, an error got triggered and an invalid handle is returned
	*/
	inline const EntityHandle& World::GetEntity(EntityId id)
	{
		if (IsEntityIdValid(id))
			return m_entityBlocks[id]->handle;
		else
		{
			NazaraError("Invalid ID");
			return EntityHandle::InvalidHandle;
		}
	}

	/*!
	* \brief Checks whether or not an entity is valid
	* \return true If it is the case
	*
	* \param entity Pointer to the entity
	*/

	inline bool World::IsEntityValid(const Entity* entity) const
	{
		return entity && entity->GetWorld() == this && IsEntityIdValid(entity->GetId());
	}

	/*!
	* \brief Checks whether or not an entity is valid
	* \return true If it is the case
	*
	* \param id Identifier of the entity
	*/

	inline bool World::IsEntityIdValid(EntityId id) const
	{
		return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid();
	}

	/*!
	* \brief Removes each system from the world
	*/

	inline void World::RemoveAllSystems()
	{
		m_systems.clear();

		InvalidateSystemOrder();
	}

	/*!
	* \brief Removes a system from the world by index
	*
	* \param index Index of the system
	*
	* \remark No change is done if system is not present
	*/

	inline void World::RemoveSystem(SystemIndex index)
	{
		if (HasSystem(index))
		{
			m_systems[index].reset();

			InvalidateSystemOrder();
		}
	}

	/*!
	* \brief Removes a system from the world by type
	*/

	template<typename SystemType>
	void World::RemoveSystem()
	{
		static_assert(std::is_base_of<BaseSystem, SystemType>(), "SystemType is not a system");

		SystemIndex index = GetSystemIndex<SystemType>();
		RemoveSystem(index);
	}

	/*!
	* \brief Updates the world
	*
	* \param elapsedTime Delta time used for the update
	*/

	inline void World::Update(float elapsedTime)
	{
		Update(); //< Update entities

		// And then update systems
		for (auto& systemPtr : m_orderedSystems)
			systemPtr->Update(elapsedTime);
	}

	/*!
	* \brief Moves a world into another world object
	* \return A reference to the object
	*/

	inline World& World::operator=(World&& world) noexcept
	{
		m_aliveEntities         = std::move(world.m_aliveEntities);
		m_dirtyEntities         = std::move(world.m_dirtyEntities);
		m_entityBlocks          = std::move(world.m_entityBlocks);
		m_freeIdList            = std::move(world.m_freeIdList);
		m_killedEntities        = std::move(world.m_killedEntities);
		m_orderedSystems        = std::move(world.m_orderedSystems);
		m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
		m_waitingEntities       = std::move(world.m_waitingEntities);

		m_entities = std::move(world.m_entities);
		for (EntityBlock& block : m_entities)
			block.entity.SetWorld(this);

		m_systems = std::move(world.m_systems);
		for (const auto& systemPtr : m_systems)
			systemPtr->SetWorld(this);

		return *this;
	}

	inline void World::Invalidate()
	{
		m_dirtyEntities.Resize(m_entityBlocks.size(), false);
		m_dirtyEntities.Set(true); // Activation of all bits
	}

	inline void World::Invalidate(EntityId id)
	{
		m_dirtyEntities.UnboundedSet(id, true);
	}

	inline void World::InvalidateSystemOrder()
	{
		m_orderedSystemsUpdated = false;
	}
}
<commit_msg>Sdk/World: Fix movement<commit_after>// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp

#include <Nazara/Core/Error.hpp>
#include <type_traits>

namespace Ndk
{
	/*!
	* \brief Constructs a World object
	*
	* \param addDefaultSystems Should default provided systems be used
	*/

	inline World::World(bool addDefaultSystems)
	{
		if (addDefaultSystems)
			AddDefaultSystems();
	}

	/*!
	* \brief Constructs a World object by move semantic
	*
	* \param world World to move into this
	*/

	inline World::World(World&& world) noexcept :
	HandledObject(std::move(world))
	{
		operator=(std::move(world));
	}

	/*!
	* \brief Adds a system to the world
	* \return A reference to the newly created system
	*
	* \param system System to add to the world
	*/

	inline BaseSystem& World::AddSystem(std::unique_ptr<BaseSystem>&& system)
	{
		NazaraAssert(system, "System must be valid");

		SystemIndex index = system->GetIndex();

		// We must ensure that the vector is big enough to hold the new system
		if (index >= m_systems.size())
			m_systems.resize(index + 1);

		// Affectation and return of system
		m_systems[index] = std::move(system);
		m_systems[index]->SetWorld(this);

		Invalidate(); // We force an update for every entities
		InvalidateSystemOrder(); // And regenerate the system update list

		return *m_systems[index].get();
	}

	/*!
	* \brief Adds a system to the world
	* \return A reference to the newly created system
	*
	* \param args Arguments used to create the system
	*/

	template<typename SystemType, typename... Args>
	SystemType& World::AddSystem(Args&&... args)
	{
		static_assert(std::is_base_of<BaseSystem, SystemType>::value, "SystemType is not a component");

		// Allocation and affectation of the system
		std::unique_ptr<SystemType> ptr(new SystemType(std::forward<Args>(args)...));
		return static_cast<SystemType&>(AddSystem(std::move(ptr)));
	}

	/*!
	* \brief Creates multiple entities in the world
	* \return The set of entities created
	*
	* \param count Number of entities to create
	*/

	inline World::EntityVector World::CreateEntities(unsigned int count)
	{
		EntityVector list;
		list.reserve(count);

		for (unsigned int i = 0; i < count; ++i)
			list.emplace_back(CreateEntity());

		return list;
	}

	/*!
	* \brief Gets every entities in the world
	* \return A constant reference to the entities
	*/

	inline const EntityList& World::GetEntities() const
	{
		return m_aliveEntities;
	}

	/*!
	* \brief Gets a system in the world by index
	* \return A reference to the system
	*
	* \param index Index of the system
	*
	* \remark Produces a NazaraAssert if system is not available in this world
	*/

	inline BaseSystem& World::GetSystem(SystemIndex index)
	{
		NazaraAssert(HasSystem(index), "This system is not part of the world");

		BaseSystem* system = m_systems[index].get();
		NazaraAssert(system, "Invalid system pointer");

		return *system;
	}

	/*!
	* \brief Gets a system in the world by type
	* \return A reference to the system
	*
	* \remark Produces a NazaraAssert if system is not available in this world
	*/

	template<typename SystemType>
	SystemType& World::GetSystem()
	{
		static_assert(std::is_base_of<BaseSystem, SystemType>::value, "SystemType is not a system");

		SystemIndex index = GetSystemIndex<SystemType>();
		return static_cast<SystemType&>(GetSystem(index));
	}

	/*!
	* \brief Checks whether or not a system is present in the world by index
	* \return true If it is the case
	*
	* \param index Index of the system
	*/

	inline bool World::HasSystem(SystemIndex index) const
	{
		return index < m_systems.size() && m_systems[index];
	}

	/*!
	* \brief Checks whether or not a system is present in the world by type
	* \return true If it is the case
	*/

	template<typename SystemType>
	bool World::HasSystem() const
	{
		static_assert(std::is_base_of<BaseSystem, SystemType>::value, "SystemType is not a component");

		SystemIndex index = GetSystemIndex<SystemType>();
		return HasSystem(index);
	}

	/*!
	* \brief Marks an entity for deletion
	*
	* \param Pointer to the entity
	*
	* \remark If the entity pointer is invalid, nothing is done
	* \remark For safety, entities are not killed until the next world update
	*/
	inline void World::KillEntity(Entity* entity)
	{
		if (IsEntityValid(entity))
			m_killedEntities.UnboundedSet(entity->GetId(), true);
	}

	/*!
	* \brief Kills a set of entities
	*
	* This function has the same effect as calling KillEntity for every entity contained in the vector
	*
	* \param list Set of entities to kill
	*/
	inline void World::KillEntities(const EntityVector& list)
	{
		for (const EntityHandle& entity : list)
			KillEntity(entity);
	}

	/*!
	* \brief Gets an entity
	* \return A constant reference to a handle of the entity
	*
	* \param id Identifier of the entity
	*
	* \remark Handle referenced by this function can move in memory when updating the world, do not keep a reference to a handle from a world update to another
	* \remark If an invalid identifier is provided, an error got triggered and an invalid handle is returned
	*/
	inline const EntityHandle& World::GetEntity(EntityId id)
	{
		if (IsEntityIdValid(id))
			return m_entityBlocks[id]->handle;
		else
		{
			NazaraError("Invalid ID");
			return EntityHandle::InvalidHandle;
		}
	}

	/*!
	* \brief Checks whether or not an entity is valid
	* \return true If it is the case
	*
	* \param entity Pointer to the entity
	*/

	inline bool World::IsEntityValid(const Entity* entity) const
	{
		return entity && entity->GetWorld() == this && IsEntityIdValid(entity->GetId());
	}

	/*!
	* \brief Checks whether or not an entity is valid
	* \return true If it is the case
	*
	* \param id Identifier of the entity
	*/

	inline bool World::IsEntityIdValid(EntityId id) const
	{
		return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid();
	}

	/*!
	* \brief Removes each system from the world
	*/

	inline void World::RemoveAllSystems()
	{
		m_systems.clear();

		InvalidateSystemOrder();
	}

	/*!
	* \brief Removes a system from the world by index
	*
	* \param index Index of the system
	*
	* \remark No change is done if system is not present
	*/

	inline void World::RemoveSystem(SystemIndex index)
	{
		if (HasSystem(index))
		{
			m_systems[index].reset();

			InvalidateSystemOrder();
		}
	}

	/*!
	* \brief Removes a system from the world by type
	*/

	template<typename SystemType>
	void World::RemoveSystem()
	{
		static_assert(std::is_base_of<BaseSystem, SystemType>(), "SystemType is not a system");

		SystemIndex index = GetSystemIndex<SystemType>();
		RemoveSystem(index);
	}

	/*!
	* \brief Updates the world
	*
	* \param elapsedTime Delta time used for the update
	*/

	inline void World::Update(float elapsedTime)
	{
		Update(); //< Update entities

		// And then update systems
		for (auto& systemPtr : m_orderedSystems)
			systemPtr->Update(elapsedTime);
	}

	/*!
	* \brief Moves a world into another world object
	* \return A reference to the object
	*/

	inline World& World::operator=(World&& world) noexcept
	{
		m_aliveEntities         = std::move(world.m_aliveEntities);
		m_dirtyEntities         = std::move(world.m_dirtyEntities);
		m_entityBlocks          = std::move(world.m_entityBlocks);
		m_freeIdList            = std::move(world.m_freeIdList);
		m_killedEntities        = std::move(world.m_killedEntities);
		m_orderedSystems        = std::move(world.m_orderedSystems);
		m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
		m_waitingEntities       = std::move(world.m_waitingEntities);

		m_entities = std::move(world.m_entities);
		for (EntityBlock& block : m_entities)
			block.entity.SetWorld(this);

		m_systems = std::move(world.m_systems);
		for (const auto& systemPtr : m_systems)
			if (systemPtr)
				systemPtr->SetWorld(this);

		return *this;
	}

	inline void World::Invalidate()
	{
		m_dirtyEntities.Resize(m_entityBlocks.size(), false);
		m_dirtyEntities.Set(true); // Activation of all bits
	}

	inline void World::Invalidate(EntityId id)
	{
		m_dirtyEntities.UnboundedSet(id, true);
	}

	inline void World::InvalidateSystemOrder()
	{
		m_orderedSystemsUpdated = false;
	}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2019 Carsten Burstedde, Donna Calhoun, Scott Aiton, Grady Wright
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "fc2d_multigrid_solve.h"

#include "fc2d_multigrid.h"
#include "fc2d_multigrid_options.h"
#include "fc2d_multigrid_vector.hpp"

#include <fclaw2d_elliptic_solver.h>

#include <fclaw2d_clawpatch.h>
#include <fclaw2d_clawpatch_options.h>
#include <fclaw2d_clawpatch_output_ascii.h>
#include <fclaw2d_clawpatch_output_vtk.h>

#include <fclaw2d_global.h>
#include <fclaw2d_map.h>
#include <fclaw2d_map_brick.h>
#include <fclaw2d_options.h>
#include <fclaw2d_patch.h>
#include <fclaw2d_vtable.h>

#include <p4est_bits.h>
#include <p4est_wrap.h>

#include <Thunderegg/BiCGStab.h>
#include <Thunderegg/BilinearInterpolator.h>
#include <Thunderegg/BiCGStabSolver.h>
#include <Thunderegg/VarPoisson/StarPatchOperator.h>
#include <Thunderegg/GMG/CycleFactory2d.h>
#include <Thunderegg/SchurDomainOp.h>
#include <Thunderegg/P4estDomGen.h>

using namespace std;
using namespace Thunderegg;
using namespace Thunderegg::VarPoisson;

void fc2d_multigrid_solve(fclaw2d_global_t *glob) {
  // get needed options
  fclaw2d_clawpatch_options_t *clawpatch_opt =
      fclaw2d_clawpatch_get_options(glob);
  fclaw_options_t *fclaw_opt = fclaw2d_get_options(glob);
  fc2d_multigrid_options_t *mg_opt = fc2d_multigrid_get_options(glob);
  fc2d_multigrid_vtable_t *mg_vt = fc2d_multigrid_vt();

  // this should be moved somewhere else
  PetscInitialize(nullptr, nullptr, nullptr, nullptr);

  // create thunderegg vector for eqn 0
  shared_ptr<Vector<2>> f(new fc2d_multigrid_vector(glob, 0));

  // get patch size
  array<int, 2> ns = {clawpatch_opt->mx, clawpatch_opt->my};

  // get p4est structure
  fclaw2d_domain_t *domain = glob->domain;
  p4est_wrap_t *wrap = (p4est_wrap_t *)domain->pp;

  // create map function
  P4estDomGen::BlockMapFunc bmf = [&](int block_no, double unit_x,
                                      double unit_y, double &x, double &y) {
    double x1,y1,z1;
    FCLAW2D_MAP_BRICK2C(&glob->cont,&block_no,&unit_x, &unit_y, &x1, &y1, &z1);
    x = fclaw_opt->ax + (fclaw_opt->bx - fclaw_opt->ax) * x1;
    y = fclaw_opt->ay + (fclaw_opt->by - fclaw_opt->ay) * y1;
  };

  // create neumann function
  IsNeumannFunc<2> inf = [&](Side<2> s, const array<double, 2> &lower,
                             const array<double, 2> &upper) {
    return mg_opt->boundary_conditions[s.toInt()] == 2;
  };

  // generates levels of patches for GMG
  shared_ptr<P4estDomGen> domain_gen(
      new P4estDomGen(wrap->p4est, ns, clawpatch_opt->mbc,inf, bmf));

  // get finest level
  shared_ptr<Domain<2>> te_domain = domain_gen->getFinestDomain();

  // create SchurHelper
  shared_ptr<SchurHelper<2>> sh(new SchurHelper<2>(te_domain));

  // define operators for problems
  // interface interpolator
  shared_ptr<IfaceInterp<2>> interp(new BilinearInterpolator(sh));

  // patch operator

  // get beta function
  auto beta_func = [&](const std::array<double,2>& coord){
      double beta;
      double grad[2];
      mg_vt->fort_beta(&coord[0],&coord[1],&beta,grad);
      return beta;
  };

  // create vector for beta
  auto beta_vec = PetscVector<2>::GetNewVector(te_domain);
  DomainTools<2>::setValuesWithGhost(te_domain, beta_vec, beta_func);

  shared_ptr<PatchOperator<2>> op(new StarPatchOperator<2>(beta_vec, beta_func, sh, interp));

  // set the patch solver
  shared_ptr<PatchSolver<2>> solver(new BiCGStabSolver<2>(sh, op,
                                                          mg_opt->patch_bcgs_tol,
                                                          mg_opt->patch_bcgs_max_it));

  // create matrix
  shared_ptr<Operator<2>> A(new SchurDomainOp<2>(sh, interp, op));

  // create gmg preconditioner
  shared_ptr<Operator<2>> M;
  if(mg_opt->mg_prec){
    GMG::CycleOpts copts;
    copts.max_levels = mg_opt->max_levels;
    copts.patches_per_proc = mg_opt->patches_per_proc;
    copts.pre_sweeps = mg_opt->pre_sweeps;
    copts.post_sweeps = mg_opt->post_sweeps;
    copts.mid_sweeps = mg_opt->mid_sweeps;
    copts.coarse_sweeps = mg_opt->coarse_sweeps;
    copts.cycle_type = mg_opt->cycle_type;
    M = GMG::CycleFactory2d::getCycle(copts, domain_gen, solver, op, interp);
  }

  // solve
  shared_ptr<VectorGenerator<2>> vg(new DomainVG<2>(te_domain));
  shared_ptr<Vector<2>> u = vg->getNewVector();

  int its = BiCGStab<2>::solve(vg, A, u, f, nullptr, mg_opt->max_it, mg_opt->tol);

  fclaw_global_productionf("Iterations: %i\n", its);
  fclaw_global_productionf("f-2norm: %f\n", f->twoNorm());
  fclaw_global_productionf("f-infnorm: %f\n", f->infNorm());
  fclaw_global_productionf("u-2norm: %f\n", u->twoNorm());
  fclaw_global_productionf("u-infnorm: %f\n\n", u->infNorm());

  // copy solution into rhs
  f->copy(u);
  fclaw_global_productionf("Checking if copy function works:\n");
  fclaw_global_productionf("fcopy-2norm: %f\n", f->twoNorm());
  fclaw_global_productionf("fcopy-infnorm: %f\n\n", f->infNorm());
}

<commit_msg>(multigrid) works the Thunderegg's new Schur namespace<commit_after>/*
Copyright (c) 2019 Carsten Burstedde, Donna Calhoun, Scott Aiton, Grady Wright
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "fc2d_multigrid_solve.h"

#include "fc2d_multigrid.h"
#include "fc2d_multigrid_options.h"
#include "fc2d_multigrid_vector.hpp"

#include <fclaw2d_elliptic_solver.h>

#include <fclaw2d_clawpatch.h>
#include <fclaw2d_clawpatch_options.h>
#include <fclaw2d_clawpatch_output_ascii.h>
#include <fclaw2d_clawpatch_output_vtk.h>

#include <fclaw2d_global.h>
#include <fclaw2d_map.h>
#include <fclaw2d_map_brick.h>
#include <fclaw2d_options.h>
#include <fclaw2d_patch.h>
#include <fclaw2d_vtable.h>

#include <p4est_bits.h>
#include <p4est_wrap.h>

#include <Thunderegg/BiCGStab.h>
#include <Thunderegg/Schur/BilinearInterpolator.h>
#include <Thunderegg/Schur/BiCGStabSolver.h>
#include <Thunderegg/VarPoisson/StarPatchOperator.h>
#include <Thunderegg/GMG/CycleFactory2d.h>
#include <Thunderegg/SchurDomainOp.h>
#include <Thunderegg/P4estDomGen.h>

using namespace std;
using namespace Thunderegg;
using namespace Thunderegg::Schur;
using namespace Thunderegg::VarPoisson;

void fc2d_multigrid_solve(fclaw2d_global_t *glob) {
  // get needed options
  fclaw2d_clawpatch_options_t *clawpatch_opt =
      fclaw2d_clawpatch_get_options(glob);
  fclaw_options_t *fclaw_opt = fclaw2d_get_options(glob);
  fc2d_multigrid_options_t *mg_opt = fc2d_multigrid_get_options(glob);
  fc2d_multigrid_vtable_t *mg_vt = fc2d_multigrid_vt();

  // this should be moved somewhere else
  PetscInitialize(nullptr, nullptr, nullptr, nullptr);

  // create thunderegg vector for eqn 0
  shared_ptr<Vector<2>> f(new fc2d_multigrid_vector(glob, 0));

  // get patch size
  array<int, 2> ns = {clawpatch_opt->mx, clawpatch_opt->my};

  // get p4est structure
  fclaw2d_domain_t *domain = glob->domain;
  p4est_wrap_t *wrap = (p4est_wrap_t *)domain->pp;

  // create map function
  P4estDomGen::BlockMapFunc bmf = [&](int block_no, double unit_x,
                                      double unit_y, double &x, double &y) {
    double x1,y1,z1;
    FCLAW2D_MAP_BRICK2C(&glob->cont,&block_no,&unit_x, &unit_y, &x1, &y1, &z1);
    x = fclaw_opt->ax + (fclaw_opt->bx - fclaw_opt->ax) * x1;
    y = fclaw_opt->ay + (fclaw_opt->by - fclaw_opt->ay) * y1;
  };

  // create neumann function
  IsNeumannFunc<2> inf = [&](Side<2> s, const array<double, 2> &lower,
                             const array<double, 2> &upper) {
    return mg_opt->boundary_conditions[s.toInt()] == 2;
  };

  // generates levels of patches for GMG
  shared_ptr<P4estDomGen> domain_gen(
      new P4estDomGen(wrap->p4est, ns, clawpatch_opt->mbc,inf, bmf));

  // get finest level
  shared_ptr<Domain<2>> te_domain = domain_gen->getFinestDomain();

  // create SchurHelper
  shared_ptr<SchurHelper<2>> sh(new SchurHelper<2>(te_domain));

  // define operators for problems
  // interface interpolator
  shared_ptr<IfaceInterp<2>> interp(new BilinearInterpolator(sh));

  // patch operator

  // get beta function
  auto beta_func = [&](const std::array<double,2>& coord){
      double beta;
      double grad[2];
      mg_vt->fort_beta(&coord[0],&coord[1],&beta,grad);
      return beta;
  };

  // create vector for beta
  auto beta_vec = PetscVector<2>::GetNewVector(te_domain);
  DomainTools<2>::setValuesWithGhost(te_domain, beta_vec, beta_func);

  shared_ptr<PatchOperator<2>> op(new StarPatchOperator<2>(beta_vec, beta_func, sh, interp));

  // set the patch solver
  shared_ptr<PatchSolver<2>> solver(new BiCGStabSolver<2>(sh, op,
                                                          mg_opt->patch_bcgs_tol,
                                                          mg_opt->patch_bcgs_max_it));

  // create matrix
  shared_ptr<Operator<2>> A(new SchurDomainOp<2>(sh, interp, op));

  // create gmg preconditioner
  shared_ptr<Operator<2>> M;
  if(mg_opt->mg_prec){
    GMG::CycleOpts copts;
    copts.max_levels = mg_opt->max_levels;
    copts.patches_per_proc = mg_opt->patches_per_proc;
    copts.pre_sweeps = mg_opt->pre_sweeps;
    copts.post_sweeps = mg_opt->post_sweeps;
    copts.mid_sweeps = mg_opt->mid_sweeps;
    copts.coarse_sweeps = mg_opt->coarse_sweeps;
    copts.cycle_type = mg_opt->cycle_type;
    M = GMG::CycleFactory2d::getCycle(copts, domain_gen, solver, op, interp);
  }

  // solve
  shared_ptr<VectorGenerator<2>> vg(new DomainVG<2>(te_domain));
  shared_ptr<Vector<2>> u = vg->getNewVector();

  int its = BiCGStab<2>::solve(vg, A, u, f, nullptr, mg_opt->max_it, mg_opt->tol);

  fclaw_global_productionf("Iterations: %i\n", its);
  fclaw_global_productionf("f-2norm: %f\n", f->twoNorm());
  fclaw_global_productionf("f-infnorm: %f\n", f->infNorm());
  fclaw_global_productionf("u-2norm: %f\n", u->twoNorm());
  fclaw_global_productionf("u-infnorm: %f\n\n", u->infNorm());

  // copy solution into rhs
  f->copy(u);
  fclaw_global_productionf("Checking if copy function works:\n");
  fclaw_global_productionf("fcopy-2norm: %f\n", f->twoNorm());
  fclaw_global_productionf("fcopy-infnorm: %f\n\n", f->infNorm());
}

<|endoftext|>


<commit_before>// SoSmileyCtrl.cpp : Implementation of CSoSmileyCtrl
#include "stdafx.h"
#include "SoSmileyCtrl.h"
#include <atltypes.h>
#include <TOM.h>
#include <richedit.h>
#include <richole.h>

DWORD _GenerateID()
{
    static DWORD s_id=1;
    return s_id++;
}

// CCSmiley

CSoSmileyCtrl::CSoSmileyCtrl():m_iFrameIndex(0),m_dwDrawFlag(0)
{
    m_dwID = _GenerateID();
    memset(&m_rcPos,0,sizeof(RECT));
}

CSoSmileyCtrl::~CSoSmileyCtrl()
{
    if(m_pSmileyHost)
    {//֤ʱԼ
        m_pSmileyHost->KillTimer(this);
    }
}

STDMETHODIMP CSoSmileyCtrl::Insert2Richedit(DWORD_PTR pole)
{
    if(!m_pSmileySource) return E_FAIL;

    IRichEditOle *ole = (IRichEditOle*)pole;

    HRESULT hr;

    //insert this to host
    CComPtr<IOleObject>	pOleObject;
    CComPtr<IOleClientSite> pClientSite;

    // Get site
    ole->GetClientSite(&pClientSite);
    ATLASSERT(pClientSite != NULL);

    CComPtr<IRichEditOleCallback> pCallback;
    hr=ole->QueryInterface(IID_IRichEditOleCallback,(void**)&pCallback);
    if(!SUCCEEDED(hr)) return E_FAIL;

    //get the IOleObject
    hr = QueryInterface(IID_IOleObject, (void**)&pOleObject);
    if (FAILED(hr))
    {
        return	 E_FAIL;
    }

    //to insert into richedit, you need a struct of REOBJECT
    REOBJECT reobject;
    ZeroMemory(&reobject, sizeof(REOBJECT));

    reobject.cbStruct = sizeof(REOBJECT);

    CLSID clsid;
    pOleObject->GetUserClassID(&clsid);

    //get storage
    pCallback->GetNewStorage(&reobject.pstg);

    //set clsid
    reobject.clsid = clsid;
    //can be selected
    reobject.cp = REO_CP_SELECTION;
    //content, but not static
    reobject.dvaspect = DVASPECT_CONTENT;
    //goes in the same line of text line
    reobject.dwFlags = REO_BELOWBASELINE;
    reobject.dwUser = (DWORD)m_dwID;
    //the very object
    reobject.poleobj = pOleObject;
    //client site contain the object
    reobject.polesite = pClientSite;

    SIZEL sizel={0};
    reobject.sizel = sizel;

    hr = pOleObject->SetClientSite(pClientSite);

    if(SUCCEEDED(hr))
    {
        hr = ole->InsertObject(&reobject);

        FireViewChange();
    }

    if(reobject.pstg)
    {
        reobject.pstg->Release();
    }

    return hr;
}

STDMETHODIMP CSoSmileyCtrl::SetSource(ISmileySource * pSource)
{
    m_pSmileySource = pSource;
    if(m_pSmileySource)
    {
        //±С
        SIZE sz;
        m_pSmileySource->GetSize(&sz);

        HDC hDC = ::GetDC(NULL);
        m_sizeExtent.cx = ::MulDiv(sz.cx, 2540, GetDeviceCaps(hDC, LOGPIXELSX));
        m_sizeExtent.cy = ::MulDiv(sz.cy, 2540, GetDeviceCaps(hDC, LOGPIXELSY));
        ::ReleaseDC(NULL, hDC);
    }

    return S_OK;
}

STDMETHODIMP CSoSmileyCtrl::GetSource(ISmileySource ** ppSource)
{
    if(! m_pSmileySource) return E_FAIL;
    *ppSource = m_pSmileySource;
    (*ppSource)->AddRef();
    return S_OK;
}

STDMETHODIMP CSoSmileyCtrl::OnTimer(HDC hdc)
{
    int cFrame = 1;
    m_pSmileySource->GetFrameCount(&cFrame);
    m_iFrameIndex++;
    m_iFrameIndex %= cFrame;
    UpdateSmiley(hdc);
    return S_OK;
}

HRESULT CSoSmileyCtrl::IPersistStreamInit_Load(LPSTREAM pStm, const ATL_PROPMAP_ENTRY* pMap)
{
    ATLASSERT(!m_pSmileySource);
    ATLASSERT(m_pSmileyHost);
    CComPtr<ISmileySource> pSource;
    HRESULT hr =m_pSmileyHost->CreateSource(&pSource);
    if(!SUCCEEDED(hr)) return hr;
    hr = pSource->Stream_Load(pStm);
    if(!SUCCEEDED(hr)) return hr;
    SetSource(pSource);

    hr = IPersistStreamInitImpl<CSoSmileyCtrl>::IPersistStreamInit_Load( pStm, pMap);

    if(!SUCCEEDED(hr)) return hr;
    FireViewChange();
    return hr;
}

HRESULT CSoSmileyCtrl::IPersistStreamInit_Save(LPSTREAM pStm, BOOL fClearDirty, const ATL_PROPMAP_ENTRY* pMap)
{
    if(!m_pSmileySource) return E_FAIL;
    HRESULT hr = m_pSmileySource->Stream_Save(pStm);
    if(!SUCCEEDED(hr)) return hr;
    return IPersistStreamInitImpl<CSoSmileyCtrl>::IPersistStreamInit_Save( pStm, fClearDirty, pMap);
}

HRESULT CSoSmileyCtrl::FireViewChange()
{
    if (m_bInPlaceActive)
    {
        // Active
        if (m_hWndCD != NULL)
            ::InvalidateRect(m_hWndCD, NULL, TRUE); // Window based
        else if (m_bWndLess && m_spInPlaceSite != NULL)
            m_spInPlaceSite->InvalidateRect(NULL, TRUE); // Windowless
    }
    else if (!m_pSmileyHost)
    {
        SendOnViewChange(DVASPECT_CONTENT);
    }
    else
    {
//         UpdateSmiley(NULL);
    }
    return S_OK;
}

HRESULT CSoSmileyCtrl::OnDraw(ATL_DRAWINFO& di)
{
    if(!m_pSmileySource) return E_FAIL;
    RECT rc={di.prcBounds->left,di.prcBounds->top,di.prcBounds->right,di.prcBounds->bottom};
    m_pSmileySource->Draw(di.hdcDraw,&rc,m_iFrameIndex);
    if(!di.bZoomed)
    {//copyʱbZoomed־ѾηŴ
        m_rcPos = rc;//cache pos, position will be updated in response to EN_UPDATE message come from it's host
    }

    int nDelay=0;
    HRESULT hr = m_pSmileySource->GetFrameDelay(m_iFrameIndex,&nDelay);
    if(SUCCEEDED(hr) && m_pSmileyHost)
    {
        m_pSmileyHost->SetTimer(this,nDelay);
    }
    return S_OK;
}

void CSoSmileyCtrl::UpdateSmiley(HDC hdc)
{
    if(!m_pSmileyHost) return ;
    if(::IsRectEmpty(&m_rcPos)) return;

    if(!hdc) return;

    UpdateSmileyFlag();

    m_pSmileySource->Draw(hdc,&m_rcPos,m_iFrameIndex);

    if (m_dwDrawFlag == REO_INVERTEDSELECT)
    {
        //Invert entire object
        InvertRect(hdc, &m_rcPos);
    }
    else if(m_dwDrawFlag == REO_SELECTED)
    {
        // Just the border, so use a null brush
        SaveDC(hdc);
        SetROP2(hdc, R2_NOT);
        SelectObject(hdc, GetStockObject(NULL_BRUSH));
        Rectangle(hdc, m_rcPos.left, m_rcPos.top, m_rcPos.right, m_rcPos.bottom);
        RestoreDC(hdc, -1);
    }

    int nDelay=0;
    HRESULT hr = m_pSmileySource->GetFrameDelay(m_iFrameIndex,&nDelay);
    if(SUCCEEDED(hr) && m_pSmileyHost)
    {
        m_pSmileyHost->SetTimer(this,nDelay);
    }
}

HRESULT CSoSmileyCtrl::Load( IStorage* pStorage )
{
    //RichEditFlagsֶд3REOBJECT־Richedit20winceԴֶʼɲricheditǰ"RichEditFlags"ж3־λ
    CComPtr<IStream> spStream;
    static LPCOLESTR vszRichEditFlags = OLESTR("RichEditFlags");
    HRESULT hr = pStorage->CreateStream(vszRichEditFlags,
        STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
        0, 0, &spStream);
    if (FAILED(hr)) return hr;
    DWORD dwFlag=REO_BELOWBASELINE,dwUser=m_dwID,dwAspect=DVASPECT_CONTENT;
    spStream->Write(&dwFlag,sizeof(dwFlag),NULL);
    spStream->Write(&dwUser,sizeof(dwUser),NULL);
    spStream->Write(&dwAspect,sizeof(dwAspect),NULL);

    return IPersistStorageImpl<CSoSmileyCtrl>::Load(pStorage);
}

STDMETHODIMP CSoSmileyCtrl::SetClientSite(IOleClientSite *pClientSite)
{
    if(pClientSite)
    {
        CComPtr<IRichEditOleCallback> pCallback;
        HRESULT hr=pClientSite->QueryInterface(IID_IRichEditOleCallback,(void**)&pCallback);
        if(FAILED(hr)) return E_FAIL;

        //callbackлȡhost
        hr = pCallback->QueryInterface(__uuidof(ISmileyHost),(LPVOID*)&m_pSmileyHost);
        if(FAILED(hr)) return E_FAIL;
    }

    return __super::SetClientSite(pClientSite);
}

LONG GetOleCP(IRichEditOle *pOle, int iOle)
{
    REOBJECT reobj={0};
    reobj.cbStruct=sizeof(REOBJECT);
    pOle->GetObject(iOle,&reobj,REO_GETOBJ_NO_INTERFACES);
    return reobj.cp;
}

//find first Ole Object in char range of [cpMin,cpMax)
int FindFirstOleInrange(IRichEditOle *pOle, int iBegin,int iEnd,int cpMin,int cpMax)
{
    if(iBegin==iEnd) return -1;

    int iMid = (iBegin + iEnd)/2;

    LONG cp = GetOleCP(pOle,iMid);

    if(cp < cpMin)
    {
        return FindFirstOleInrange(pOle,iMid+1,iEnd,cpMin,cpMax);
    }else if(cp >= cpMax)
    {
        return FindFirstOleInrange(pOle,iBegin,iMid,cpMin,cpMax);
    }else
    {
        int iRet = iMid;
        while(iRet>iBegin)
        {
            cp = GetOleCP(pOle,iRet-1);
            if(cp<cpMin) break;
            iRet --;
        }
        return iRet;
    }
}

//find Last Ole Object in char range of [cpMin,cpMax)
int FindLastOleInrange(IRichEditOle *pOle, int iBegin,int iEnd,int cpMin,int cpMax)
{
    if(iBegin==iEnd) return -1;

    int iMid = (iBegin + iEnd)/2;

    LONG cp = GetOleCP(pOle,iMid);

    if(cp < cpMin)
    {
        return FindLastOleInrange(pOle,iMid+1,iEnd,cpMin,cpMax);
    }else if(cp >= cpMax)
    {
        return FindLastOleInrange(pOle,iBegin,iMid,cpMin,cpMax);
    }else
    {
        int iRet = iMid;
        while(iRet<(iEnd-1))
        {
            cp = GetOleCP(pOle,iRet+1);
            if(cp>=cpMax) break;
            iRet ++;
        }
        return iRet;
    }
}

//ñĻ״̬
DWORD CSoSmileyCtrl::GetSmileyFlag(IRichEditOle *ole,int iFirst,int iLast)
{
    ATLASSERT(m_pSmileyHost);

    DWORD dwFlag =0;
    for(int i=iFirst;i<=iLast;i++)
    {
        REOBJECT reobj={0};
        reobj.cbStruct=sizeof(REOBJECT);
        HRESULT hr=ole->GetObject(i,&reobj,REO_GETOBJ_NO_INTERFACES);
        if(FAILED(hr)) break;

        if (reobj.clsid==__uuidof(CSoSmileyCtrl) && reobj.dwUser==m_dwID)
        {
            //ѡеʱƱ߿ѡжʱɫ
            CHARRANGE chr={0};
            m_pSmileyHost->SendMessage(EM_EXGETSEL, 0, (LPARAM)&chr,NULL);

            if  ( chr.cpMin!=chr.cpMax
                && (reobj.cp<chr.cpMax && reobj.cp>=chr.cpMin) )
            {
                if((chr.cpMax-chr.cpMin) > 1)
                    dwFlag=REO_INVERTEDSELECT;
                else
                    dwFlag=REO_SELECTED;
            }

            break;
        }
    }
    return dwFlag;
}

void CSoSmileyCtrl::UpdateSmileyFlag()
{
    if(!m_pSmileyHost) return ;

    if(m_dwDrawFlag!=-1) return;

    CComPtr<IRichEditOle>  ole;
    LRESULT lMsgRet = 0;
    m_pSmileyHost->SendMessage(EM_GETOLEINTERFACE, 0, (LPARAM)&ole,&lMsgRet);
    if (!lMsgRet) return;

    //ÿɼַΧ
    m_pSmileyHost->SendMessage(EM_GETFIRSTVISIBLELINE,0,0,&lMsgRet);

    int iFirstLine = (int)lMsgRet;
    RECT rcView;
    m_pSmileyHost->SendMessage(EM_GETRECT,0,(LPARAM)&rcView,&lMsgRet);
    POINT pt={rcView.right+1,rcView.bottom-2};

    m_pSmileyHost->SendMessage(EM_LINEINDEX,iFirstLine,0,&lMsgRet);
    LONG cpFirst = (LONG)lMsgRet;
    m_pSmileyHost->SendMessage(EM_CHARFROMPOS,0,(LPARAM)&pt,&lMsgRet);
    LONG cpLast  = (LONG)lMsgRet;

    //ַڿɼΧеOLE
    int nCount=ole->GetObjectCount();

    int iFirstVisibleOle = FindFirstOleInrange(ole,0,nCount,cpFirst,cpLast);
    if(iFirstVisibleOle==-1) return;
    int iLastVisibleOle = FindLastOleInrange(ole,iFirstVisibleOle,nCount,cpFirst,cpLast);

    m_dwDrawFlag = GetSmileyFlag(ole,iFirstVisibleOle,iLastVisibleOle);
}

STDMETHODIMP CSoSmileyCtrl::Clear()
{
    memset(&m_rcPos,0,sizeof(RECT));
    m_dwDrawFlag = -1;
    return S_OK;
}
<commit_msg><commit_after>// SoSmileyCtrl.cpp : Implementation of CSoSmileyCtrl
#include "stdafx.h"
#include "SoSmileyCtrl.h"
#include <atltypes.h>
#include <TOM.h>
#include <richedit.h>
#include <richole.h>

DWORD _GenerateID()
{
    static DWORD s_id=1;
    return s_id++;
}

// CCSmiley

CSoSmileyCtrl::CSoSmileyCtrl():m_iFrameIndex(0),m_dwDrawFlag(0)
{
    m_dwID = _GenerateID();
    memset(&m_rcPos,0,sizeof(RECT));
	m_bRecomposeOnResize=1;
}

CSoSmileyCtrl::~CSoSmileyCtrl()
{
    if(m_pSmileyHost)
    {//֤ʱԼ
        m_pSmileyHost->KillTimer(this);
    }
}

STDMETHODIMP CSoSmileyCtrl::Insert2Richedit(DWORD_PTR pole)
{
    if(!m_pSmileySource) return E_FAIL;

    IRichEditOle *ole = (IRichEditOle*)pole;

    HRESULT hr;

    //insert this to host
    CComPtr<IOleObject>	pOleObject;
    CComPtr<IOleClientSite> pClientSite;

    // Get site
    ole->GetClientSite(&pClientSite);
    ATLASSERT(pClientSite != NULL);

    CComPtr<IRichEditOleCallback> pCallback;
    hr=ole->QueryInterface(IID_IRichEditOleCallback,(void**)&pCallback);
    if(!SUCCEEDED(hr)) return E_FAIL;

    //get the IOleObject
    hr = QueryInterface(IID_IOleObject, (void**)&pOleObject);
    if (FAILED(hr))
    {
        return	 E_FAIL;
    }

    //to insert into richedit, you need a struct of REOBJECT
    REOBJECT reobject;
    ZeroMemory(&reobject, sizeof(REOBJECT));

    reobject.cbStruct = sizeof(REOBJECT);

    CLSID clsid;
    pOleObject->GetUserClassID(&clsid);

    //get storage
    pCallback->GetNewStorage(&reobject.pstg);

    //set clsid
    reobject.clsid = clsid;
    //can be selected
    reobject.cp = REO_CP_SELECTION;
    //content, but not static
    reobject.dvaspect = DVASPECT_CONTENT;
    //goes in the same line of text line
    reobject.dwFlags = REO_BELOWBASELINE;
    reobject.dwUser = (DWORD)m_dwID;
    //the very object
    reobject.poleobj = pOleObject;
    //client site contain the object
    reobject.polesite = pClientSite;

    SIZEL sizel={0};
    reobject.sizel = sizel;

    hr = pOleObject->SetClientSite(pClientSite);

    if(SUCCEEDED(hr))
    {
        hr = ole->InsertObject(&reobject);

        FireViewChange();
    }

    if(reobject.pstg)
    {
        reobject.pstg->Release();
    }

    return hr;
}

STDMETHODIMP CSoSmileyCtrl::SetSource(ISmileySource * pSource)
{
    m_pSmileySource = pSource;
    if(m_pSmileySource)
    {
        //±С
        SIZE sz;
        m_pSmileySource->GetSize(&sz);

        HDC hDC = ::GetDC(NULL);
        m_sizeExtent.cx = ::MulDiv(sz.cx, 2540, GetDeviceCaps(hDC, LOGPIXELSX));
        m_sizeExtent.cy = ::MulDiv(sz.cy, 2540, GetDeviceCaps(hDC, LOGPIXELSY));
        ::ReleaseDC(NULL, hDC);
    }

    return S_OK;
}

STDMETHODIMP CSoSmileyCtrl::GetSource(ISmileySource ** ppSource)
{
    if(! m_pSmileySource) return E_FAIL;
    *ppSource = m_pSmileySource;
    (*ppSource)->AddRef();
    return S_OK;
}

STDMETHODIMP CSoSmileyCtrl::OnTimer(HDC hdc)
{
    int cFrame = 1;
    m_pSmileySource->GetFrameCount(&cFrame);
    m_iFrameIndex++;
    m_iFrameIndex %= cFrame;
    UpdateSmiley(hdc);
    return S_OK;
}

HRESULT CSoSmileyCtrl::IPersistStreamInit_Load(LPSTREAM pStm, const ATL_PROPMAP_ENTRY* pMap)
{
    ATLASSERT(!m_pSmileySource);
    ATLASSERT(m_pSmileyHost);
    CComPtr<ISmileySource> pSource;
    HRESULT hr =m_pSmileyHost->CreateSource(&pSource);
    if(!SUCCEEDED(hr)) return hr;
    hr = pSource->Stream_Load(pStm);
    if(!SUCCEEDED(hr)) return hr;
    SetSource(pSource);

    hr = IPersistStreamInitImpl<CSoSmileyCtrl>::IPersistStreamInit_Load( pStm, pMap);

    if(!SUCCEEDED(hr)) return hr;
    FireViewChange();
    return hr;
}

HRESULT CSoSmileyCtrl::IPersistStreamInit_Save(LPSTREAM pStm, BOOL fClearDirty, const ATL_PROPMAP_ENTRY* pMap)
{
    if(!m_pSmileySource) return E_FAIL;
    HRESULT hr = m_pSmileySource->Stream_Save(pStm);
    if(!SUCCEEDED(hr)) return hr;
    return IPersistStreamInitImpl<CSoSmileyCtrl>::IPersistStreamInit_Save( pStm, fClearDirty, pMap);
}

HRESULT CSoSmileyCtrl::FireViewChange()
{
    if (m_bInPlaceActive)
    {
        // Active
        if (m_hWndCD != NULL)
            ::InvalidateRect(m_hWndCD, NULL, TRUE); // Window based
        else if (m_bWndLess && m_spInPlaceSite != NULL)
            m_spInPlaceSite->InvalidateRect(NULL, TRUE); // Windowless
    }
    else if (!m_pSmileyHost)
    {
        SendOnViewChange(DVASPECT_CONTENT);
    }
    else
    {
//         UpdateSmiley(NULL);
    }
    return S_OK;
}

HRESULT CSoSmileyCtrl::OnDraw(ATL_DRAWINFO& di)
{
    if(!m_pSmileySource) return E_FAIL;
    RECT rc={di.prcBounds->left,di.prcBounds->top,di.prcBounds->right,di.prcBounds->bottom};
    m_pSmileySource->Draw(di.hdcDraw,&rc,m_iFrameIndex);
    if(!di.bZoomed)
    {//copyʱbZoomed־ѾηŴ
        m_rcPos = rc;//cache pos, position will be updated in response to EN_UPDATE message come from it's host
    }

    int nDelay=0;
    HRESULT hr = m_pSmileySource->GetFrameDelay(m_iFrameIndex,&nDelay);
    if(SUCCEEDED(hr) && m_pSmileyHost)
    {
        m_pSmileyHost->SetTimer(this,nDelay);
    }
    return S_OK;
}

void CSoSmileyCtrl::UpdateSmiley(HDC hdc)
{
    if(!m_pSmileyHost) return ;
    if(::IsRectEmpty(&m_rcPos)) return;

    if(!hdc) return;

    UpdateSmileyFlag();

    m_pSmileySource->Draw(hdc,&m_rcPos,m_iFrameIndex);

    if (m_dwDrawFlag == REO_INVERTEDSELECT)
    {
        //Invert entire object
        InvertRect(hdc, &m_rcPos);
    }
    else if(m_dwDrawFlag == REO_SELECTED)
    {
        // Just the border, so use a null brush
        SaveDC(hdc);
        SetROP2(hdc, R2_NOT);
        SelectObject(hdc, GetStockObject(NULL_BRUSH));
        Rectangle(hdc, m_rcPos.left, m_rcPos.top, m_rcPos.right, m_rcPos.bottom);
        RestoreDC(hdc, -1);
    }

    int nDelay=0;
    HRESULT hr = m_pSmileySource->GetFrameDelay(m_iFrameIndex,&nDelay);
    if(SUCCEEDED(hr) && m_pSmileyHost)
    {
        m_pSmileyHost->SetTimer(this,nDelay);
    }
}

HRESULT CSoSmileyCtrl::Load( IStorage* pStorage )
{
    //RichEditFlagsֶд3REOBJECT־Richedit20winceԴֶʼɲricheditǰ"RichEditFlags"ж3־λ
    CComPtr<IStream> spStream;
    static LPCOLESTR vszRichEditFlags = OLESTR("RichEditFlags");
    HRESULT hr = pStorage->CreateStream(vszRichEditFlags,
        STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
        0, 0, &spStream);
    if (FAILED(hr)) return hr;
    DWORD dwFlag=REO_BELOWBASELINE,dwUser=m_dwID,dwAspect=DVASPECT_CONTENT;
    spStream->Write(&dwFlag,sizeof(dwFlag),NULL);
    spStream->Write(&dwUser,sizeof(dwUser),NULL);
    spStream->Write(&dwAspect,sizeof(dwAspect),NULL);

    return IPersistStorageImpl<CSoSmileyCtrl>::Load(pStorage);
}

STDMETHODIMP CSoSmileyCtrl::SetClientSite(IOleClientSite *pClientSite)
{
    if(pClientSite)
    {
        CComPtr<IRichEditOleCallback> pCallback;
        HRESULT hr=pClientSite->QueryInterface(IID_IRichEditOleCallback,(void**)&pCallback);
        if(FAILED(hr)) return E_FAIL;

        //callbackлȡhost
        hr = pCallback->QueryInterface(__uuidof(ISmileyHost),(LPVOID*)&m_pSmileyHost);
        if(FAILED(hr)) return E_FAIL;
    }

    return __super::SetClientSite(pClientSite);
}

LONG GetOleCP(IRichEditOle *pOle, int iOle)
{
    REOBJECT reobj={0};
    reobj.cbStruct=sizeof(REOBJECT);
    pOle->GetObject(iOle,&reobj,REO_GETOBJ_NO_INTERFACES);
    return reobj.cp;
}

//find first Ole Object in char range of [cpMin,cpMax)
int FindFirstOleInrange(IRichEditOle *pOle, int iBegin,int iEnd,int cpMin,int cpMax)
{
    if(iBegin==iEnd) return -1;

    int iMid = (iBegin + iEnd)/2;

    LONG cp = GetOleCP(pOle,iMid);

    if(cp < cpMin)
    {
        return FindFirstOleInrange(pOle,iMid+1,iEnd,cpMin,cpMax);
    }else if(cp >= cpMax)
    {
        return FindFirstOleInrange(pOle,iBegin,iMid,cpMin,cpMax);
    }else
    {
        int iRet = iMid;
        while(iRet>iBegin)
        {
            cp = GetOleCP(pOle,iRet-1);
            if(cp<cpMin) break;
            iRet --;
        }
        return iRet;
    }
}

//find Last Ole Object in char range of [cpMin,cpMax)
int FindLastOleInrange(IRichEditOle *pOle, int iBegin,int iEnd,int cpMin,int cpMax)
{
    if(iBegin==iEnd) return -1;

    int iMid = (iBegin + iEnd)/2;

    LONG cp = GetOleCP(pOle,iMid);

    if(cp < cpMin)
    {
        return FindLastOleInrange(pOle,iMid+1,iEnd,cpMin,cpMax);
    }else if(cp >= cpMax)
    {
        return FindLastOleInrange(pOle,iBegin,iMid,cpMin,cpMax);
    }else
    {
        int iRet = iMid;
        while(iRet<(iEnd-1))
        {
            cp = GetOleCP(pOle,iRet+1);
            if(cp>=cpMax) break;
            iRet ++;
        }
        return iRet;
    }
}

//ñĻ״̬
DWORD CSoSmileyCtrl::GetSmileyFlag(IRichEditOle *ole,int iFirst,int iLast)
{
    ATLASSERT(m_pSmileyHost);

    DWORD dwFlag =0;
    for(int i=iFirst;i<=iLast;i++)
    {
        REOBJECT reobj={0};
        reobj.cbStruct=sizeof(REOBJECT);
        HRESULT hr=ole->GetObject(i,&reobj,REO_GETOBJ_NO_INTERFACES);
        if(FAILED(hr)) break;

        if (reobj.clsid==__uuidof(CSoSmileyCtrl) && reobj.dwUser==m_dwID)
        {
            //ѡеʱƱ߿ѡжʱɫ
            CHARRANGE chr={0};
            m_pSmileyHost->SendMessage(EM_EXGETSEL, 0, (LPARAM)&chr,NULL);

            if  ( chr.cpMin!=chr.cpMax
                && (reobj.cp<chr.cpMax && reobj.cp>=chr.cpMin) )
            {
                if((chr.cpMax-chr.cpMin) > 1)
                    dwFlag=REO_INVERTEDSELECT;
                else
                    dwFlag=REO_SELECTED;
            }

            break;
        }
    }
    return dwFlag;
}

void CSoSmileyCtrl::UpdateSmileyFlag()
{
    if(!m_pSmileyHost) return ;

    if(m_dwDrawFlag!=-1) return;

    CComPtr<IRichEditOle>  ole;
    LRESULT lMsgRet = 0;
    m_pSmileyHost->SendMessage(EM_GETOLEINTERFACE, 0, (LPARAM)&ole,&lMsgRet);
    if (!lMsgRet) return;

    //ÿɼַΧ
    m_pSmileyHost->SendMessage(EM_GETFIRSTVISIBLELINE,0,0,&lMsgRet);

    int iFirstLine = (int)lMsgRet;
    RECT rcView;
    m_pSmileyHost->SendMessage(EM_GETRECT,0,(LPARAM)&rcView,&lMsgRet);
    POINT pt={rcView.right+1,rcView.bottom-2};

    m_pSmileyHost->SendMessage(EM_LINEINDEX,iFirstLine,0,&lMsgRet);
    LONG cpFirst = (LONG)lMsgRet;
    m_pSmileyHost->SendMessage(EM_CHARFROMPOS,0,(LPARAM)&pt,&lMsgRet);
    LONG cpLast  = (LONG)lMsgRet;

    //ַڿɼΧеOLE
    int nCount=ole->GetObjectCount();

    int iFirstVisibleOle = FindFirstOleInrange(ole,0,nCount,cpFirst,cpLast);
    if(iFirstVisibleOle==-1) return;
    int iLastVisibleOle = FindLastOleInrange(ole,iFirstVisibleOle,nCount,cpFirst,cpLast);

    m_dwDrawFlag = GetSmileyFlag(ole,iFirstVisibleOle,iLastVisibleOle);
}

STDMETHODIMP CSoSmileyCtrl::Clear()
{
    memset(&m_rcPos,0,sizeof(RECT));
    m_dwDrawFlag = -1;
    return S_OK;
}
<|endoftext|>


<commit_before>#include "GLTexture2D.h"
#include <OpenGL\gl_core_4_4.hpp>
#include <GLM\detail\func_common.hpp>

using glm::max;

// Computes the number of MIP-map levels given the texture dimensions
static inline GLsizei computeMipLvlCnt(const GLsizei width, const GLsizei height) {
    return static_cast<GLsizei>(floor(log2(max(width, height))) + 1);
}

template <GLenum SIF>
GLTexture2D<SIF>::GLTexture2D(const GLenum tex_unit, const GLsizei width, const GLsizei height,
                               const bool use_mipmaps, const bool use_lin_filter) {
    // Aquire a texture handle
    gl::ActiveTexture(gl::TEXTURE0 + tex_unit);
    gl::GenTextures(1, &m_handle);
    gl::BindTexture(gl::TEXTURE_2D, m_handle);
    // Compute the number of MIP-map levels
    const GLsizei levels{use_mipmaps ? computeMipLvlCnt(width, height) : 0};
    // Allocate storage
    gl::TexStorage2D(gl::TEXTURE_2D, levels, SIF, width, height);
    // Set texture filering
    const GLint mag_filter{use_lin_filter ? gl::LINEAR : gl::NEAREST};
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, mag_filter);
    const GLint min_filter{[=](){
        if (use_mipmaps) {
            return use_lin_filter ? gl::NEAREST_MIPMAP_NEAREST : gl::NEAREST_MIPMAP_LINEAR;
        } else {
            return use_lin_filter ? gl::LINEAR : gl::NEAREST;
        }
    }()};
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, min_filter);
    // Use border-clamping for both dimensions
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_BORDER);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_BORDER);
}

template <GLenum SIF>
GLTexture2D<SIF>::GLTexture2D(GLTexture2D&& tex): m_handle{tex.m_handle} {
    // Mark as moved
    tex.m_handle = 0;
}

template <GLenum SIF>
GLTexture2D<SIF>& GLTexture2D<SIF>::operator=(GLTexture2D&& tex) {
    assert(this != &tex);
    // Delete the old texture
    gl::DeleteTextures(1, &m_handle);
    // Now copy the data
    memcpy(this, &tex, sizeof(*this));
    // Mark as moved
    tex.m_handle = 0;
    return *this;
}

template <GLenum SIF>
GLTexture2D<SIF>::~GLTexture2D() {
    // Check if it was moved
    if (m_handle) {
        gl::DeleteTextures(1, &m_handle);
    }
}

template <GLenum SIF>
GLuint GLTexture2D<SIF>::id() const {
    return m_handle;
}
<commit_msg>Removed a using declaration from the header to avoid global namespace pollution.<commit_after>#include "GLTexture2D.h"
#include <OpenGL\gl_core_4_4.hpp>
#include <GLM\detail\func_common.hpp>

// Computes the number of MIP-map levels given the texture dimensions
static inline GLsizei computeMipLvlCnt(const GLsizei width, const GLsizei height) {
    return static_cast<GLsizei>(floor(log2(glm::max(width, height))) + 1);
}

template <GLenum SIF>
GLTexture2D<SIF>::GLTexture2D(const GLenum tex_unit, const GLsizei width, const GLsizei height,
                               const bool use_mipmaps, const bool use_lin_filter) {
    // Aquire a texture handle
    gl::ActiveTexture(gl::TEXTURE0 + tex_unit);
    gl::GenTextures(1, &m_handle);
    gl::BindTexture(gl::TEXTURE_2D, m_handle);
    // Compute the number of MIP-map levels
    const GLsizei levels{use_mipmaps ? computeMipLvlCnt(width, height) : 0};
    // Allocate storage
    gl::TexStorage2D(gl::TEXTURE_2D, levels, SIF, width, height);
    // Set texture filering
    const GLint mag_filter{use_lin_filter ? gl::LINEAR : gl::NEAREST};
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, mag_filter);
    const GLint min_filter{[=](){
        if (use_mipmaps) {
            return use_lin_filter ? gl::NEAREST_MIPMAP_NEAREST : gl::NEAREST_MIPMAP_LINEAR;
        } else {
            return use_lin_filter ? gl::LINEAR : gl::NEAREST;
        }
    }()};
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, min_filter);
    // Use border-clamping for both dimensions
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_BORDER);
    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_BORDER);
}

template <GLenum SIF>
GLTexture2D<SIF>::GLTexture2D(GLTexture2D&& tex): m_handle{tex.m_handle} {
    // Mark as moved
    tex.m_handle = 0;
}

template <GLenum SIF>
GLTexture2D<SIF>& GLTexture2D<SIF>::operator=(GLTexture2D&& tex) {
    assert(this != &tex);
    // Delete the old texture
    gl::DeleteTextures(1, &m_handle);
    // Now copy the data
    memcpy(this, &tex, sizeof(*this));
    // Mark as moved
    tex.m_handle = 0;
    return *this;
}

template <GLenum SIF>
GLTexture2D<SIF>::~GLTexture2D() {
    // Check if it was moved
    if (m_handle) {
        gl::DeleteTextures(1, &m_handle);
    }
}

template <GLenum SIF>
GLuint GLTexture2D<SIF>::id() const {
    return m_handle;
}
<|endoftext|>


<commit_before>/*
 * DShow Native wrapper
 * Copyright (c) 2010 Gianluigi Tiesi <sherpya@netfarm.it>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "stdafx.h"

class DSCodec
{
public:
    DSCodec::DSCodec(const char *filename, const GUID guid, BITMAPINFOHEADER *bih) :
      m_guid(guid), m_bih(bih), m_hDll(NULL), m_filter(NULL),
      m_iPin(NULL), m_oPin(NULL), m_iMem(NULL)
    {
        strncpy(m_fname, filename, MAX_PATH);
    }

    BOOL LoadLibrary(void)
    {
        return ((m_hDll = ::LoadLibrary(m_fname)) != NULL);
    }

    BOOL CreateFilter(void)
    {
        LPFNGETCLASSOBJECT pDllGetClassObject = (LPFNGETCLASSOBJECT) GetProcAddress(m_hDll, "DllGetClassObject");
        if (!pDllGetClassObject) return FALSE;

        HRESULT res;

        IClassFactory *factory;
        res = pDllGetClassObject(m_guid, IID_IClassFactory, (LPVOID *) &factory);
        if (FAILED(res))return FALSE;

        IUnknown* object;
        res = factory->CreateInstance(NULL, IID_IUnknown, (LPVOID *) &object);
        factory->Release();

        if (FAILED(res)) return FALSE;

        res = object->QueryInterface(IID_IBaseFilter, (LPVOID *) &m_filter);
        object->Release();

        return (!FAILED(res));
    }

    BOOL ReleaseFilter(void)
    {
        return m_filter->Release();
    }

    BOOL SetOutputType(void)
    {
        m_sDestType.majortype = MEDIATYPE_Video;
        m_sDestType.subtype = MEDIASUBTYPE_RGB24;
        m_sDestType.formattype = FORMAT_VideoInfo2;
        m_sDestType.bFixedSizeSamples = TRUE;
        m_sDestType.bTemporalCompression = FALSE;
        m_sDestType.lSampleSize = 1;
        m_sDestType.pUnk = 0;

        memset(&m_vi2, 0, sizeof(m_vi2));
        m_vi2.bmiHeader.biSizeImage = m_sDestType.lSampleSize;
        m_vi2.bmiHeader.biBitCount = 24;
        m_vi2.bmiHeader.biBitCount = 0;
        m_vi2.bmiHeader.biWidth = m_bih->biWidth;
        m_vi2.bmiHeader.biHeight = m_bih->biHeight;

        m_sDestType.cbFormat = sizeof(VIDEOINFOHEADER2);
        m_sDestType.pbFormat = (BYTE *) &m_vi2;

        return TRUE;
    }

    BOOL SetInputType(void)
    {
        m_sOurType.majortype = MEDIATYPE_Video;
        m_sOurType.subtype = MEDIATYPE_Video;
        m_sOurType.subtype.Data1 = m_bih->biCompression;
        m_sOurType.formattype = FORMAT_VideoInfo;
        m_sOurType.bFixedSizeSamples = FALSE;
        m_sOurType.bTemporalCompression = TRUE;
        m_sOurType.lSampleSize = 1;
        m_sOurType.pUnk = NULL;

        switch (m_bih->biCompression)
        {
            case mmioFOURCC('H', '2', '6', '4'):
            case mmioFOURCC('h', '2', '6', '4'):
            case mmioFOURCC('X', '2', '6', '4'):
            case mmioFOURCC('x', '2', '6', '4'):
            case mmioFOURCC('A', 'V', 'C', '1'):
            case mmioFOURCC('a', 'v', 'c', '1'):
            case mmioFOURCC('d', 'a', 'v', 'c'):
            case mmioFOURCC('D', 'A', 'V', 'C'):
            case mmioFOURCC('V', 'S', 'S', 'H'):
                return SetInputMPEG2();
        }
        return SetInputVideoInfo();
    }

    BOOL SetInputVideoInfo(void)
    {
        memset(&m_vi, 0, sizeof(m_vi));
        memcpy(&m_vi.bmiHeader, m_bih, m_bih->biSize);
        m_vi.rcSource.left = m_vi.rcSource.top = 0;
        m_vi.rcSource.right = m_bih->biWidth;
        m_vi.rcSource.bottom = m_bih->biHeight;
        m_vi.rcTarget = m_vi.rcSource;

        m_sOurType.formattype = FORMAT_VideoInfo;
        m_sOurType.pbFormat = (BYTE *) &m_vi;
        m_sOurType.cbFormat = sizeof(VIDEOINFOHEADER);
        return TRUE;
    }

    BOOL SetInputMPEG2(void)
    {
        memset(&m_mp2vi, 0, sizeof(m_mp2vi));
        m_mp2vi.hdr.rcSource.left = m_mp2vi.hdr.rcSource.top = 0;
        m_mp2vi.hdr.rcSource.right = m_bih->biWidth;
        m_mp2vi.hdr.rcSource.bottom = m_bih->biHeight;
        m_mp2vi.hdr.rcTarget = m_mp2vi.hdr.rcSource;
        m_mp2vi.hdr.dwPictAspectRatioX = m_bih->biWidth;
        m_mp2vi.hdr.dwPictAspectRatioY = m_bih->biHeight;
        memcpy(&m_mp2vi.hdr.bmiHeader, m_bih, sizeof(BITMAPINFOHEADER));
        m_mp2vi.hdr.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

        m_sOurType.formattype = FORMAT_MPEG2Video;
        m_sOurType.pbFormat = (BYTE *) &m_mp2vi;
        m_sOurType.cbFormat = sizeof(MPEG2VIDEOINFO);
        return TRUE;
    }

    BOOL EnumPins(void)
    {
        HRESULT res;
        IEnumPins *enumpins;
        res = m_filter->EnumPins(&enumpins);
        enumpins->Reset();

        IPin *pin;
        PIN_INFO pInfo;

        while ((res = enumpins->Next(1, &pin, NULL)) == S_OK)
        {
            pin->QueryPinInfo(&pInfo);
            wprintf(L"Pin: %s - %s\n", pInfo.achName, (pInfo.dir == PINDIR_INPUT) ? L"Input" : L"Output");
            if (pInfo.dir == PINDIR_INPUT)
            {
                m_iPin = pin;
                m_iPin->AddRef();
            }
            else if (pInfo.dir == PINDIR_OUTPUT)
            {
                m_oPin = pin;
                m_oPin->AddRef();
            }
            pin->Release();
        }

        enumpins->Release();
        res = m_iPin->QueryInterface(IID_IMemInputPin, (LPVOID *) &m_iMem);
        return TRUE;
    }

    BOOL CreateGraph(void)
    {
        HRESULT res;
        this->EnumPins();
        this->SetInputType();
        this->SetOutputType();

        res = m_iPin->QueryAccept(&m_sOurType);

        DebugBreak();

        CBaseFilter2 *basef = new CBaseFilter2();
        CBasePin *remotep = basef->GetPin(1);
        remotep->AddRef();
        res = m_iPin->ReceiveConnection(remotep, &m_sOurType);

        // Create receiver
        //CBaseFilter s_filter = CBaseFilter();
        res = m_oPin->QueryAccept(&m_sDestType);

        return TRUE;
    }

    BOOL ShowPropertyPage(void)
    {
        if (!m_filter) return FALSE;
        HRESULT res;
        ISpecifyPropertyPages *pProp;
        if ((res = m_filter->QueryInterface(IID_ISpecifyPropertyPages, (LPVOID *) &pProp)) == S_OK)
        {
            // Get the filter's name and IUnknown pointer.
            FILTER_INFO FilterInfo;
            res = m_filter->QueryFilterInfo(&FilterInfo);
            IUnknown *pFilterUnk;
            res = m_filter->QueryInterface(IID_IUnknown, (LPVOID *) &pFilterUnk);
            CAUUID caGUID;
            pProp->GetPages(&caGUID);
            pProp->Release();

            __try
            {
                res = OleCreatePropertyFrame(
                    NULL,                   // Parent window
                    0, 0,                   // Reserved
                    FilterInfo.achName,     // Caption for the dialog box
                    1,                      // Number of objects (just the filter)
                    &pFilterUnk,            // Array of object pointers.
                    caGUID.cElems,          // Number of property pages
                    caGUID.pElems,          // Array of property page CLSIDs
                    0,                      // Locale identifier
                    0, NULL                 // Reserved
                    );
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
            }
            // Clean up.
            pFilterUnk->Release();
            /* FIXME: it crashes (broken example on msdn?) */
            //FilterInfo.pGraph->Release();
            CoTaskMemFree(caGUID.pElems);
        }
        return (!FAILED(res));
    }

private:
    HMODULE m_hDll;
    GUID m_guid;
    char m_fname[MAX_PATH + 1];
    BITMAPINFOHEADER *m_bih;
    IBaseFilter *m_filter;

    IPin *m_iPin;
    IPin *m_oPin;
    IMemInputPin *m_iMem;
    AM_MEDIA_TYPE m_sOurType, m_sDestType;
    MPEG2VIDEOINFO m_mp2vi;
    VIDEOINFOHEADER m_vi;
    VIDEOINFOHEADER2 m_vi2;
};

extern "C" DSCodec * WINAPI DSOpenCodec(const char *dll, const GUID guid, BITMAPINFOHEADER* bih)
{
    DSCodec *codec = new DSCodec(dll, guid, bih);
    if (!codec->LoadLibrary())
        return NULL;
    if (!codec->CreateFilter())
        return NULL;
    if (!codec->CreateGraph())
        return NULL;
    return codec;
}

extern "C" void WINAPI DSCloseCodec(DSCodec *codec)
{
    codec->ReleaseFilter();
    delete codec;
}

extern "C" BOOL WINAPI DSShowPropertyPage(DSCodec *codec)
{
    return codec->ShowPropertyPage();
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
    switch (reason)
    {
        case DLL_PROCESS_ATTACH:
        {
            DisableThreadLibraryCalls(hModule);
            return (OleInitialize(NULL) == S_OK);
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            return TRUE;
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

<commit_msg>rename vars to match loader vars<commit_after>/*
 * DShow Native wrapper
 * Copyright (c) 2010 Gianluigi Tiesi <sherpya@netfarm.it>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "stdafx.h"

class DSCodec
{
public:
    DSCodec::DSCodec(const char *filename, const GUID guid, BITMAPINFOHEADER *bih) :
      m_guid(guid), m_bih(bih), m_hDll(NULL), m_pFilter(NULL),
      m_pInputPin(NULL), m_pOutputPin(NULL), m_pImp(NULL)
    {
        strncpy(m_fname, filename, MAX_PATH);
    }

    BOOL LoadLibrary(void)
    {
        return ((m_hDll = ::LoadLibrary(m_fname)) != NULL);
    }

    BOOL CreateFilter(void)
    {
        LPFNGETCLASSOBJECT pDllGetClassObject = (LPFNGETCLASSOBJECT) GetProcAddress(m_hDll, "DllGetClassObject");
        if (!pDllGetClassObject) return FALSE;

        HRESULT res;

        IClassFactory *factory;
        res = pDllGetClassObject(m_guid, IID_IClassFactory, (LPVOID *) &factory);
        if (FAILED(res))return FALSE;

        IUnknown* object;
        res = factory->CreateInstance(NULL, IID_IUnknown, (LPVOID *) &object);
        factory->Release();

        if (FAILED(res)) return FALSE;

        res = object->QueryInterface(IID_IBaseFilter, (LPVOID *) &m_pFilter);
        object->Release();

        return (!FAILED(res));
    }

    BOOL ReleaseFilter(void)
    {
        return m_pFilter->Release();
    }

    BOOL SetOutputType(void)
    {
        m_pDestType.majortype = MEDIATYPE_Video;
        m_pDestType.subtype = MEDIASUBTYPE_RGB24;
        m_pDestType.formattype = FORMAT_VideoInfo2;
        m_pDestType.bFixedSizeSamples = TRUE;
        m_pDestType.bTemporalCompression = FALSE;
        m_pDestType.lSampleSize = 1;
        m_pDestType.pUnk = 0;

        memset(&m_vi2, 0, sizeof(m_vi2));
        m_vi2.bmiHeader.biSizeImage = m_pDestType.lSampleSize;
        m_vi2.bmiHeader.biBitCount = 24;
        m_vi2.bmiHeader.biBitCount = 0;
        m_vi2.bmiHeader.biWidth = m_bih->biWidth;
        m_vi2.bmiHeader.biHeight = m_bih->biHeight;

        m_pDestType.cbFormat = sizeof(VIDEOINFOHEADER2);
        m_pDestType.pbFormat = (BYTE *) &m_vi2;

        return TRUE;
    }

    BOOL SetInputType(void)
    {
        m_pOurType.majortype = MEDIATYPE_Video;
        m_pOurType.subtype = MEDIATYPE_Video;
        m_pOurType.subtype.Data1 = m_bih->biCompression;
        m_pOurType.formattype = FORMAT_VideoInfo;
        m_pOurType.bFixedSizeSamples = FALSE;
        m_pOurType.bTemporalCompression = TRUE;
        m_pOurType.lSampleSize = 1;
        m_pOurType.pUnk = NULL;

        switch (m_bih->biCompression)
        {
            case mmioFOURCC('H', '2', '6', '4'):
            case mmioFOURCC('h', '2', '6', '4'):
            case mmioFOURCC('X', '2', '6', '4'):
            case mmioFOURCC('x', '2', '6', '4'):
            case mmioFOURCC('A', 'V', 'C', '1'):
            case mmioFOURCC('a', 'v', 'c', '1'):
            case mmioFOURCC('d', 'a', 'v', 'c'):
            case mmioFOURCC('D', 'A', 'V', 'C'):
            case mmioFOURCC('V', 'S', 'S', 'H'):
                return SetInputMPEG2();
        }
        return SetInputVideoInfo();
    }

    BOOL SetInputVideoInfo(void)
    {
        memset(&m_vi, 0, sizeof(m_vi));
        memcpy(&m_vi.bmiHeader, m_bih, m_bih->biSize);
        m_vi.rcSource.left = m_vi.rcSource.top = 0;
        m_vi.rcSource.right = m_bih->biWidth;
        m_vi.rcSource.bottom = m_bih->biHeight;
        m_vi.rcTarget = m_vi.rcSource;

        m_pOurType.formattype = FORMAT_VideoInfo;
        m_pOurType.pbFormat = (BYTE *) &m_vi;
        m_pOurType.cbFormat = sizeof(VIDEOINFOHEADER);
        return TRUE;
    }

    BOOL SetInputMPEG2(void)
    {
        memset(&m_mp2vi, 0, sizeof(m_mp2vi));
        m_mp2vi.hdr.rcSource.left = m_mp2vi.hdr.rcSource.top = 0;
        m_mp2vi.hdr.rcSource.right = m_bih->biWidth;
        m_mp2vi.hdr.rcSource.bottom = m_bih->biHeight;
        m_mp2vi.hdr.rcTarget = m_mp2vi.hdr.rcSource;
        m_mp2vi.hdr.dwPictAspectRatioX = m_bih->biWidth;
        m_mp2vi.hdr.dwPictAspectRatioY = m_bih->biHeight;
        memcpy(&m_mp2vi.hdr.bmiHeader, m_bih, sizeof(BITMAPINFOHEADER));
        m_mp2vi.hdr.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

        m_pOurType.formattype = FORMAT_MPEG2Video;
        m_pOurType.pbFormat = (BYTE *) &m_mp2vi;
        m_pOurType.cbFormat = sizeof(MPEG2VIDEOINFO);
        return TRUE;
    }

    BOOL EnumPins(void)
    {
        HRESULT res;
        IEnumPins *enumpins;
        res = m_pFilter->EnumPins(&enumpins);
        enumpins->Reset();

        IPin *pin;
        PIN_INFO pInfo;

        while ((res = enumpins->Next(1, &pin, NULL)) == S_OK)
        {
            pin->QueryPinInfo(&pInfo);
            wprintf(L"Pin: %s - %s\n", pInfo.achName, (pInfo.dir == PINDIR_INPUT) ? L"Input" : L"Output");
            if (pInfo.dir == PINDIR_INPUT)
            {
                m_pInputPin = pin;
                m_pInputPin->AddRef();
            }
            else if (pInfo.dir == PINDIR_OUTPUT)
            {
                m_pOutputPin = pin;
                m_pOutputPin->AddRef();
            }
            pin->Release();
        }

        enumpins->Release();
        res = m_pInputPin->QueryInterface(IID_IMemInputPin, (LPVOID *) &m_pImp);
        return TRUE;
    }

    BOOL CreateGraph(void)
    {
        HRESULT res;
        this->EnumPins();
        this->SetInputType();
        this->SetOutputType();

        res = m_pInputPin->QueryAccept(&m_pOurType);

        CBaseFilter2 *basef = new CBaseFilter2();
        CBasePin *remotep = basef->GetPin(1);
        remotep->AddRef();
        res = m_pInputPin->ReceiveConnection(remotep, &m_pOurType);

        // Create receiver
        //CBaseFilter s_filter = CBaseFilter();
        res = m_pOutputPin->QueryAccept(&m_pDestType);
        res = m_pImp->NotifyAllocator(m_pAll, FALSE);

        return TRUE;
    }

    BOOL ShowPropertyPage(void)
    {
        if (!m_pFilter) return FALSE;
        HRESULT res;
        ISpecifyPropertyPages *pProp;
        if ((res = m_pFilter->QueryInterface(IID_ISpecifyPropertyPages, (LPVOID *) &pProp)) == S_OK)
        {
            // Get the filter's name and IUnknown pointer.
            FILTER_INFO FilterInfo;
            res = m_pFilter->QueryFilterInfo(&FilterInfo);
            IUnknown *pFilterUnk;
            res = m_pFilter->QueryInterface(IID_IUnknown, (LPVOID *) &pFilterUnk);
            CAUUID caGUID;
            pProp->GetPages(&caGUID);
            pProp->Release();

            __try
            {
                res = OleCreatePropertyFrame(
                    NULL,                   // Parent window
                    0, 0,                   // Reserved
                    FilterInfo.achName,     // Caption for the dialog box
                    1,                      // Number of objects (just the filter)
                    &pFilterUnk,            // Array of object pointers.
                    caGUID.cElems,          // Number of property pages
                    caGUID.pElems,          // Array of property page CLSIDs
                    0,                      // Locale identifier
                    0, NULL                 // Reserved
                    );
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
            }
            // Clean up.
            pFilterUnk->Release();
            /* FIXME: it crashes (broken example on msdn?) */
            //FilterInfo.pGraph->Release();
            CoTaskMemFree(caGUID.pElems);
        }
        return (!FAILED(res));
    }

private:
    HMODULE m_hDll;
    GUID m_guid;
    char m_fname[MAX_PATH + 1];
    BITMAPINFOHEADER *m_bih;
    IBaseFilter *m_pFilter;

    IPin *m_pInputPin;
    IPin *m_pOutputPin;
    IMemInputPin *m_pImp;
    IMemAllocator *m_pAll;
    AM_MEDIA_TYPE m_pOurType, m_pDestType;
    MPEG2VIDEOINFO m_mp2vi;
    VIDEOINFOHEADER m_vi;
    VIDEOINFOHEADER2 m_vi2;
};

extern "C" DSCodec * WINAPI DSOpenCodec(const char *dll, const GUID guid, BITMAPINFOHEADER* bih)
{
    DSCodec *codec = new DSCodec(dll, guid, bih);
    if (!codec->LoadLibrary())
        return NULL;
    if (!codec->CreateFilter())
        return NULL;
    if (!codec->CreateGraph())
        return NULL;
    return codec;
}

extern "C" void WINAPI DSCloseCodec(DSCodec *codec)
{
    codec->ReleaseFilter();
    delete codec;
}

extern "C" BOOL WINAPI DSShowPropertyPage(DSCodec *codec)
{
    return codec->ShowPropertyPage();
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
    switch (reason)
    {
        case DLL_PROCESS_ATTACH:
        {
            DisableThreadLibraryCalls(hModule);
            return (OleInitialize(NULL) == S_OK);
        }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            return TRUE;
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

<|endoftext|>


<commit_before>#include <pigpio.h>
#include <nan.h>

#if NODE_VERSION_AT_LEAST(0, 11, 13)
static void gpioISREventLoopHandler(uv_async_t* handle);
#else
static void gpioISREventLoopHandler(uv_async_t* handle, int status);
#endif

// TODO errors returned by uv calls are ignored

class GpioISR_t {
public:
  GpioISR_t() : callback_(0) {
    uv_async_init(uv_default_loop(), &async_, gpioISREventLoopHandler);

    // Prevent async from keeping event loop alive, for the time being.
    uv_unref((uv_handle_t *) &async_);
  }

  ~GpioISR_t() {
    if (callback_) {
      uv_unref((uv_handle_t *) &async_);
      delete callback_;
    }

    uv_close((uv_handle_t *) &async_, 0);

    callback_ = 0;
  }

  void AsyncSend() {
    uv_async_send(&async_);
  }

  void SetCallback(Nan::Callback *callback) {
    if (callback_) {
      uv_unref((uv_handle_t *) &async_);
      delete callback_;
    }

    callback_ = callback;

    if (callback_) {
      uv_ref((uv_handle_t *) &async_);
    }
  }

  Nan::Callback *Callback() {
    return callback_;
  }

private:
  uv_async_t async_;
  Nan::Callback *callback_;
};

static GpioISR_t gpioISR_g[PI_MAX_USER_GPIO + 1];
static int gpio_g;
static int level_g;
static uint32_t tick_g;
static uv_sem_t sem_g;

void ThrowPigpioError(int err, const char *pigpiocall) {
  char buf[128];

  snprintf(buf, sizeof(buf), "pigpio error %d in %s", err, pigpiocall);

  Nan::ThrowError(buf);
}

NAN_METHOD(gpioInitialise) {
  int rc = gpioInitialise();
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioInitialise");
  }

  info.GetReturnValue().Set(rc);
}

NAN_METHOD(gpioTerminate) {
  gpioTerminate();
}

NAN_METHOD(gpioSetMode) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetMode"));
  }

  unsigned gpio = info[0]->Uint32Value();
  unsigned mode = info[1]->Uint32Value();

  int rc = gpioSetMode(gpio, mode);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioSetMode");
  }
}

NAN_METHOD(gpioGetMode) {
  if (info.Length() < 1 || !info[0]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioGetMode"));
  }

  unsigned gpio = info[0]->Uint32Value();

  int rc = gpioGetMode(gpio);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioGetMode");
  }

  info.GetReturnValue().Set(rc);
}

NAN_METHOD(gpioSetPullUpDown) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetPullUpDown"));
  }

  unsigned gpio = info[0]->Uint32Value();
  unsigned pud = info[1]->Uint32Value();

  int rc = gpioSetPullUpDown(gpio, pud);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioSetPullUpDown");
  }
}

NAN_METHOD(gpioRead) {
  if (info.Length() < 1 || !info[0]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioRead"));
  }

  unsigned gpio = info[0]->Uint32Value();

  int rc = gpioRead(gpio);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioRead");
  }

  info.GetReturnValue().Set(rc);
}

NAN_METHOD(gpioWrite) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetPullUpDown"));
  }

  unsigned gpio = info[0]->Uint32Value();
  unsigned level = info[1]->Uint32Value();

  int rc = gpioWrite(gpio, level);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioWrite");
  }
}

NAN_METHOD(gpioPWM) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioPWM"));
  }

  unsigned user_gpio = info[0]->Uint32Value();
  unsigned dutycycle = info[1]->Uint32Value();

  int rc = gpioPWM(user_gpio, dutycycle);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioPWM");
  }
}

NAN_METHOD(gpioServo) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioServo"));
  }

  unsigned user_gpio = info[0]->Uint32Value();
  unsigned pulsewidth = info[1]->Uint32Value();

  int rc = gpioServo(user_gpio, pulsewidth);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioServo");
  }
}

// gpioISRHandler is not executed in the event loop thread
static void gpioISRHandler(int gpio, int level, uint32_t tick) {
  uv_sem_wait(&sem_g);

  gpio_g = gpio;
  level_g = level;
  tick_g = tick;

  gpioISR_g[gpio].AsyncSend();
}

// gpioISREventLoopHandler is executed in the event loop thread.
#if NODE_VERSION_AT_LEAST(0, 11, 13)
static void gpioISREventLoopHandler(uv_async_t* handle) {
#else
static void gpioISREventLoopHandler(uv_async_t* handle, int status) {
#endif
  Nan::HandleScope scope;

  if (gpioISR_g[gpio_g].Callback()) {
    v8::Local<v8::Value> args[3] = {
      Nan::New<v8::Integer>(gpio_g),
      Nan::New<v8::Integer>(level_g),
      Nan::New<v8::Integer>(tick_g)
    };
    gpioISR_g[gpio_g].Callback()->Call(3, args);
  }

  uv_sem_post(&sem_g);
}

static NAN_METHOD(gpioSetISRFunc) {
  if (info.Length() < 3 ||
      !info[0]->IsUint32() ||
      !info[1]->IsUint32() ||
      !info[2]->IsInt32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetISRFunc"));
  }

  if (info.Length() >= 4 &&
      !info[3]->IsFunction() &&
      !info[3]->IsNull() &&
      !info[3]->IsUndefined()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetISRFunc"));
  }

  unsigned user_gpio = info[0]->Uint32Value();
  unsigned edge = info[1]->Uint32Value();
  int timeout = info[2]->Int32Value();
  Nan::Callback *callback = 0;
  gpioISRFunc_t isrFunc = 0;

  if (info.Length() >= 4 && info[3]->IsFunction()) {
    callback = new Nan::Callback(info[3].As<v8::Function>());
    isrFunc = gpioISRHandler;
  }

  gpioISR_g[user_gpio].SetCallback(callback);

  int rc = gpioSetISRFunc(user_gpio, edge, timeout, isrFunc);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioSetISRFunc");
  }
}

static void SetConst(
  Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target,
  const char* name,
  int value
) {
  Nan::Set(target,
    Nan::New<v8::String>(name).ToLocalChecked(),
    Nan::New<v8::Integer>(value)
  );
}

static void SetFunction(
  Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target,
  const char* name,
  Nan::FunctionCallback callback
) {
  Nan::Set(target,
    Nan::New(name).ToLocalChecked(),
    Nan::GetFunction(Nan::New<v8::FunctionTemplate>(callback)).ToLocalChecked()
  );
}

NAN_MODULE_INIT(InitAll) {
  uv_sem_init(&sem_g, 1);

  SetConst(target, "PI_MAX_USER_GPIO", PI_MAX_USER_GPIO);

  /* mode */
  SetConst(target, "PI_INPUT", PI_INPUT);
  SetConst(target, "PI_OUTPUT", PI_OUTPUT);
  SetConst(target, "PI_ALT0", PI_ALT0);
  SetConst(target, "PI_ALT1", PI_ALT1);
  SetConst(target, "PI_ALT2", PI_ALT2);
  SetConst(target, "PI_ALT3", PI_ALT3);
  SetConst(target, "PI_ALT4", PI_ALT4);
  SetConst(target, "PI_ALT5", PI_ALT5);

  /* pud */
  SetConst(target, "PI_PUD_OFF", PI_PUD_OFF);
  SetConst(target, "PI_PUD_DOWN", PI_PUD_DOWN);
  SetConst(target, "PI_PUD_UP", PI_PUD_UP);

  /* isr */
  SetConst(target, "RISING_EDGE", RISING_EDGE);
  SetConst(target, "FALLING_EDGE", FALLING_EDGE);
  SetConst(target, "EITHER_EDGE", EITHER_EDGE);

  /* timeout */
  SetConst(target, "PI_TIMEOUT", PI_TIMEOUT);

  /* gpio numbers */
  SetConst(target, "PI_MIN_GPIO", PI_MIN_GPIO);
  SetConst(target, "PI_MAX_GPIO", PI_MAX_GPIO);
  SetConst(target, "PI_MAX_USER_GPIO", PI_MAX_USER_GPIO);

  /* error codes */
  SetConst(target, "PI_INIT_FAILED", PI_INIT_FAILED);

  SetFunction(target, "gpioInitialise", gpioInitialise);
  SetFunction(target, "gpioTerminate", gpioTerminate);

  SetFunction(target, "gpioSetMode", gpioSetMode);
  SetFunction(target, "gpioGetMode", gpioGetMode);
  SetFunction(target, "gpioSetPullUpDown", gpioSetPullUpDown);
  SetFunction(target, "gpioRead", gpioRead);
  SetFunction(target, "gpioWrite", gpioWrite);

  SetFunction(target, "gpioPWM", gpioPWM);
  SetFunction(target, "gpioServo", gpioServo);

  SetFunction(target, "gpioSetISRFunc", gpioSetISRFunc);
}

NODE_MODULE(pigpio, InitAll)

<commit_msg>define PI_MAX_USER_GPIO once not twice<commit_after>#include <pigpio.h>
#include <nan.h>

#if NODE_VERSION_AT_LEAST(0, 11, 13)
static void gpioISREventLoopHandler(uv_async_t* handle);
#else
static void gpioISREventLoopHandler(uv_async_t* handle, int status);
#endif

// TODO errors returned by uv calls are ignored

class GpioISR_t {
public:
  GpioISR_t() : callback_(0) {
    uv_async_init(uv_default_loop(), &async_, gpioISREventLoopHandler);

    // Prevent async from keeping event loop alive, for the time being.
    uv_unref((uv_handle_t *) &async_);
  }

  ~GpioISR_t() {
    if (callback_) {
      uv_unref((uv_handle_t *) &async_);
      delete callback_;
    }

    uv_close((uv_handle_t *) &async_, 0);

    callback_ = 0;
  }

  void AsyncSend() {
    uv_async_send(&async_);
  }

  void SetCallback(Nan::Callback *callback) {
    if (callback_) {
      uv_unref((uv_handle_t *) &async_);
      delete callback_;
    }

    callback_ = callback;

    if (callback_) {
      uv_ref((uv_handle_t *) &async_);
    }
  }

  Nan::Callback *Callback() {
    return callback_;
  }

private:
  uv_async_t async_;
  Nan::Callback *callback_;
};

static GpioISR_t gpioISR_g[PI_MAX_USER_GPIO + 1];
static int gpio_g;
static int level_g;
static uint32_t tick_g;
static uv_sem_t sem_g;

void ThrowPigpioError(int err, const char *pigpiocall) {
  char buf[128];

  snprintf(buf, sizeof(buf), "pigpio error %d in %s", err, pigpiocall);

  Nan::ThrowError(buf);
}

NAN_METHOD(gpioInitialise) {
  int rc = gpioInitialise();
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioInitialise");
  }

  info.GetReturnValue().Set(rc);
}

NAN_METHOD(gpioTerminate) {
  gpioTerminate();
}

NAN_METHOD(gpioSetMode) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetMode"));
  }

  unsigned gpio = info[0]->Uint32Value();
  unsigned mode = info[1]->Uint32Value();

  int rc = gpioSetMode(gpio, mode);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioSetMode");
  }
}

NAN_METHOD(gpioGetMode) {
  if (info.Length() < 1 || !info[0]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioGetMode"));
  }

  unsigned gpio = info[0]->Uint32Value();

  int rc = gpioGetMode(gpio);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioGetMode");
  }

  info.GetReturnValue().Set(rc);
}

NAN_METHOD(gpioSetPullUpDown) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetPullUpDown"));
  }

  unsigned gpio = info[0]->Uint32Value();
  unsigned pud = info[1]->Uint32Value();

  int rc = gpioSetPullUpDown(gpio, pud);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioSetPullUpDown");
  }
}

NAN_METHOD(gpioRead) {
  if (info.Length() < 1 || !info[0]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioRead"));
  }

  unsigned gpio = info[0]->Uint32Value();

  int rc = gpioRead(gpio);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioRead");
  }

  info.GetReturnValue().Set(rc);
}

NAN_METHOD(gpioWrite) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetPullUpDown"));
  }

  unsigned gpio = info[0]->Uint32Value();
  unsigned level = info[1]->Uint32Value();

  int rc = gpioWrite(gpio, level);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioWrite");
  }
}

NAN_METHOD(gpioPWM) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioPWM"));
  }

  unsigned user_gpio = info[0]->Uint32Value();
  unsigned dutycycle = info[1]->Uint32Value();

  int rc = gpioPWM(user_gpio, dutycycle);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioPWM");
  }
}

NAN_METHOD(gpioServo) {
  if (info.Length() < 2 || !info[0]->IsUint32() || !info[1]->IsUint32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioServo"));
  }

  unsigned user_gpio = info[0]->Uint32Value();
  unsigned pulsewidth = info[1]->Uint32Value();

  int rc = gpioServo(user_gpio, pulsewidth);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioServo");
  }
}

// gpioISRHandler is not executed in the event loop thread
static void gpioISRHandler(int gpio, int level, uint32_t tick) {
  uv_sem_wait(&sem_g);

  gpio_g = gpio;
  level_g = level;
  tick_g = tick;

  gpioISR_g[gpio].AsyncSend();
}

// gpioISREventLoopHandler is executed in the event loop thread.
#if NODE_VERSION_AT_LEAST(0, 11, 13)
static void gpioISREventLoopHandler(uv_async_t* handle) {
#else
static void gpioISREventLoopHandler(uv_async_t* handle, int status) {
#endif
  Nan::HandleScope scope;

  if (gpioISR_g[gpio_g].Callback()) {
    v8::Local<v8::Value> args[3] = {
      Nan::New<v8::Integer>(gpio_g),
      Nan::New<v8::Integer>(level_g),
      Nan::New<v8::Integer>(tick_g)
    };
    gpioISR_g[gpio_g].Callback()->Call(3, args);
  }

  uv_sem_post(&sem_g);
}

static NAN_METHOD(gpioSetISRFunc) {
  if (info.Length() < 3 ||
      !info[0]->IsUint32() ||
      !info[1]->IsUint32() ||
      !info[2]->IsInt32()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetISRFunc"));
  }

  if (info.Length() >= 4 &&
      !info[3]->IsFunction() &&
      !info[3]->IsNull() &&
      !info[3]->IsUndefined()) {
    return Nan::ThrowError(Nan::ErrnoException(EINVAL, "gpioSetISRFunc"));
  }

  unsigned user_gpio = info[0]->Uint32Value();
  unsigned edge = info[1]->Uint32Value();
  int timeout = info[2]->Int32Value();
  Nan::Callback *callback = 0;
  gpioISRFunc_t isrFunc = 0;

  if (info.Length() >= 4 && info[3]->IsFunction()) {
    callback = new Nan::Callback(info[3].As<v8::Function>());
    isrFunc = gpioISRHandler;
  }

  gpioISR_g[user_gpio].SetCallback(callback);

  int rc = gpioSetISRFunc(user_gpio, edge, timeout, isrFunc);
  if (rc < 0) {
    return ThrowPigpioError(rc, "gpioSetISRFunc");
  }
}

static void SetConst(
  Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target,
  const char* name,
  int value
) {
  Nan::Set(target,
    Nan::New<v8::String>(name).ToLocalChecked(),
    Nan::New<v8::Integer>(value)
  );
}

static void SetFunction(
  Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target,
  const char* name,
  Nan::FunctionCallback callback
) {
  Nan::Set(target,
    Nan::New(name).ToLocalChecked(),
    Nan::GetFunction(Nan::New<v8::FunctionTemplate>(callback)).ToLocalChecked()
  );
}

NAN_MODULE_INIT(InitAll) {
  uv_sem_init(&sem_g, 1);

  /* mode */
  SetConst(target, "PI_INPUT", PI_INPUT);
  SetConst(target, "PI_OUTPUT", PI_OUTPUT);
  SetConst(target, "PI_ALT0", PI_ALT0);
  SetConst(target, "PI_ALT1", PI_ALT1);
  SetConst(target, "PI_ALT2", PI_ALT2);
  SetConst(target, "PI_ALT3", PI_ALT3);
  SetConst(target, "PI_ALT4", PI_ALT4);
  SetConst(target, "PI_ALT5", PI_ALT5);

  /* pud */
  SetConst(target, "PI_PUD_OFF", PI_PUD_OFF);
  SetConst(target, "PI_PUD_DOWN", PI_PUD_DOWN);
  SetConst(target, "PI_PUD_UP", PI_PUD_UP);

  /* isr */
  SetConst(target, "RISING_EDGE", RISING_EDGE);
  SetConst(target, "FALLING_EDGE", FALLING_EDGE);
  SetConst(target, "EITHER_EDGE", EITHER_EDGE);

  /* timeout */
  SetConst(target, "PI_TIMEOUT", PI_TIMEOUT);

  /* gpio numbers */
  SetConst(target, "PI_MIN_GPIO", PI_MIN_GPIO);
  SetConst(target, "PI_MAX_GPIO", PI_MAX_GPIO);
  SetConst(target, "PI_MAX_USER_GPIO", PI_MAX_USER_GPIO);

  /* error codes */
  SetConst(target, "PI_INIT_FAILED", PI_INIT_FAILED);

  SetFunction(target, "gpioInitialise", gpioInitialise);
  SetFunction(target, "gpioTerminate", gpioTerminate);

  SetFunction(target, "gpioSetMode", gpioSetMode);
  SetFunction(target, "gpioGetMode", gpioGetMode);
  SetFunction(target, "gpioSetPullUpDown", gpioSetPullUpDown);
  SetFunction(target, "gpioRead", gpioRead);
  SetFunction(target, "gpioWrite", gpioWrite);

  SetFunction(target, "gpioPWM", gpioPWM);
  SetFunction(target, "gpioServo", gpioServo);

  SetFunction(target, "gpioSetISRFunc", gpioSetISRFunc);
}

NODE_MODULE(pigpio, InitAll)

<|endoftext|>