<commit_before>/* Copyright 2017 QReal Research Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include "pioneerStateMachineGenerator.h"

#include <generatorBase/semanticTree/semanticNode.h>
#include <generatorBase/semanticTree/simpleNode.h>

using namespace pioneer::lua;
using namespace generatorBase;
using namespace generatorBase::semantics;

PioneerStateMachineGenerator::PioneerStateMachineGenerator(
		const qrRepo::RepoApi &repo
		, qReal::ErrorReporterInterface &errorReporter
		, generatorBase::GeneratorCustomizer &customizer
		, generatorBase::PrimaryControlFlowValidator &validator
		, const qReal::Id &diagramId
		, QObject *parent
		, bool isThisDiagramMain)
	: GotoControlFlowGenerator(repo, errorReporter, customizer, validator, diagramId, parent, isThisDiagramMain)
{
	mAsynchronousNodes << "GeoTakeoff" << "GeoLanding" << "GoToPoint";
}

void PioneerStateMachineGenerator::registerNodeHook(std::function<void(const qReal::Id)> hook)
{
	mNodeHooks.append(hook);
}

void PioneerStateMachineGenerator::performGeneration()
{
	mSemanticTreeManager.reset(new SemanticTreeManager(*mSemanticTree, mErrorReporter, mErrorsOccured));
	GotoControlFlowGenerator::performGeneration();
}

void PioneerStateMachineGenerator::visitRegular(const qReal::Id &id, const QList<LinkInfo> &links)
{
	// Base class method checks for subprogram calls, which is irrelevant for now, but does not hurt and hopefully
	// will be needed later.
	ControlFlowGeneratorBase::visitRegular(id, links);

	SimpleNode * const thisNode = static_cast<SimpleNode *>(mSemanticTree->findNodeFor(id));
	SemanticNode *nextNode = nullptr;
	const qReal::Id target = links[0].target;

	if (mAsynchronousNodes.contains(id.element())) {
		if (mSemanticTree->findNodeFor(target)) {
			// thisNode is asyncronous node that transfers control to already visited node.
			// Generated code for thisNode will initiate asynchronous action and all we need to do is to generate
			// transition to a state which will execute target block when this block finishes its asynchronous
			// operation.
			nextNode = produceGotoNode(target);
			mSemanticTreeManager->addAfter(thisNode, nextNode);

			if (!mLabeledNodes.contains(target)) {
				//
				// Target node, despite being already visited, does not have a label, it means that it is a part of a
				// synchronous fragment. We copy that fragment from this node to the first asyncronous node and
				// label a start of the copied fragment. At the end of the fragment we will generate said asynchronous
				// node, which will initiate asynchronous operation, and then transition to its next state, which will
				// continue execution when operation is done.
				//
				// It will confuse "findNodeFor(id)" (there will be many semantic nodes for a block with given id), but
				// we actually do not care which copy will be used later, since they are the same.
				//
				nextNode = copySynchronousFragment(nextNode, target, true);
			}

			if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
				SemanticNode * const endNode = produceEndOfHandlerNode();
				mSemanticTreeManager->addAfter(nextNode, endNode);
			}
		} else {
			// thisNode is asynchronous node that transfers control to a node that has not been visited yet. Generating
			// transition into a state associated with that node and then a new handler for target node itself.
			nextNode = mSemanticTreeManager->produceLabeledNode(target);
			if (!nextNode) {
				return;
			} else {
				mLabeledNodes << nextNode->id();
			}

			SemanticNode * const gotoNode = produceGotoNode(target);
			mSemanticTreeManager->addAfter(thisNode, gotoNode);

			// Labeled node can not be a part of a zone (i.e. "then" or "else" branch), it shall be generated in top
			// level zone.
			if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
				SemanticNode * const endNode = produceEndOfHandlerNode();
				mSemanticTreeManager->addAfter(gotoNode, endNode);
				mSemanticTreeManager->addAfter(endNode, nextNode);
			} else {
				// Getting parent node (i.e. If statement to the branch of which our node belongs).
				NonZoneNode *aParent = mSemanticTreeManager->parent(thisNode);
				while (!mSemanticTreeManager->isTopLevelNode(aParent)) {
					aParent = mSemanticTreeManager->parent(aParent);
				}

				// Skipping "end" that finishes handler with If.
				SemanticNode * const endOfHandler = mSemanticTreeManager->findSibling(
						aParent
						, [](SemanticNode *node){ return node->id().element() == "EndOfHandler"; });

				if (!endOfHandler) {
					mErrorReporter.addError(tr("Can not find end of an If statement, generation internal error or "
							"too complex algorithmic construction."));
					mErrorsOccured = true;
					return;
				}

				// Adding our labeled node denoting new handler after the end of a handler with If node.
				mSemanticTreeManager->addAfter(endOfHandler, nextNode);
			}
		}
	} else {
		if (!mSemanticTree->findNodeFor(target)) {
			// It is not an asynchronous node, generating as-is.
			nextNode = mSemanticTree->produceNodeFor(target);
			mSemanticTreeManager->addAfter(thisNode, nextNode);
		} else {
			// Synchronous node leading to already visited node. Need some copypasting of synchronous fragments,
			// or else we will stall the program waiting for an event that was never initiated.
			copySynchronousFragment(thisNode, target, false);
		}
	}
}

void PioneerStateMachineGenerator::visitConditional(const qReal::Id &id, const QList<LinkInfo> &links)
{
	Q_UNUSED(links)

	const QPair<LinkInfo, LinkInfo> branches(ifBranchesFor(id));
	const LinkInfo thenLink = branches.first;
	const LinkInfo elseLink = branches.second;

	IfNode * const thisNode = static_cast<IfNode *>(mSemanticTree->findNodeFor(id));

	mSemanticTreeManager->addToZone(thisNode->thenZone(), thenLink.target);
	mSemanticTreeManager->addToZone(thisNode->elseZone(), elseLink.target);

	if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
		SemanticNode * const endNode = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(thisNode, endNode);
	}
}

void PioneerStateMachineGenerator::visitFinal(const qReal::Id &id, const QList<LinkInfo> &links)
{
	generatorBase::GotoControlFlowGenerator::visitFinal(id, links);

	// Here we are going to add finishing end-of-handler node in case it is missing (for example, diagrams like
	// "Initial Node" -> "Final Node" will not generate it automatically).
	// It is a kind of hack because asynchronous handler shall be a first-class entity and a zone node.

	SimpleNode * const thisNode = static_cast<SimpleNode *>(mSemanticTree->findNodeFor(id));

	// Getting root node.
	NonZoneNode *aParent = mSemanticTreeManager->parent(thisNode);
	while (!mSemanticTreeManager->isTopLevelNode(aParent)) {
		aParent = mSemanticTreeManager->parent(aParent);
	}

	// Searching for end-of-handler node.
	SemanticNode * endOfHandler = mSemanticTreeManager->findSibling(
			aParent
			, [](SemanticNode *node){ return node->id().element() == "EndOfHandler"; });

	if (!endOfHandler) {
		// If not found, create and add one.
		endOfHandler = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(thisNode, endOfHandler);
	}
}

void PioneerStateMachineGenerator::visit(const qReal::Id &nodeId, QList<utils::DeepFirstSearcher::LinkInfo> &links)
{
	generatorBase::GotoControlFlowGenerator::visit(nodeId, links);
	for (const auto &hook : mNodeHooks) {
		hook(nodeId);
	}
}

SemanticNode *PioneerStateMachineGenerator::copySynchronousFragment(
		SemanticNode *after
		, const qReal::Id &from
		, bool withLabel)
{
	NonZoneNode *oldTarget = dynamic_cast<NonZoneNode *>(mSemanticTree->findNodeFor(from));
	if (!oldTarget) {
		/// @todo: actually, why not?
		mErrorReporter.addError(tr("Can not close a loop on algorithmic block."));
		mErrorsOccured = true;
		return nullptr;
	}

	NonZoneNode *fragmentStartNode = withLabel
			? mSemanticTreeManager->produceLabeledNode(from)
			: dynamic_cast<NonZoneNode *>(mSemanticTree->produceNodeFor(from));

	if (!fragmentStartNode) {
		return nullptr;
	} else {
		mLabeledNodes << fragmentStartNode->id();
	}

	if (!dynamic_cast<NonZoneNode *>(after)) {
		mErrorReporter.addError(tr("Generation internal error, non-zone node is a start of a fragment."));
		mErrorsOccured = true;
		return nullptr;
	}

	// End-of-handler shall go before every labeled node, since label here is actually a start of a new handler.
	SemanticNode * const endNode = produceEndOfHandlerNode();
	mSemanticTreeManager->addAfter(after, endNode);

	mSemanticTreeManager->addAfter(endNode, fragmentStartNode);

	if (isAsynchronous(fragmentStartNode)) {
		// Synchronous fragment is trivial and its first node is asynchronous. Generating transition from it and we're
		// done here.
		//
		// Using oldTarget because fragmentStartNode was just added and does not have siblings, but it is a copy
		// of oldTarget.
		const auto rightSibling = mSemanticTreeManager->findRightSibling(oldTarget);
		if (rightSibling) {
			auto gotoNode = produceGotoNode(rightSibling->id());
			fragmentStartNode->appendSibling(gotoNode);
			return gotoNode;
		} else {
			mErrorReporter.addError(tr("Generation internal error, asynchronous fragment start node generation " \
					"failed."));
			mErrorsOccured = true;
		}

		return nullptr;
	}

	auto siblings = mSemanticTreeManager->copyRightSiblingsUntil(
			oldTarget
			, [this](SemanticNode * node){ return isAsynchronous(node); });

	if (siblings.isEmpty()) {
		mErrorReporter.addError(tr("Loop can not be closed on a block that is last in its structural construct."));
		mErrorsOccured = true;
		return nullptr;
	}

	if (isAsynchronous(siblings.last())) {
		// Synchronous fragment finished with asynchronous node
		fragmentStartNode->appendSiblings(siblings);

		// Now we shall look for the end of original fragment: find asynchronous node on which a fragment shall be
		// ending and get its target node. Assuming that they are already visited (here we assuming that it is a loop,
		// may not be the case when logical branching blocks will be introduced).
		auto asynchronousNode = mSemanticTreeManager->findSibling(
				oldTarget
				, [this](SemanticNode * node){ return isAsynchronous(node); });

		const auto asynchronousNodeTarget = mSemanticTreeManager->findRightSibling(asynchronousNode);
		if (!asynchronousNodeTarget) {
			mErrorReporter.addError(tr("Generation internal error, asynchronous node does not have target node."));
			mErrorsOccured = true;
			return nullptr;
		}

		auto gotoNode = produceGotoNode(asynchronousNodeTarget->id());
		fragmentStartNode->appendSibling(gotoNode);
		return gotoNode;
	} else {
		mErrorReporter.addError(tr("Purely synchronous loops are not supported yet."));
		mErrorsOccured = true;
	}

	return nullptr;
}

bool PioneerStateMachineGenerator::isAsynchronous(const SemanticNode * const node) const
{
	return mAsynchronousNodes.contains(node->id().element());
}

SemanticNode *PioneerStateMachineGenerator::produceEndOfHandlerNode()
{
	qReal::Id syntheticId = qReal::Id::createElementId("synthetic", "synthetic", "EndOfHandler");
	SimpleNode * const result = mSemanticTree->produceSimple(syntheticId);
	// No need for special handling, from the point of view of a generator it is just some simple node.
	result->bindToSyntheticConstruction(SimpleNode::noSytheticBinding);
	return result;
}
<commit_msg>Corrected error message<commit_after>/* Copyright 2017 QReal Research Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include "pioneerStateMachineGenerator.h"

#include <generatorBase/semanticTree/semanticNode.h>
#include <generatorBase/semanticTree/simpleNode.h>

using namespace pioneer::lua;
using namespace generatorBase;
using namespace generatorBase::semantics;

PioneerStateMachineGenerator::PioneerStateMachineGenerator(
		const qrRepo::RepoApi &repo
		, qReal::ErrorReporterInterface &errorReporter
		, generatorBase::GeneratorCustomizer &customizer
		, generatorBase::PrimaryControlFlowValidator &validator
		, const qReal::Id &diagramId
		, QObject *parent
		, bool isThisDiagramMain)
	: GotoControlFlowGenerator(repo, errorReporter, customizer, validator, diagramId, parent, isThisDiagramMain)
{
	mAsynchronousNodes << "GeoTakeoff" << "GeoLanding" << "GoToPoint";
}

void PioneerStateMachineGenerator::registerNodeHook(std::function<void(const qReal::Id)> hook)
{
	mNodeHooks.append(hook);
}

void PioneerStateMachineGenerator::performGeneration()
{
	mSemanticTreeManager.reset(new SemanticTreeManager(*mSemanticTree, mErrorReporter, mErrorsOccured));
	GotoControlFlowGenerator::performGeneration();
}

void PioneerStateMachineGenerator::visitRegular(const qReal::Id &id, const QList<LinkInfo> &links)
{
	// Base class method checks for subprogram calls, which is irrelevant for now, but does not hurt and hopefully
	// will be needed later.
	ControlFlowGeneratorBase::visitRegular(id, links);

	SimpleNode * const thisNode = static_cast<SimpleNode *>(mSemanticTree->findNodeFor(id));
	SemanticNode *nextNode = nullptr;
	const qReal::Id target = links[0].target;

	if (mAsynchronousNodes.contains(id.element())) {
		if (mSemanticTree->findNodeFor(target)) {
			// thisNode is asyncronous node that transfers control to already visited node.
			// Generated code for thisNode will initiate asynchronous action and all we need to do is to generate
			// transition to a state which will execute target block when this block finishes its asynchronous
			// operation.
			nextNode = produceGotoNode(target);
			mSemanticTreeManager->addAfter(thisNode, nextNode);

			if (!mLabeledNodes.contains(target)) {
				//
				// Target node, despite being already visited, does not have a label, it means that it is a part of a
				// synchronous fragment. We copy that fragment from this node to the first asyncronous node and
				// label a start of the copied fragment. At the end of the fragment we will generate said asynchronous
				// node, which will initiate asynchronous operation, and then transition to its next state, which will
				// continue execution when operation is done.
				//
				// It will confuse "findNodeFor(id)" (there will be many semantic nodes for a block with given id), but
				// we actually do not care which copy will be used later, since they are the same.
				//
				nextNode = copySynchronousFragment(nextNode, target, true);
			}

			if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
				SemanticNode * const endNode = produceEndOfHandlerNode();
				mSemanticTreeManager->addAfter(nextNode, endNode);
			}
		} else {
			// thisNode is asynchronous node that transfers control to a node that has not been visited yet. Generating
			// transition into a state associated with that node and then a new handler for target node itself.
			nextNode = mSemanticTreeManager->produceLabeledNode(target);
			if (!nextNode) {
				return;
			} else {
				mLabeledNodes << nextNode->id();
			}

			SemanticNode * const gotoNode = produceGotoNode(target);
			mSemanticTreeManager->addAfter(thisNode, gotoNode);

			// Labeled node can not be a part of a zone (i.e. "then" or "else" branch), it shall be generated in top
			// level zone.
			if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
				SemanticNode * const endNode = produceEndOfHandlerNode();
				mSemanticTreeManager->addAfter(gotoNode, endNode);
				mSemanticTreeManager->addAfter(endNode, nextNode);
			} else {
				// Getting parent node (i.e. If statement to the branch of which our node belongs).
				NonZoneNode *aParent = mSemanticTreeManager->parent(thisNode);
				while (!mSemanticTreeManager->isTopLevelNode(aParent)) {
					aParent = mSemanticTreeManager->parent(aParent);
				}

				// Skipping "end" that finishes handler with If.
				SemanticNode * const endOfHandler = mSemanticTreeManager->findSibling(
						aParent
						, [](SemanticNode *node){ return node->id().element() == "EndOfHandler"; });

				if (!endOfHandler) {
					mErrorReporter.addError(tr("Can not find end of an If statement, generation internal error or "
							"too complex algorithmic construction."));
					mErrorsOccured = true;
					return;
				}

				// Adding our labeled node denoting new handler after the end of a handler with If node.
				mSemanticTreeManager->addAfter(endOfHandler, nextNode);
			}
		}
	} else {
		if (!mSemanticTree->findNodeFor(target)) {
			// It is not an asynchronous node, generating as-is.
			nextNode = mSemanticTree->produceNodeFor(target);
			mSemanticTreeManager->addAfter(thisNode, nextNode);
		} else {
			// Synchronous node leading to already visited node. Need some copypasting of synchronous fragments,
			// or else we will stall the program waiting for an event that was never initiated.
			copySynchronousFragment(thisNode, target, false);
		}
	}
}

void PioneerStateMachineGenerator::visitConditional(const qReal::Id &id, const QList<LinkInfo> &links)
{
	Q_UNUSED(links)

	const QPair<LinkInfo, LinkInfo> branches(ifBranchesFor(id));
	const LinkInfo thenLink = branches.first;
	const LinkInfo elseLink = branches.second;

	IfNode * const thisNode = static_cast<IfNode *>(mSemanticTree->findNodeFor(id));

	mSemanticTreeManager->addToZone(thisNode->thenZone(), thenLink.target);
	mSemanticTreeManager->addToZone(thisNode->elseZone(), elseLink.target);

	if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
		SemanticNode * const endNode = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(thisNode, endNode);
	}
}

void PioneerStateMachineGenerator::visitFinal(const qReal::Id &id, const QList<LinkInfo> &links)
{
	generatorBase::GotoControlFlowGenerator::visitFinal(id, links);

	// Here we are going to add finishing end-of-handler node in case it is missing (for example, diagrams like
	// "Initial Node" -> "Final Node" will not generate it automatically).
	// It is a kind of hack because asynchronous handler shall be a first-class entity and a zone node.

	SimpleNode * const thisNode = static_cast<SimpleNode *>(mSemanticTree->findNodeFor(id));

	// Getting root node.
	NonZoneNode *aParent = mSemanticTreeManager->parent(thisNode);
	while (!mSemanticTreeManager->isTopLevelNode(aParent)) {
		aParent = mSemanticTreeManager->parent(aParent);
	}

	// Searching for end-of-handler node.
	SemanticNode * endOfHandler = mSemanticTreeManager->findSibling(
			aParent
			, [](SemanticNode *node){ return node->id().element() == "EndOfHandler"; });

	if (!endOfHandler) {
		// If not found, create and add one.
		endOfHandler = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(thisNode, endOfHandler);
	}
}

void PioneerStateMachineGenerator::visit(const qReal::Id &nodeId, QList<utils::DeepFirstSearcher::LinkInfo> &links)
{
	generatorBase::GotoControlFlowGenerator::visit(nodeId, links);
	for (const auto &hook : mNodeHooks) {
		hook(nodeId);
	}
}

SemanticNode *PioneerStateMachineGenerator::copySynchronousFragment(
		SemanticNode *after
		, const qReal::Id &from
		, bool withLabel)
{
	NonZoneNode *oldTarget = dynamic_cast<NonZoneNode *>(mSemanticTree->findNodeFor(from));
	if (!oldTarget) {
		/// @todo: actually, why not?
		mErrorReporter.addError(tr("Can not close a loop on algorithmic block."));
		mErrorsOccured = true;
		return nullptr;
	}

	NonZoneNode *fragmentStartNode = withLabel
			? mSemanticTreeManager->produceLabeledNode(from)
			: dynamic_cast<NonZoneNode *>(mSemanticTree->produceNodeFor(from));

	if (!fragmentStartNode) {
		return nullptr;
	} else {
		mLabeledNodes << fragmentStartNode->id();
	}

	if (!dynamic_cast<NonZoneNode *>(after)) {
		mErrorReporter.addError(tr("Generation internal error, non-zone node is a start of a fragment."));
		mErrorsOccured = true;
		return nullptr;
	}

	// End-of-handler shall go before every labeled node, since label here is actually a start of a new handler.
	SemanticNode * const endNode = produceEndOfHandlerNode();
	mSemanticTreeManager->addAfter(after, endNode);

	mSemanticTreeManager->addAfter(endNode, fragmentStartNode);

	if (isAsynchronous(fragmentStartNode)) {
		// Synchronous fragment is trivial and its first node is asynchronous. Generating transition from it and we're
		// done here.
		//
		// Using oldTarget because fragmentStartNode was just added and does not have siblings, but it is a copy
		// of oldTarget.
		const auto rightSibling = mSemanticTreeManager->findRightSibling(oldTarget);
		if (rightSibling) {
			auto gotoNode = produceGotoNode(rightSibling->id());
			fragmentStartNode->appendSibling(gotoNode);
			return gotoNode;
		} else {
			mErrorReporter.addError(tr("Generation internal error, asynchronous fragment start node generation " \
					"failed."));
			mErrorsOccured = true;
		}

		return nullptr;
	}

	auto siblings = mSemanticTreeManager->copyRightSiblingsUntil(
			oldTarget
			, [this](SemanticNode * node){ return isAsynchronous(node); });

	if (siblings.isEmpty()) {
		mErrorReporter.addError(tr("Loop can not be closed on a block that is last in its structural construct."));
		mErrorsOccured = true;
		return nullptr;
	}

	if (isAsynchronous(siblings.last())) {
		// Synchronous fragment finished with asynchronous node
		fragmentStartNode->appendSiblings(siblings);

		// Now we shall look for the end of original fragment: find asynchronous node on which a fragment shall be
		// ending and get its target node. Assuming that they are already visited (here we assuming that it is a loop,
		// may not be the case when logical branching blocks will be introduced).
		auto asynchronousNode = mSemanticTreeManager->findSibling(
				oldTarget
				, [this](SemanticNode * node){ return isAsynchronous(node); });

		const auto asynchronousNodeTarget = mSemanticTreeManager->findRightSibling(asynchronousNode);
		if (!asynchronousNodeTarget) {
			mErrorReporter.addError(tr("Generation internal error, asynchronous node does not have target node."));
			mErrorsOccured = true;
			return nullptr;
		}

		auto gotoNode = produceGotoNode(asynchronousNodeTarget->id());
		fragmentStartNode->appendSibling(gotoNode);
		return gotoNode;
	} else {
		mErrorReporter.addError(tr("Purely synchronous loops or If branches are not supported yet."));
		mErrorsOccured = true;
	}

	return nullptr;
}

bool PioneerStateMachineGenerator::isAsynchronous(const SemanticNode * const node) const
{
	return mAsynchronousNodes.contains(node->id().element());
}

SemanticNode *PioneerStateMachineGenerator::produceEndOfHandlerNode()
{
	qReal::Id syntheticId = qReal::Id::createElementId("synthetic", "synthetic", "EndOfHandler");
	SimpleNode * const result = mSemanticTree->produceSimple(syntheticId);
	// No need for special handling, from the point of view of a generator it is just some simple node.
	result->bindToSyntheticConstruction(SimpleNode::noSytheticBinding);
	return result;
}
<|endoftext|>


<commit_before>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#include "UrhoRenderer.h"
#include "GraphicsWorld.h"
#include "Framework.h"
#include "Placeable.h"
#include "Mesh.h"
#include "AnimationController.h"
#include "Camera.h"
#include "Light.h"
#include "EnvironmentLight.h"
#include "Terrain.h"
#include "Sky.h"
#include "ParticleSystem.h"
#include "Fog.h"
#include "WaterPlane.h"
#include "IComponentFactory.h"
#include "ConfigAPI.h"
#include "SceneAPI.h"
#include "AssetAPI.h"
#include "Entity.h"
#include "Scene/Scene.h"
#include "LoggingFunctions.h"
#include "JavaScript.h"
#include "JavaScriptInstance.h"
#include "UrhoRendererBindings/UrhoRendererBindings.h"

#include "TextureAsset.h"
#include "UrhoMeshAsset.h"
#include "Ogre/OgreMeshAsset.h"
#include "Ogre/OgreMaterialAsset.h"
#include "Ogre/OgreSkeletonAsset.h"
#include "Ogre/DefaultOgreMaterialProcessor.h"
#include "Ogre/OgreParticleAsset.h"
#include "GenericAssetFactory.h"

#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Core/ProcessUtils.h>
#include <Urho3D/Core/Profiler.h>
#include <Urho3D/Graphics/Camera.h>
#include <Urho3D/Graphics/Graphics.h>
#include <Urho3D/Graphics/GraphicsEvents.h>
#include <Urho3D/Graphics/Renderer.h>
#include <Urho3D/Graphics/Viewport.h>

using namespace JSBindings;

namespace Tundra
{

UrhoRenderer::UrhoRenderer(Framework* owner) :
    IModule("UrhoRenderer", owner)
{
    // Register default material convertor
    RegisterOgreMaterialProcessor(new DefaultOgreMaterialProcessor(GetContext()));
}

UrhoRenderer::~UrhoRenderer()
{
}

void UrhoRenderer::Load()
{
    SceneAPI* scene = framework->Scene();
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Placeable>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Mesh>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Camera>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Light>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<AnimationController>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<EnvironmentLight>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Terrain>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Sky>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<ParticleSystem>));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Fog>));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<WaterPlane>));

    /// \todo Check and add new supported texture extensions
    StringList textureExtensions;
    textureExtensions.Push(".crn"); // CRN to DDS decompression implemented in TextureAsset
    textureExtensions.Push(".dds");
    textureExtensions.Push(".png");
    textureExtensions.Push(".jpeg");
    textureExtensions.Push(".jpg");
    textureExtensions.Push(".gif");
    textureExtensions.Push(".bmp");
    textureExtensions.Push(".tga");
    textureExtensions.Push(".psd");

    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<UrhoMeshAsset>("UrhoMesh", ".mdl")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<OgreMeshAsset>("OgreMesh", ".mesh")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<OgreMaterialAsset>("OgreMaterial", ".material")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<OgreSkeletonAsset>("OgreSkeleton", ".skeleton")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<OgreParticleAsset>("OgreParticle", ".particle")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<TextureAsset>("Texture", textureExtensions)));
}

void UrhoRenderer::Initialize()
{
    framework->RegisterRenderer(this);

    // Connect to scene change signals.
    framework->Scene()->SceneCreated.Connect(this, &UrhoRenderer::CreateGraphicsWorld);
    framework->Scene()->SceneAboutToBeRemoved.Connect(this, &UrhoRenderer::RemoveGraphicsWorld);

    // Enable the main (full-screen) viewport
    Urho3D::Renderer* rend = GetSubsystem<Urho3D::Renderer>();
    if (rend)
    {
        rend->SetNumViewports(1);
        rend->SetViewport(0, new Urho3D::Viewport(context_));

        // Track window position and screen mode changes to keep config up-to-date
        SubscribeToEvent(Urho3D::E_WINDOWPOS, URHO3D_HANDLER(UrhoRenderer, HandleScreenModeChange));
        SubscribeToEvent(Urho3D::E_SCREENMODE, URHO3D_HANDLER(UrhoRenderer, HandleScreenModeChange));

        // Disable shadows completely for now on mobile devices, as the shadow bias is problematic, and it consumes GPU performance
        // Also disable specular highlights for per-pixel lighting
        if (Urho3D::GetPlatform() == "Android" || Urho3D::GetPlatform() == "iOS")
        {
            rend->SetDrawShadows(false);
            rend->SetSpecularLighting(false);
        }
    }

    // Connect to JavaScript module instance creation to be able to expose the renderer classes to each instance
    JavaScript* javaScript = framework->Module<JavaScript>();
    if (javaScript)
        javaScript->ScriptInstanceCreated.Connect(this, &UrhoRenderer::OnScriptInstanceCreated);
}

void UrhoRenderer::Uninitialize()
{
    framework->RegisterRenderer(0);
    Urho3D::Renderer* rend = GetSubsystem<Urho3D::Renderer>();
    // Let go of the viewport that we created. If done later at Urho Context destruction time, may cause a crash
    if (rend)
        rend->SetViewport(0, nullptr);
}

void UrhoRenderer::HandleScreenModeChange(StringHash /*eventType*/, VariantMap& /*eventData*/)
{
    ConfigAPI *config = framework->Config();
    if (!config)
        return;

    HashMap<String, Variant> data;

    Urho3D::Graphics* graphics = GetSubsystem<Urho3D::Graphics>();
    if (graphics)
    {
        data["window position"] = graphics->GetWindowPosition();
        data["window size"] = Urho3D::IntVector2(graphics->GetWidth(), graphics->GetHeight());
        data["window fullscreen"] = graphics->GetFullscreen();

        /* Store potentially frequent runtime changes in memory only.
        The changes will be written to disk latest at a clean Framework exit. */
        ConfigFile &f = config->GetFile(ConfigAPI::FILE_FRAMEWORK);
        f.Set(ConfigAPI::SECTION_GRAPHICS, data);
    }
}

Entity *UrhoRenderer::MainCamera()
{
    Entity *mainCameraEntity = activeMainCamera.Get();
    if (!mainCameraEntity)
        return nullptr;

    if (!mainCameraEntity->ParentScene() || !mainCameraEntity->Component<Camera>())
    {
        SetMainCamera(0);
        return nullptr;
    }
    return mainCameraEntity;
}

Camera *UrhoRenderer::MainCameraComponent()
{
    Entity *mainCamera = MainCamera();
    if (!mainCamera)
        return nullptr;
    return mainCamera->Component<Camera>().Get();
}

Scene *UrhoRenderer::MainCameraScene()
{
    Entity *mainCamera = MainCamera();
    Scene *scene = mainCamera ? mainCamera->ParentScene() : 0;
    if (scene)
        return scene;

    // If there is no active camera, return the first scene on the list.
    const SceneMap &scenes = framework->Scene()->Scenes();
    if (scenes.Size() > 0)
        return scenes.Begin()->second_.Get();

    return nullptr;
}

void UrhoRenderer::SetMainCamera(Entity *mainCameraEntity)
{
    activeMainCamera = mainCameraEntity;

    Urho3D::Camera *newActiveCamera = 0;
    Camera *cameraComponent = mainCameraEntity ? mainCameraEntity->Component<Camera>().Get() : 0;
    if (cameraComponent)
        newActiveCamera = cameraComponent->UrhoCamera();
    else
    {
        activeMainCamera.Reset();
        if (mainCameraEntity)
            LogWarning("Cannot activate camera '" + mainCameraEntity->Name() + "': It does not have a Camera component!");
    }
    if (mainCameraEntity && !mainCameraEntity->ParentScene()) // If the new to-be camera is not in a scene, don't add it as active.
    {
        LogWarning("Cannot activate camera \"" + mainCameraEntity->Name() + "\": It is not attached to a scene!");
        activeMainCamera.Reset();
        newActiveCamera = 0;
    }

    if (!activeMainCamera.Lock() || !newActiveCamera)
        LogWarning("Setting main window camera to null!");

    Urho3D::Renderer* rend = GetSubsystem<Urho3D::Renderer>();
    if (!rend)
        return; // In headless mode the renderer doesn't exist

    Urho3D::Viewport* vp = rend->GetViewport(0);
    if (vp)
    {
        vp->SetCamera(newActiveCamera);
        vp->SetScene(mainCameraEntity->ParentScene()->Subsystem<GraphicsWorld>()->UrhoScene());
    }
    else
        LogWarning("Could not set active camera, no viewport defined");

    MainCameraChanged.Emit(mainCameraEntity);
}

int UrhoRenderer::WindowWidth() const
{
    Urho3D::Graphics* gfx = GetSubsystem<Urho3D::Graphics>();
    return gfx ? gfx->GetWidth() : 0;
}

int UrhoRenderer::WindowHeight() const
{
    Urho3D::Graphics* gfx = GetSubsystem<Urho3D::Graphics>();
    return gfx ? gfx->GetHeight() : 0;
}

void UrhoRenderer::RegisterOgreMaterialProcessor(IOgreMaterialProcessor* processor, bool addFirst)
{
    if (addFirst)
        materialProcessors.Insert(0, SharedPtr<IOgreMaterialProcessor>(processor));
    else
        materialProcessors.Push(SharedPtr<IOgreMaterialProcessor>(processor));
}

void UrhoRenderer::UnregisterOgreMaterialProcessor(IOgreMaterialProcessor* processor)
{
    for (Vector<SharedPtr<IOgreMaterialProcessor> >::Iterator i = materialProcessors.Begin(); i != materialProcessors.End(); ++i)
    {
        if (i->Get() == processor)
        {
            materialProcessors.Erase(i);
            return;
        }
    }

    LogWarning("Could not find Ogre material processor to remove");
}

IOgreMaterialProcessor* UrhoRenderer::FindOgreMaterialProcessor(const Ogre::MaterialParser& material) const
{
    for (Vector<SharedPtr<IOgreMaterialProcessor> >::ConstIterator i = materialProcessors.Begin(); i != materialProcessors.End(); ++i)
    {
        if ((*i)->CanConvert(material))
            return *i;
    }

    return nullptr;
}

void UrhoRenderer::CreateGraphicsWorld(Scene *scene, AttributeChange::Type)
{
    // Add an GraphicsWorld to the scene
    if (scene->ViewEnabled())
        scene->AddSubsystem("graphics", new GraphicsWorld(this, scene));
}

void UrhoRenderer::RemoveGraphicsWorld(Scene *scene, AttributeChange::Type)
{
    scene->RemoveSubsystem("graphics");
}

void UrhoRenderer::OnScriptInstanceCreated(JavaScriptInstance* instance)
{
    URHO3D_PROFILE(ExposeUrhoRendererClasses);

    duk_context* ctx = instance->Context();
    ExposeUrhoRendererClasses(ctx);
}

}

extern "C"
{

DLLEXPORT void TundraPluginMain(Tundra::Framework *fw)
{
    fw->RegisterModule(new Tundra::UrhoRenderer(fw));
}

}
<commit_msg>Register UrhoRenderer object as the "renderer" global property.<commit_after>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#include "UrhoRenderer.h"
#include "GraphicsWorld.h"
#include "Framework.h"
#include "Placeable.h"
#include "Mesh.h"
#include "AnimationController.h"
#include "Camera.h"
#include "Light.h"
#include "EnvironmentLight.h"
#include "Terrain.h"
#include "Sky.h"
#include "ParticleSystem.h"
#include "Fog.h"
#include "WaterPlane.h"
#include "IComponentFactory.h"
#include "ConfigAPI.h"
#include "SceneAPI.h"
#include "AssetAPI.h"
#include "Entity.h"
#include "Scene/Scene.h"
#include "LoggingFunctions.h"
#include "JavaScript.h"
#include "JavaScriptInstance.h"
#include "UrhoRendererBindings/UrhoRendererBindings.h"

#include "TextureAsset.h"
#include "UrhoMeshAsset.h"
#include "Ogre/OgreMeshAsset.h"
#include "Ogre/OgreMaterialAsset.h"
#include "Ogre/OgreSkeletonAsset.h"
#include "Ogre/DefaultOgreMaterialProcessor.h"
#include "Ogre/OgreParticleAsset.h"
#include "GenericAssetFactory.h"

#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Core/ProcessUtils.h>
#include <Urho3D/Core/Profiler.h>
#include <Urho3D/Graphics/Camera.h>
#include <Urho3D/Graphics/Graphics.h>
#include <Urho3D/Graphics/GraphicsEvents.h>
#include <Urho3D/Graphics/Renderer.h>
#include <Urho3D/Graphics/Viewport.h>

using namespace JSBindings;

namespace Tundra
{

UrhoRenderer::UrhoRenderer(Framework* owner) :
    IModule("UrhoRenderer", owner)
{
    // Register default material convertor
    RegisterOgreMaterialProcessor(new DefaultOgreMaterialProcessor(GetContext()));
}

UrhoRenderer::~UrhoRenderer()
{
}

void UrhoRenderer::Load()
{
    SceneAPI* scene = framework->Scene();
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Placeable>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Mesh>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Camera>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Light>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<AnimationController>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<EnvironmentLight>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Terrain>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Sky>()));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<ParticleSystem>));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<Fog>));
    scene->RegisterComponentFactory(ComponentFactoryPtr(new GenericComponentFactory<WaterPlane>));

    /// \todo Check and add new supported texture extensions
    StringList textureExtensions;
    textureExtensions.Push(".crn"); // CRN to DDS decompression implemented in TextureAsset
    textureExtensions.Push(".dds");
    textureExtensions.Push(".png");
    textureExtensions.Push(".jpeg");
    textureExtensions.Push(".jpg");
    textureExtensions.Push(".gif");
    textureExtensions.Push(".bmp");
    textureExtensions.Push(".tga");
    textureExtensions.Push(".psd");

    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<UrhoMeshAsset>("UrhoMesh", ".mdl")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<OgreMeshAsset>("OgreMesh", ".mesh")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<OgreMaterialAsset>("OgreMaterial", ".material")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<OgreSkeletonAsset>("OgreSkeleton", ".skeleton")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<OgreParticleAsset>("OgreParticle", ".particle")));
    framework->Asset()->RegisterAssetTypeFactory(AssetTypeFactoryPtr(new GenericAssetFactory<TextureAsset>("Texture", textureExtensions)));
}

void UrhoRenderer::Initialize()
{
    framework->RegisterRenderer(this);

    // Connect to scene change signals.
    framework->Scene()->SceneCreated.Connect(this, &UrhoRenderer::CreateGraphicsWorld);
    framework->Scene()->SceneAboutToBeRemoved.Connect(this, &UrhoRenderer::RemoveGraphicsWorld);

    // Enable the main (full-screen) viewport
    Urho3D::Renderer* rend = GetSubsystem<Urho3D::Renderer>();
    if (rend)
    {
        rend->SetNumViewports(1);
        rend->SetViewport(0, new Urho3D::Viewport(context_));

        // Track window position and screen mode changes to keep config up-to-date
        SubscribeToEvent(Urho3D::E_WINDOWPOS, URHO3D_HANDLER(UrhoRenderer, HandleScreenModeChange));
        SubscribeToEvent(Urho3D::E_SCREENMODE, URHO3D_HANDLER(UrhoRenderer, HandleScreenModeChange));

        // Disable shadows completely for now on mobile devices, as the shadow bias is problematic, and it consumes GPU performance
        // Also disable specular highlights for per-pixel lighting
        if (Urho3D::GetPlatform() == "Android" || Urho3D::GetPlatform() == "iOS")
        {
            rend->SetDrawShadows(false);
            rend->SetSpecularLighting(false);
        }
    }

    // Connect to JavaScript module instance creation to be able to expose the renderer classes to each instance
    JavaScript* javaScript = framework->Module<JavaScript>();
    if (javaScript)
        javaScript->ScriptInstanceCreated.Connect(this, &UrhoRenderer::OnScriptInstanceCreated);
}

void UrhoRenderer::Uninitialize()
{
    framework->RegisterRenderer(0);
    Urho3D::Renderer* rend = GetSubsystem<Urho3D::Renderer>();
    // Let go of the viewport that we created. If done later at Urho Context destruction time, may cause a crash
    if (rend)
        rend->SetViewport(0, nullptr);
}

void UrhoRenderer::HandleScreenModeChange(StringHash /*eventType*/, VariantMap& /*eventData*/)
{
    ConfigAPI *config = framework->Config();
    if (!config)
        return;

    HashMap<String, Variant> data;

    Urho3D::Graphics* graphics = GetSubsystem<Urho3D::Graphics>();
    if (graphics)
    {
        data["window position"] = graphics->GetWindowPosition();
        data["window size"] = Urho3D::IntVector2(graphics->GetWidth(), graphics->GetHeight());
        data["window fullscreen"] = graphics->GetFullscreen();

        /* Store potentially frequent runtime changes in memory only.
        The changes will be written to disk latest at a clean Framework exit. */
        ConfigFile &f = config->GetFile(ConfigAPI::FILE_FRAMEWORK);
        f.Set(ConfigAPI::SECTION_GRAPHICS, data);
    }
}

Entity *UrhoRenderer::MainCamera()
{
    Entity *mainCameraEntity = activeMainCamera.Get();
    if (!mainCameraEntity)
        return nullptr;

    if (!mainCameraEntity->ParentScene() || !mainCameraEntity->Component<Camera>())
    {
        SetMainCamera(0);
        return nullptr;
    }
    return mainCameraEntity;
}

Camera *UrhoRenderer::MainCameraComponent()
{
    Entity *mainCamera = MainCamera();
    if (!mainCamera)
        return nullptr;
    return mainCamera->Component<Camera>().Get();
}

Scene *UrhoRenderer::MainCameraScene()
{
    Entity *mainCamera = MainCamera();
    Scene *scene = mainCamera ? mainCamera->ParentScene() : 0;
    if (scene)
        return scene;

    // If there is no active camera, return the first scene on the list.
    const SceneMap &scenes = framework->Scene()->Scenes();
    if (scenes.Size() > 0)
        return scenes.Begin()->second_.Get();

    return nullptr;
}

void UrhoRenderer::SetMainCamera(Entity *mainCameraEntity)
{
    activeMainCamera = mainCameraEntity;

    Urho3D::Camera *newActiveCamera = 0;
    Camera *cameraComponent = mainCameraEntity ? mainCameraEntity->Component<Camera>().Get() : 0;
    if (cameraComponent)
        newActiveCamera = cameraComponent->UrhoCamera();
    else
    {
        activeMainCamera.Reset();
        if (mainCameraEntity)
            LogWarning("Cannot activate camera '" + mainCameraEntity->Name() + "': It does not have a Camera component!");
    }
    if (mainCameraEntity && !mainCameraEntity->ParentScene()) // If the new to-be camera is not in a scene, don't add it as active.
    {
        LogWarning("Cannot activate camera \"" + mainCameraEntity->Name() + "\": It is not attached to a scene!");
        activeMainCamera.Reset();
        newActiveCamera = 0;
    }

    if (!activeMainCamera.Lock() || !newActiveCamera)
        LogWarning("Setting main window camera to null!");

    Urho3D::Renderer* rend = GetSubsystem<Urho3D::Renderer>();
    if (!rend)
        return; // In headless mode the renderer doesn't exist

    Urho3D::Viewport* vp = rend->GetViewport(0);
    if (vp)
    {
        vp->SetCamera(newActiveCamera);
        vp->SetScene(mainCameraEntity->ParentScene()->Subsystem<GraphicsWorld>()->UrhoScene());
    }
    else
        LogWarning("Could not set active camera, no viewport defined");

    MainCameraChanged.Emit(mainCameraEntity);
}

int UrhoRenderer::WindowWidth() const
{
    Urho3D::Graphics* gfx = GetSubsystem<Urho3D::Graphics>();
    return gfx ? gfx->GetWidth() : 0;
}

int UrhoRenderer::WindowHeight() const
{
    Urho3D::Graphics* gfx = GetSubsystem<Urho3D::Graphics>();
    return gfx ? gfx->GetHeight() : 0;
}

void UrhoRenderer::RegisterOgreMaterialProcessor(IOgreMaterialProcessor* processor, bool addFirst)
{
    if (addFirst)
        materialProcessors.Insert(0, SharedPtr<IOgreMaterialProcessor>(processor));
    else
        materialProcessors.Push(SharedPtr<IOgreMaterialProcessor>(processor));
}

void UrhoRenderer::UnregisterOgreMaterialProcessor(IOgreMaterialProcessor* processor)
{
    for (Vector<SharedPtr<IOgreMaterialProcessor> >::Iterator i = materialProcessors.Begin(); i != materialProcessors.End(); ++i)
    {
        if (i->Get() == processor)
        {
            materialProcessors.Erase(i);
            return;
        }
    }

    LogWarning("Could not find Ogre material processor to remove");
}

IOgreMaterialProcessor* UrhoRenderer::FindOgreMaterialProcessor(const Ogre::MaterialParser& material) const
{
    for (Vector<SharedPtr<IOgreMaterialProcessor> >::ConstIterator i = materialProcessors.Begin(); i != materialProcessors.End(); ++i)
    {
        if ((*i)->CanConvert(material))
            return *i;
    }

    return nullptr;
}

void UrhoRenderer::CreateGraphicsWorld(Scene *scene, AttributeChange::Type)
{
    // Add an GraphicsWorld to the scene
    if (scene->ViewEnabled())
        scene->AddSubsystem("graphics", new GraphicsWorld(this, scene));
}

void UrhoRenderer::RemoveGraphicsWorld(Scene *scene, AttributeChange::Type)
{
    scene->RemoveSubsystem("graphics");
}

void UrhoRenderer::OnScriptInstanceCreated(JavaScriptInstance* instance)
{
    URHO3D_PROFILE(ExposeUrhoRendererClasses);

    duk_context* ctx = instance->Context();
    ExposeUrhoRendererClasses(ctx);
    instance->RegisterService("renderer", this);
}

}

extern "C"
{

DLLEXPORT void TundraPluginMain(Tundra::Framework *fw)
{
    fw->RegisterModule(new Tundra::UrhoRenderer(fw));
}

}
<|endoftext|>


<commit_before>// @(#)root/cont:$Name:  $:$Id: TProcessID.cxx,v 1.17 2002/08/12 06:20:13 brun Exp $
// Author: Rene Brun   28/09/2001

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//
// TProcessID
//
// A TProcessID identifies a ROOT job in a unique way in time and space.
// The TProcessID title consists of a TUUID object which provides a globally
// unique identifier (for more see TUUID.h).
//
// A TProcessID is automatically created by the TROOT constructor.
// When a TFile contains referenced objects (see TRef), the TProcessID
// object is written to the file.
// If a file has been written in multiple sessions (same machine or not),
// a TProcessID is written for each session.
// These objects are used by the class TRef to uniquely identified
// any TObject pointed by a TRef.
//
// When a referenced object is read from a file (its bit kIsReferenced is set),
// this object is entered into the objects table of the corresponding TProcessID.
// Each TFile has a list of TProcessIDs (see TFile::fProcessIDs) also
// accessible via TProcessID::fgPIDs (for all files).
// When this object is deleted, it is removed from the table via the cleanup
// mechanism invoked by the TObject destructor.
//
// Each TProcessID has a table (TObjArray *fObjects) that keeps track
// of all referenced objects. If a referenced object has a fUniqueID set,
// a pointer to this unique object may be found via fObjects->At(fUniqueID).
// In the same way, when a TRef::GetObject is called, GetObject uses
// its own fUniqueID to find the pointer to the referenced object.
// See TProcessID::GetObjectWithID and PutObjectWithID.
//
// When a referenced object is deleted, its slot in fObjects is set to null.
//
// See also TProcessUUID: a specialized TProcessID to manage the single list
// of TUUIDs.
//
//////////////////////////////////////////////////////////////////////////

#include "TProcessID.h"
#include "TROOT.h"
#include "TFile.h"
#include "TObjArray.h"

TObjArray  *TProcessID::fgPIDs   = 0; //pointer to the list of TProcessID
TProcessID *TProcessID::fgPID    = 0; //pointer to the TProcessID of the current session
UInt_t      TProcessID::fgNumber = 0; //Current referenced object instance count

ClassImp(TProcessID)

//______________________________________________________________________________
TProcessID::TProcessID()
{
   fCount = 0;
   fObjects = 0;
}

//______________________________________________________________________________
TProcessID::~TProcessID()
{

   delete fObjects;
   fObjects = 0;
   fgPIDs->Remove(this);
}

//______________________________________________________________________________
TProcessID::TProcessID(const TProcessID &ref) : TNamed(ref)
{
   // TProcessID copy ctor.
}

//______________________________________________________________________________
TProcessID *TProcessID::AddProcessID()
{
// static function to add a new TProcessID to the list of PIDs

   TProcessID *pid = new TProcessID();

   if (!fgPIDs) {
      fgPID  = pid;
      fgPIDs = new TObjArray(10);
      gROOT->GetListOfCleanups()->Add(fgPIDs);
   }
   UShort_t apid = fgPIDs->GetEntriesFast();
   pid->IncrementCount();

   fgPIDs->Add(pid);
   char name[20];
   sprintf(name,"ProcessID%d",apid);
   pid->SetName(name);
   TUUID u;
   apid = fgPIDs->GetEntriesFast();
   pid->SetTitle(u.AsString());
   return pid;
}

//______________________________________________________________________________
UInt_t TProcessID::AssignID(TObject *obj)
{
// static function returning the ID assigned to obj
// If the object is not yet referenced, its kIsReferenced bit is set
// and its fUniqueID set to the current number of referenced objects so far.

   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == fgPID->GetObjectWithID(uid)) return uid;
   if (obj->TestBit(kIsReferenced)) {
      fgPID->PutObjectWithID(obj,uid);
      return uid;
   }
   fgNumber++;
   obj->SetBit(kIsReferenced);
   uid = fgNumber;
   obj->SetUniqueID(uid);
   fgPID->PutObjectWithID(obj,uid);
   return uid;
}

//______________________________________________________________________________
void TProcessID::Cleanup()
{
   // static function (called by TROOT destructor) to delete all TProcessIDs

   fgPIDs->Delete();
   gROOT->GetListOfCleanups()->Remove(fgPIDs);
   delete fgPIDs;
}

//______________________________________________________________________________
Int_t TProcessID::DecrementCount()
{

   // the reference fCount is used to delete the TProcessID
   // in the TFile destructor when fCount = 0

   fCount--;
   if (fCount < 0) fCount = 0;
   return fCount;
}

//______________________________________________________________________________
TProcessID *TProcessID::GetProcessID(UShort_t pid)
{
// static function returning a pointer to TProcessID number pid in fgPIDs

   return (TProcessID*)fgPIDs->At(pid);
}

//______________________________________________________________________________
TProcessID *TProcessID::GetProcessWithUID(UInt_t uid)
{
// static function returning a pointer to TProcessID with its pid
// encoded in the highest byte of uid

   Int_t pid = (uid>>24)&0xff;
   return (TProcessID*)fgPIDs->At(pid);
}

//______________________________________________________________________________
TProcessID *TProcessID::GetSessionProcessID()
{
// static function returning the pointer to the session TProcessID

   return fgPID;
}

//______________________________________________________________________________
Int_t TProcessID::IncrementCount()
{

   if (!fObjects) fObjects = new TObjArray(100);
   fCount++;
   return fCount;
}

//______________________________________________________________________________
UInt_t TProcessID::GetObjectCount()
{
// Return the current referenced object count
// fgNumber is incremented everytime a new object is referenced

   return fgNumber;
}

//______________________________________________________________________________
TObject *TProcessID::GetObjectWithID(UInt_t uidd)
{
   //returns the TObject with unique identifier uid in the table of objects
   //if (!fObjects) fObjects = new TObjArray(100);

   Int_t uid = uidd & 0xffffff;  //take only the 24 lower bits

   if (uid >= fObjects->GetSize()) return 0;
   return fObjects->UncheckedAt(uid);
}

//______________________________________________________________________________
void TProcessID::PutObjectWithID(TObject *obj, UInt_t uid)
{

   //stores the object at the uid th slot in the table of objects
   //The object uniqueid is set as well as its kMustCleanup bit
   //if (!fObjects) fObjects = new TObjArray(100);
   if (uid == 0) uid = obj->GetUniqueID() & 0xffffff;
   fObjects->AddAtAndExpand(obj,uid);
   obj->SetBit(kMustCleanup);
}

//______________________________________________________________________________
TProcessID  *TProcessID::ReadProcessID(UShort_t pidf, TFile *file)
{
// static function

   //The TProcessID with number pidf is read from file.
   //If the object is not already entered in the gROOT list, it is added.

   if (!file) return 0;
   TObjArray *pids = file->GetListOfProcessIDs();
   TProcessID *pid = 0;
   if (pidf < pids->GetSize()) pid = (TProcessID *)pids->UncheckedAt(pidf);
   if (pid) return pid;

   //check if fProcessIDs[uid] is set in file
   //if not set, read the process uid from file
   char pidname[32];
   sprintf(pidname,"ProcessID%d",pidf);
   TDirectory *dirsav = gDirectory;
   file->cd();
   pid = (TProcessID *)file->Get(pidname);
   if (dirsav) dirsav->cd();
   if (gDebug > 0) {
      printf("ReadProcessID, name=%s, file=%s, pid=%lx\n",pidname,file->GetName(),(Long_t)pid);
   }
   if (!pid) {
      //file->Error("ReadProcessID","Cannot find %s in file %s",pidname,file->GetName());
      return 0;
   }
      //check that a similar pid is not already registered in fgPIDs
   TIter next(fgPIDs);
   TProcessID *p;
   while ((p = (TProcessID*)next())) {
      if (!strcmp(p->GetTitle(),pid->GetTitle())) {
         delete pid;
         pids->AddAtAndExpand(p,pidf);
         p->IncrementCount();
         return p;
      }
   }
   pids->AddAtAndExpand(pid,pidf);
   pid->IncrementCount();
   fgPIDs->Add(pid);
   Int_t ind = fgPIDs->IndexOf(pid);
   pid->SetUniqueID((UInt_t)ind);
   return pid;
}

//______________________________________________________________________________
void TProcessID::RecursiveRemove(TObject *obj)
{
   // called by the object destructor
   // remove reference to obj from the current table if it is referenced

   if (!obj->TestBit(kIsReferenced)) return;
   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == GetObjectWithID(uid)) fObjects->RemoveAt(uid);
}

//______________________________________________________________________________
void TProcessID::SetObjectCount(UInt_t number)
{
// static function to set the current referenced object count
// fgNumber is incremented everytime a new object is referenced

   fgNumber = number;
}

//______________________________________________________________________________
UShort_t TProcessID::WriteProcessID(TProcessID *pidd, TFile *file)
{
// static function
// Check if the ProcessID pid is already in the file.
// if not, add it and return the index  number in the local file list

   if (!file) return 0;
   TProcessID *pid = pidd;
   if (!pid) pid = fgPID;
   TObjArray *pids = file->GetListOfProcessIDs();
   Int_t npids = file->GetNProcessIDs();
   for (Int_t i=0;i<npids;i++) {
      if (pids->At(i) == pid) return (UShort_t)i;
   }

   TDirectory *dirsav = gDirectory;
   file->cd();
   file->SetBit(TFile::kHasReferences);
   pids->Add(pid);
   pid->IncrementCount();
   char name[32];
   sprintf(name,"ProcessID%d",npids);
   pid->Write(name);
   file->IncrementProcessIDs();
   if (gDebug > 0) {
      printf("WriteProcessID, name=%s, file=%s\n",name,file->GetName());
   }
   if (dirsav) dirsav->cd();
   return (UShort_t)npids;
}
<commit_msg>Fix from Bill Tanenbaum in TProcessID::WriteProcessID.   In TProcessID::WriteProcessID(TProcessID*, TFile *), the statement      pids->Add(pid);   must be replaced by      pids->AddAtAndExpand(pid,npids);<commit_after>// @(#)root/cont:$Name:  $:$Id: TProcessID.cxx,v 1.18 2002/10/31 21:38:36 brun Exp $
// Author: Rene Brun   28/09/2001

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//
// TProcessID
//
// A TProcessID identifies a ROOT job in a unique way in time and space.
// The TProcessID title consists of a TUUID object which provides a globally
// unique identifier (for more see TUUID.h).
//
// A TProcessID is automatically created by the TROOT constructor.
// When a TFile contains referenced objects (see TRef), the TProcessID
// object is written to the file.
// If a file has been written in multiple sessions (same machine or not),
// a TProcessID is written for each session.
// These objects are used by the class TRef to uniquely identified
// any TObject pointed by a TRef.
//
// When a referenced object is read from a file (its bit kIsReferenced is set),
// this object is entered into the objects table of the corresponding TProcessID.
// Each TFile has a list of TProcessIDs (see TFile::fProcessIDs) also
// accessible via TProcessID::fgPIDs (for all files).
// When this object is deleted, it is removed from the table via the cleanup
// mechanism invoked by the TObject destructor.
//
// Each TProcessID has a table (TObjArray *fObjects) that keeps track
// of all referenced objects. If a referenced object has a fUniqueID set,
// a pointer to this unique object may be found via fObjects->At(fUniqueID).
// In the same way, when a TRef::GetObject is called, GetObject uses
// its own fUniqueID to find the pointer to the referenced object.
// See TProcessID::GetObjectWithID and PutObjectWithID.
//
// When a referenced object is deleted, its slot in fObjects is set to null.
//
// See also TProcessUUID: a specialized TProcessID to manage the single list
// of TUUIDs.
//
//////////////////////////////////////////////////////////////////////////

#include "TProcessID.h"
#include "TROOT.h"
#include "TFile.h"
#include "TObjArray.h"

TObjArray  *TProcessID::fgPIDs   = 0; //pointer to the list of TProcessID
TProcessID *TProcessID::fgPID    = 0; //pointer to the TProcessID of the current session
UInt_t      TProcessID::fgNumber = 0; //Current referenced object instance count

ClassImp(TProcessID)

//______________________________________________________________________________
TProcessID::TProcessID()
{
   fCount = 0;
   fObjects = 0;
}

//______________________________________________________________________________
TProcessID::~TProcessID()
{

   delete fObjects;
   fObjects = 0;
   fgPIDs->Remove(this);
}

//______________________________________________________________________________
TProcessID::TProcessID(const TProcessID &ref) : TNamed(ref)
{
   // TProcessID copy ctor.
}

//______________________________________________________________________________
TProcessID *TProcessID::AddProcessID()
{
// static function to add a new TProcessID to the list of PIDs

   TProcessID *pid = new TProcessID();

   if (!fgPIDs) {
      fgPID  = pid;
      fgPIDs = new TObjArray(10);
      gROOT->GetListOfCleanups()->Add(fgPIDs);
   }
   UShort_t apid = fgPIDs->GetEntriesFast();
   pid->IncrementCount();

   fgPIDs->Add(pid);
   char name[20];
   sprintf(name,"ProcessID%d",apid);
   pid->SetName(name);
   TUUID u;
   apid = fgPIDs->GetEntriesFast();
   pid->SetTitle(u.AsString());
   return pid;
}

//______________________________________________________________________________
UInt_t TProcessID::AssignID(TObject *obj)
{
// static function returning the ID assigned to obj
// If the object is not yet referenced, its kIsReferenced bit is set
// and its fUniqueID set to the current number of referenced objects so far.

   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == fgPID->GetObjectWithID(uid)) return uid;
   if (obj->TestBit(kIsReferenced)) {
      fgPID->PutObjectWithID(obj,uid);
      return uid;
   }
   fgNumber++;
   obj->SetBit(kIsReferenced);
   uid = fgNumber;
   obj->SetUniqueID(uid);
   fgPID->PutObjectWithID(obj,uid);
   return uid;
}

//______________________________________________________________________________
void TProcessID::Cleanup()
{
   // static function (called by TROOT destructor) to delete all TProcessIDs

   fgPIDs->Delete();
   gROOT->GetListOfCleanups()->Remove(fgPIDs);
   delete fgPIDs;
}

//______________________________________________________________________________
Int_t TProcessID::DecrementCount()
{

   // the reference fCount is used to delete the TProcessID
   // in the TFile destructor when fCount = 0

   fCount--;
   if (fCount < 0) fCount = 0;
   return fCount;
}

//______________________________________________________________________________
TProcessID *TProcessID::GetProcessID(UShort_t pid)
{
// static function returning a pointer to TProcessID number pid in fgPIDs

   return (TProcessID*)fgPIDs->At(pid);
}

//______________________________________________________________________________
TProcessID *TProcessID::GetProcessWithUID(UInt_t uid)
{
// static function returning a pointer to TProcessID with its pid
// encoded in the highest byte of uid

   Int_t pid = (uid>>24)&0xff;
   return (TProcessID*)fgPIDs->At(pid);
}

//______________________________________________________________________________
TProcessID *TProcessID::GetSessionProcessID()
{
// static function returning the pointer to the session TProcessID

   return fgPID;
}

//______________________________________________________________________________
Int_t TProcessID::IncrementCount()
{

   if (!fObjects) fObjects = new TObjArray(100);
   fCount++;
   return fCount;
}

//______________________________________________________________________________
UInt_t TProcessID::GetObjectCount()
{
// Return the current referenced object count
// fgNumber is incremented everytime a new object is referenced

   return fgNumber;
}

//______________________________________________________________________________
TObject *TProcessID::GetObjectWithID(UInt_t uidd)
{
   //returns the TObject with unique identifier uid in the table of objects
   //if (!fObjects) fObjects = new TObjArray(100);

   Int_t uid = uidd & 0xffffff;  //take only the 24 lower bits

   if (uid >= fObjects->GetSize()) return 0;
   return fObjects->UncheckedAt(uid);
}

//______________________________________________________________________________
void TProcessID::PutObjectWithID(TObject *obj, UInt_t uid)
{

   //stores the object at the uid th slot in the table of objects
   //The object uniqueid is set as well as its kMustCleanup bit
   //if (!fObjects) fObjects = new TObjArray(100);
   if (uid == 0) uid = obj->GetUniqueID() & 0xffffff;
   fObjects->AddAtAndExpand(obj,uid);
   obj->SetBit(kMustCleanup);
}

//______________________________________________________________________________
TProcessID  *TProcessID::ReadProcessID(UShort_t pidf, TFile *file)
{
// static function

   //The TProcessID with number pidf is read from file.
   //If the object is not already entered in the gROOT list, it is added.

   if (!file) return 0;
   TObjArray *pids = file->GetListOfProcessIDs();
   TProcessID *pid = 0;
   if (pidf < pids->GetSize()) pid = (TProcessID *)pids->UncheckedAt(pidf);
   if (pid) return pid;

   //check if fProcessIDs[uid] is set in file
   //if not set, read the process uid from file
   char pidname[32];
   sprintf(pidname,"ProcessID%d",pidf);
   TDirectory *dirsav = gDirectory;
   file->cd();
   pid = (TProcessID *)file->Get(pidname);
   if (dirsav) dirsav->cd();
   if (gDebug > 0) {
      printf("ReadProcessID, name=%s, file=%s, pid=%lx\n",pidname,file->GetName(),(Long_t)pid);
   }
   if (!pid) {
      //file->Error("ReadProcessID","Cannot find %s in file %s",pidname,file->GetName());
      return 0;
   }
      //check that a similar pid is not already registered in fgPIDs
   TIter next(fgPIDs);
   TProcessID *p;
   while ((p = (TProcessID*)next())) {
      if (!strcmp(p->GetTitle(),pid->GetTitle())) {
         delete pid;
         pids->AddAtAndExpand(p,pidf);
         p->IncrementCount();
         return p;
      }
   }
   pids->AddAtAndExpand(pid,pidf);
   pid->IncrementCount();
   fgPIDs->Add(pid);
   Int_t ind = fgPIDs->IndexOf(pid);
   pid->SetUniqueID((UInt_t)ind);
   return pid;
}

//______________________________________________________________________________
void TProcessID::RecursiveRemove(TObject *obj)
{
   // called by the object destructor
   // remove reference to obj from the current table if it is referenced

   if (!obj->TestBit(kIsReferenced)) return;
   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == GetObjectWithID(uid)) fObjects->RemoveAt(uid);
}

//______________________________________________________________________________
void TProcessID::SetObjectCount(UInt_t number)
{
// static function to set the current referenced object count
// fgNumber is incremented everytime a new object is referenced

   fgNumber = number;
}

//______________________________________________________________________________
UShort_t TProcessID::WriteProcessID(TProcessID *pidd, TFile *file)
{
// static function
// Check if the ProcessID pid is already in the file.
// if not, add it and return the index  number in the local file list

   if (!file) return 0;
   TProcessID *pid = pidd;
   if (!pid) pid = fgPID;
   TObjArray *pids = file->GetListOfProcessIDs();
   Int_t npids = file->GetNProcessIDs();
   for (Int_t i=0;i<npids;i++) {
      if (pids->At(i) == pid) return (UShort_t)i;
   }

   TDirectory *dirsav = gDirectory;
   file->cd();
   file->SetBit(TFile::kHasReferences);
   pids->AddAtAndExpand(pid,npids);
   pid->IncrementCount();
   char name[32];
   sprintf(name,"ProcessID%d",npids);
   pid->Write(name);
   file->IncrementProcessIDs();
   if (gDebug > 0) {
      printf("WriteProcessID, name=%s, file=%s\n",name,file->GetName());
   }
   if (dirsav) dirsav->cd();
   return (UShort_t)npids;
}
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once
#ifndef DASHBOARD_COMPONENTS_LOGGER_HPP
#define DASHBOARD_COMPONENTS_LOGGER_HPP

#include "../component.hpp"

#include <logger/logger.hpp>

namespace dashboard {

class Logger : public Component {

public:

  Logger(::Logger& logger, size_t entries = 20)
   : logger_{logger}, entries_{entries}
  {}

  std::string key() const override
  { return "logger"; }

  void serialize(Writer& writer) const override {
    writer.StartArray();
    auto entries = logger_.entries(entries_);

    auto it = entries.begin();
    // Temporary hack to only send N latest
    const size_t N = 50;
    if(entries.size() > N)
      it += entries.size() - N;

    while(it != entries.end())
      writer.String(*it++);

    writer.EndArray();
  }

private:
  const ::Logger& logger_;
  const size_t entries_;

};

} // < namespace dashboard

#endif

<commit_msg>Dashboard: Logger no longer hacked<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once
#ifndef DASHBOARD_COMPONENTS_LOGGER_HPP
#define DASHBOARD_COMPONENTS_LOGGER_HPP

#include "../component.hpp"

#include <logger/logger.hpp>

namespace dashboard {

class Logger : public Component {

public:

  Logger(::Logger& logger, size_t entries = 50)
   : logger_{logger}, entries_{entries}
  {}

  std::string key() const override
  { return "logger"; }

  void serialize(Writer& writer) const override {
    writer.StartArray();
    auto entries = (entries_) ? logger_.entries(entries_) : logger_.entries();

    auto it = entries.begin();

    while(it != entries.end())
      writer.String(*it++);

    writer.EndArray();
  }

private:
  const ::Logger& logger_;
  const size_t entries_;

};

} // < namespace dashboard

#endif

<|endoftext|>


<commit_before>#ifndef ACTIONMAPPINGS_HPP
#define ACTIONMAPPINGS_HPP

#include <unordered_map>
#include <string>
#include <vector>
#include <SDL.h>
#include "LuaReference.hpp"
#include "LuaState.hpp"

class ActionMappings
{
    public:
        ActionMappings(LuaState& lua);
        virtual ~ActionMappings();
        ActionMappings(const ActionMappings& other) = delete;
        ActionMappings& operator=(const ActionMappings& other) = delete;

        bool CreateAction(std::string name, LuaReference keyCallback);
        bool CreateAction(std::string name, LuaReference keyDownCallback, LuaReference keyUpCallback);

        void AddActionKey(std::string actionName, SDL_Keycode key);

        void FireActionKeyDown(SDL_Keycode key, int value);

        void DestroyState()
        {
//            _entities.clear();
//            _callback = LuaReference();
            _actionKeys.clear();
            _actionCallbacks.clear();
        }

        void InitializeLua()
        {
            _lua.SetUserData((void*)&LuaKeyBase, this);
            _lua.AddFunction(AddActionCallbacks, "AddActionCallbacks");
        }

    protected:
    private:
        struct Action
        {
            public:
                Action(LuaReference keyDownCallback, LuaReference keyUpCallback)
                {
                    _keyDownCallback = std::move(keyDownCallback);
                    _keyUpCallback = std::move(keyUpCallback);
                }

                Action(LuaReference keyCallback)
                    : _keyDownCallback(std::move(keyCallback))
                {
//                    _keyDownCallback = std::move(keyCallback);
                }

                void FireKeyDown(LuaState& lua, int value)
                {
                    if (_keyDownCallback.HasReference())
                    {
                        auto state = lua.Raw();
                        _keyDownCallback.Push();
                        lua_pushnumber(state, value);
                        lua.Call(1, 0);
                    }
                }

                void FireKeyUp(LuaState& lua, int value)
                {
                    if (_keyUpCallback.HasReference())
                    {
                        auto state = lua.Raw();
                        _keyDownCallback.Push();
                        lua_pushnumber(state, value);
                        lua.Call(1, 0);
                    }
                    else
                    {
                        FireKeyDown(lua, value);
                    }
                }

            private:

                LuaReference _keyDownCallback;
                LuaReference _keyUpCallback;
        };

        std::unordered_map<SDL_Keycode, Action*> _actionCallbacks;
        std::unordered_map<std::string, Action> _actionKeys;

        LuaState& _lua;

        static const int LuaKeyBase;
        static ActionMappings& FromLua(lua_State* state);
        static int AddActionCallbacks(lua_State* state);

};

#endif // ACTIONMAPPINGS_HPP
<commit_msg>Cleanup.<commit_after>#ifndef ACTIONMAPPINGS_HPP
#define ACTIONMAPPINGS_HPP

#include <unordered_map>
#include <string>
#include <vector>
#include <SDL.h>
#include "LuaReference.hpp"
#include "LuaState.hpp"

class ActionMappings
{
    public:
        ActionMappings(LuaState& lua);
        virtual ~ActionMappings();
        ActionMappings(const ActionMappings& other) = delete;
        ActionMappings& operator=(const ActionMappings& other) = delete;

        bool CreateAction(std::string name, LuaReference keyCallback);
        bool CreateAction(std::string name, LuaReference keyDownCallback, LuaReference keyUpCallback);

        void AddActionKey(std::string actionName, SDL_Keycode key);

        void FireActionKeyDown(SDL_Keycode key, int value);

        void DestroyState()
        {
            _actionKeys.clear();
            _actionCallbacks.clear();
        }

        void InitializeLua()
        {
            _lua.SetUserData((void*)&LuaKeyBase, this);
            _lua.AddFunction(AddActionCallbacks, "AddActionCallbacks");
        }

    protected:
    private:
        struct Action
        {
            public:
                Action(LuaReference keyDownCallback, LuaReference keyUpCallback)
                {
                    _keyDownCallback = std::move(keyDownCallback);
                    _keyUpCallback = std::move(keyUpCallback);
                }

                Action(LuaReference keyCallback)
                    : _keyDownCallback(std::move(keyCallback))
                {

                }

                void FireKeyDown(LuaState& lua, int value)
                {
                    if (_keyDownCallback.HasReference())
                    {
                        auto state = lua.Raw();
                        _keyDownCallback.Push();
                        lua_pushnumber(state, value);
                        lua.Call(1, 0);
                    }
                }

                void FireKeyUp(LuaState& lua, int value)
                {
                    if (_keyUpCallback.HasReference())
                    {
                        auto state = lua.Raw();
                        _keyDownCallback.Push();
                        lua_pushnumber(state, value);
                        lua.Call(1, 0);
                    }
                    else
                    {
                        FireKeyDown(lua, value);
                    }
                }

            private:

                LuaReference _keyDownCallback;
                LuaReference _keyUpCallback;
        };

        std::unordered_map<SDL_Keycode, Action*> _actionCallbacks;
        std::unordered_map<std::string, Action> _actionKeys;

        LuaState& _lua;

        static const int LuaKeyBase;
        static ActionMappings& FromLua(lua_State* state);
        static int AddActionCallbacks(lua_State* state);

};

#endif // ACTIONMAPPINGS_HPP
<|endoftext|>


<commit_before>// $Id: exception.C,v 1.13 2000/08/09 09:18:50 amoll Exp $

#include <BALL/COMMON/exception.h>
#include <BALL/COMMON/logStream.h>

#include <iostream>
#include <typeinfo>
#include <exception>
#include <stdio.h>
#include <stdlib.h>	// for getenv in terminate()
#include <sys/types.h>
#include <signal.h> // for SIGSEGV and kill
#include <unistd.h> // fot getpid

#define BALL_CORE_DUMP_ENVNAME "BALL_DUMP_CORE"

#define DEF_EXCEPTION(a,b) \
	a##::##a##(const char* file, int line)\
		: GeneralException(file, line, #a, b)\
	{\
	}\
\

using std::string;
using std::endl;

namespace BALL
{

	namespace Exception
	{

			GeneralException::GeneralException()
				:	file_("?"),
					line_(-1),
					name_("GeneralException"),
					message_("unspecified error")
			{
				globalHandler.set(file_, line_, name_, message_);
			}

			GeneralException::GeneralException(const char* file, int line, const string& name, const string& message)
				:	file_(file),
					line_(line),
					name_(name),
					message_(message)
			{
				globalHandler.set(file_, line_, name_, message_);
			}

			GeneralException::GeneralException(const GeneralException& exception)
				:	file_(exception.file_),
					line_(exception.line_),
					name_(exception.name_),
					message_(exception.message_)
			{
			}

			GeneralException::~GeneralException()
			{
			}

			string GeneralException::getName() const
			{
				return name_;
			}

			string GeneralException::getMessage() const
			{
				return message_;
			}

			string GeneralException::getFile() const
			{
				return file_;
			}

			int GeneralException::getLine() const
			{
				return line_;
			}

			IndexUnderflow::IndexUnderflow(const char* file, int line, Index index, Size size)
				: GeneralException(file, line, "IndexUnderflow", ""),
					size_(size),
					index_(index)
			{
				message_ = "the given index was too small: ";
				char buf[40];

				sprintf(buf, "%ld", (long)index);
				message_ += buf;
				message_ += " (size = ";

				sprintf(buf, "%ld", (long)size);
				message_ += buf;
				message_ += ")";

				globalHandler.setMessage(message_);
			}

			IndexOverflow::IndexOverflow(const char* file, int line, Index index, Size size)
				:	GeneralException(file, line, "IndexOverflow", "an index was too large"),
					size_(size),
					index_(index)
			{
				message_ = "the given index was too large: ";
				char buf[40];

				sprintf(buf, "%ld", (long)index);
				message_ += buf;
				message_ += " (size = ";

				sprintf(buf, "%ld", (long)size);
				message_ += buf;
				message_ += ")";

				globalHandler.setMessage(message_);
			}

			OutOfMemory::OutOfMemory(const char* file, int line, Size size)
				:	GeneralException(file, line, "OutOfMemory", "a memory allocation failed"),
					size_(size)
			{
				message_ = "unable to allocate enough memory (size = ";
				char buf[40];

				sprintf(buf, "%ld", (long)size_);
				message_ += buf;
				message_ += " bytes) ";

				globalHandler.setMessage(message_);
			}

			OutOfMemory::~OutOfMemory() throw()
			{
			}

			SizeUnderflow::SizeUnderflow(const char* file, int line, Size size)
				:	GeneralException(file, line, "SizeUnderflow", ""),
					size_(size)
			{
				message_ = "the given size was too small: ";
				char buf[40];
				sprintf(buf, "%ld", (long)size);

				message_ += buf;
				globalHandler.setMessage(message_);
			}

			FileNotFound::FileNotFound(const char* file, int line, const string& filename)
				:	GeneralException(file, line, "FileNotFound", ""),
					filename_(filename)
			{
				message_ = "the file " + filename + " could not be found";
				globalHandler.setMessage(message_);
			}

			string FileNotFound::getFilename() const
			{
				return filename_;
			}

			InvalidFormat::InvalidFormat(const char* file, int line, const string& s)
				:	GeneralException(file, line, "InvalidFormat", ""),
					format_(s)
			{
				message_ = "problem converting '";
				message_.append(s + "' to a number.");

				globalHandler.setMessage(message_);
			}

			DEF_EXCEPTION(DivisionByZero, "a division by zero was requested")

			DEF_EXCEPTION(InvalidRange, "the range of the operation was invalid")

			DEF_EXCEPTION(NullPointer, "a null pointer was specified")

			DEF_EXCEPTION(InvalidIterator, "the iterator is invalid - probably it is not bound to a container")

			DEF_EXCEPTION(IncompatibleIterators, "the iterator could not be assigned because it is bound to a different container")

			DEF_EXCEPTION(NotImplemented, "this method has not been implemented yet. Feel free to complain about it!")

			DEF_EXCEPTION(IllegalTreeOperation, "a illegal tree operation was requested")

			DEF_EXCEPTION(BufferOverflow, "the maximum buffersize has been reached")

			DEF_EXCEPTION(OutOfGrid, "a point was outside a grid")

			GlobalExceptionHandler::GlobalExceptionHandler()
			{
				std::set_terminate(terminate);
				std::set_unexpected(terminate);
				std::set_new_handler(newHandler);
			}

			void GlobalExceptionHandler::newHandler()
			{
				throw Exception::OutOfMemory(__FILE__, __LINE__);
			}

			void GlobalExceptionHandler::terminate()
			{
				// add cerr to the log stream
				// and write all available information on
				// the exception to the log stream (potentially with an assigned file!)
				// and cerr

				Log.insert(std::cerr);
				Log.error() << endl;
				Log.error() << "---------------------------------------------------" << endl;
				Log.error() << "FATAL: terminate called!" << endl;
				Log.error() << "---------------------------------------------------" << endl;
				if ((line_ != -1) && (name_ != "unknown"))
				{
					Log.error() << "last entry in the exception handler: " << endl;
					Log.error() << "exception of type " << name_.c_str() << " occured in line "
											<< line_ << " of " << file_.c_str() << endl;
					Log.error() << "error message: " << message_.c_str() << endl;
				}
				Log.error() << "---------------------------------------------------" << endl;

				// if the environment variable declared in BALL_CORE_DUMP_ENVNAME
				// is set, provoke a core dump (this is helpful to get s stack traceback)
				if (getenv(BALL_CORE_DUMP_ENVNAME) != 0)
				{
					Log.error() << "dumping core file.... (to avoid this, unset " << BALL_CORE_DUMP_ENVNAME
											<< " in your environment)" << endl;
					// provoke a core dump
					kill(getpid(), SIGSEGV);
				}

				// otherwise exit cleanly
				exit(1);
			}

			void GlobalExceptionHandler::set
				(const string& file, int line,
				 const string& name, const string& message)
			{
				name_ = name;
				line_ = line;
				message_ = message;
				file_ = file;
			}

			void GlobalExceptionHandler::setName(const string& name)
			{
				name_ = name;
			}

			void GlobalExceptionHandler::setMessage(const string& message)
			{
				message_ = message;
			}

			void GlobalExceptionHandler::setFile(const string& file)
			{
				file_ = file;
			}

			void GlobalExceptionHandler::setLine(int line)
			{
				line_ = line;
			}

			string	GlobalExceptionHandler::name_			= "unknown exception";
			int			GlobalExceptionHandler::line_			= -1;
			string	GlobalExceptionHandler::message_	= " - ";
			string	GlobalExceptionHandler::file_			= "unknown";

			// create a global instance of the exception handler
			GlobalExceptionHandler globalHandler;

	} // namespace Exception

} // namespace BALL
<commit_msg>fixed: spelling error<commit_after>// $Id: exception.C,v 1.14 2000/09/01 10:15:29 anker Exp $

#include <BALL/COMMON/exception.h>
#include <BALL/COMMON/logStream.h>

#include <iostream>
#include <typeinfo>
#include <exception>
#include <stdio.h>
#include <stdlib.h>	// for getenv in terminate()
#include <sys/types.h>
#include <signal.h> // for SIGSEGV and kill
#include <unistd.h> // fot getpid

#define BALL_CORE_DUMP_ENVNAME "BALL_DUMP_CORE"

#define DEF_EXCEPTION(a,b) \
	a##::##a##(const char* file, int line)\
		: GeneralException(file, line, #a, b)\
	{\
	}\
\

using std::string;
using std::endl;

namespace BALL
{

	namespace Exception
	{

			GeneralException::GeneralException()
				:	file_("?"),
					line_(-1),
					name_("GeneralException"),
					message_("unspecified error")
			{
				globalHandler.set(file_, line_, name_, message_);
			}

			GeneralException::GeneralException(const char* file, int line, const string& name, const string& message)
				:	file_(file),
					line_(line),
					name_(name),
					message_(message)
			{
				globalHandler.set(file_, line_, name_, message_);
			}

			GeneralException::GeneralException(const GeneralException& exception)
				:	file_(exception.file_),
					line_(exception.line_),
					name_(exception.name_),
					message_(exception.message_)
			{
			}

			GeneralException::~GeneralException()
			{
			}

			string GeneralException::getName() const
			{
				return name_;
			}

			string GeneralException::getMessage() const
			{
				return message_;
			}

			string GeneralException::getFile() const
			{
				return file_;
			}

			int GeneralException::getLine() const
			{
				return line_;
			}

			IndexUnderflow::IndexUnderflow(const char* file, int line, Index index, Size size)
				: GeneralException(file, line, "IndexUnderflow", ""),
					size_(size),
					index_(index)
			{
				message_ = "the given index was too small: ";
				char buf[40];

				sprintf(buf, "%ld", (long)index);
				message_ += buf;
				message_ += " (size = ";

				sprintf(buf, "%ld", (long)size);
				message_ += buf;
				message_ += ")";

				globalHandler.setMessage(message_);
			}

			IndexOverflow::IndexOverflow(const char* file, int line, Index index, Size size)
				:	GeneralException(file, line, "IndexOverflow", "an index was too large"),
					size_(size),
					index_(index)
			{
				message_ = "the given index was too large: ";
				char buf[40];

				sprintf(buf, "%ld", (long)index);
				message_ += buf;
				message_ += " (size = ";

				sprintf(buf, "%ld", (long)size);
				message_ += buf;
				message_ += ")";

				globalHandler.setMessage(message_);
			}

			OutOfMemory::OutOfMemory(const char* file, int line, Size size)
				:	GeneralException(file, line, "OutOfMemory", "a memory allocation failed"),
					size_(size)
			{
				message_ = "unable to allocate enough memory (size = ";
				char buf[40];

				sprintf(buf, "%ld", (long)size_);
				message_ += buf;
				message_ += " bytes) ";

				globalHandler.setMessage(message_);
			}

			OutOfMemory::~OutOfMemory() throw()
			{
			}

			SizeUnderflow::SizeUnderflow(const char* file, int line, Size size)
				:	GeneralException(file, line, "SizeUnderflow", ""),
					size_(size)
			{
				message_ = "the given size was too small: ";
				char buf[40];
				sprintf(buf, "%ld", (long)size);

				message_ += buf;
				globalHandler.setMessage(message_);
			}

			FileNotFound::FileNotFound(const char* file, int line, const string& filename)
				:	GeneralException(file, line, "FileNotFound", ""),
					filename_(filename)
			{
				message_ = "the file " + filename + " could not be found";
				globalHandler.setMessage(message_);
			}

			string FileNotFound::getFilename() const
			{
				return filename_;
			}

			InvalidFormat::InvalidFormat(const char* file, int line, const string& s)
				:	GeneralException(file, line, "InvalidFormat", ""),
					format_(s)
			{
				message_ = "problem converting '";
				message_.append(s + "' to a number.");

				globalHandler.setMessage(message_);
			}

			DEF_EXCEPTION(DivisionByZero, "a division by zero was requested")

			DEF_EXCEPTION(InvalidRange, "the range of the operation was invalid")

			DEF_EXCEPTION(NullPointer, "a null pointer was specified")

			DEF_EXCEPTION(InvalidIterator, "the iterator is invalid - probably it is not bound to a container")

			DEF_EXCEPTION(IncompatibleIterators, "the iterator could not be assigned because it is bound to a different container")

			DEF_EXCEPTION(NotImplemented, "this method has not been implemented yet. Feel free to complain about it!")

			DEF_EXCEPTION(IllegalTreeOperation, "an illegal tree operation was requested")

			DEF_EXCEPTION(BufferOverflow, "the maximum buffersize has been reached")

			DEF_EXCEPTION(OutOfGrid, "a point was outside a grid")

			GlobalExceptionHandler::GlobalExceptionHandler()
			{
				std::set_terminate(terminate);
				std::set_unexpected(terminate);
				std::set_new_handler(newHandler);
			}

			void GlobalExceptionHandler::newHandler()
			{
				throw Exception::OutOfMemory(__FILE__, __LINE__);
			}

			void GlobalExceptionHandler::terminate()
			{
				// add cerr to the log stream
				// and write all available information on
				// the exception to the log stream (potentially with an assigned file!)
				// and cerr

				Log.insert(std::cerr);
				Log.error() << endl;
				Log.error() << "---------------------------------------------------" << endl;
				Log.error() << "FATAL: terminate called!" << endl;
				Log.error() << "---------------------------------------------------" << endl;
				if ((line_ != -1) && (name_ != "unknown"))
				{
					Log.error() << "last entry in the exception handler: " << endl;
					Log.error() << "exception of type " << name_.c_str() << " occured in line "
											<< line_ << " of " << file_.c_str() << endl;
					Log.error() << "error message: " << message_.c_str() << endl;
				}
				Log.error() << "---------------------------------------------------" << endl;

				// if the environment variable declared in BALL_CORE_DUMP_ENVNAME
				// is set, provoke a core dump (this is helpful to get s stack traceback)
				if (getenv(BALL_CORE_DUMP_ENVNAME) != 0)
				{
					Log.error() << "dumping core file.... (to avoid this, unset " << BALL_CORE_DUMP_ENVNAME
											<< " in your environment)" << endl;
					// provoke a core dump
					kill(getpid(), SIGSEGV);
				}

				// otherwise exit cleanly
				exit(1);
			}

			void GlobalExceptionHandler::set
				(const string& file, int line,
				 const string& name, const string& message)
			{
				name_ = name;
				line_ = line;
				message_ = message;
				file_ = file;
			}

			void GlobalExceptionHandler::setName(const string& name)
			{
				name_ = name;
			}

			void GlobalExceptionHandler::setMessage(const string& message)
			{
				message_ = message;
			}

			void GlobalExceptionHandler::setFile(const string& file)
			{
				file_ = file;
			}

			void GlobalExceptionHandler::setLine(int line)
			{
				line_ = line;
			}

			string	GlobalExceptionHandler::name_			= "unknown exception";
			int			GlobalExceptionHandler::line_			= -1;
			string	GlobalExceptionHandler::message_	= " - ";
			string	GlobalExceptionHandler::file_			= "unknown";

			// create a global instance of the exception handler
			GlobalExceptionHandler globalHandler;

	} // namespace Exception

} // namespace BALL
<|endoftext|>


<commit_before><commit_msg>samples_index: update examples status<commit_after><|endoftext|>


<commit_before>// OS-specific networking includes
// -------------------------------
#ifdef __WIN32
    #include <winsock2.h>
    typedef int socklen_t;
#else
    extern "C" {
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <fcntl.h>
    }

    typedef int SOCKET;
    typedef sockaddr_in SOCKADDR_IN;
    typedef sockaddr SOCKADDR;
    #define SOCKET_ERROR -1
#endif

// Socket used for all communications
SOCKET sock;

// Address of the remote server
SOCKADDR_IN addr;

// Buffer used to return dynamic strings to the caller
#define BUFFER_SIZE 1024
char return_buffer[BUFFER_SIZE];

// exposed functions
// ------------------------------

const char* SUCCESS = "1\0"; // string representing success

// arg1: ip(in the xx.xx.xx.xx format)
// arg2: port(a short)
// return: NULL on failure, SUCCESS otherwise
extern "C" __declspec(dllexport) const char* establish_connection(int n, char *v[])
{
    // extract args
    // ------------
    if(n < 2) return 0;
    const char* ip = v[0];
    const char* port_s = v[1];
    unsigned short port = atoi(port_s);

    // set up network stuff
    // --------------------
    #ifdef __WIN32
        WSADATA wsa;
        WSAStartup(MAKEWORD(2,0),&wsa);
    #endif
    sock = socket(AF_INET,SOCK_DGRAM,0);

    // make the socket non-blocking
    // ----------------------------
    #ifdef __WIN32
        unsigned long iMode=1;
        ioctlsocket(sock,FIONBIO,&iMode);
    #else
        fcntl(sock, F_SETFL, O_NONBLOCK);
    #endif

    // establish a connection to the server
    // ------------------------------------
    memset(&addr,0,sizeof(SOCKADDR_IN));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);

    // convert the string representation of the ip to a byte representation
    addr.sin_addr.s_addr=inet_addr(ip);

    return SUCCESS;
}

// arg1: string message to send
// return: NULL on failure, SUCCESS otherwise
extern "C" __declspec(dllexport) const char* send_message(int n, char *v[])
{
    // extract the args
    if(n < 1) return 0;
    const char* msg = v[0];

    // send the message
    int rc = sendto(sock,msg,strlen(msg),0,(SOCKADDR*)&addr,sizeof(SOCKADDR));

    // check for errors
    if (rc != -1) {
       return SUCCESS;
    }
    else {
       return 0;
    }
}

// no args
// return: message if any received, NULL otherwise
extern "C" __declspec(dllexport) const char* recv_message(int n, char *v[])
{
    SOCKADDR_IN sender; // we will store the sender address here

    socklen_t sender_byte_length = sizeof(sender);

    // Try receiving messages until we receive one that's valid, or there are no more messages
    while(1) {
        int rc = recvfrom(sock, return_buffer, BUFFER_SIZE,0,(SOCKADDR*) &sender,&sender_byte_length);
        if(rc > 0) {
            // we could read something

            if(sender.sin_addr.s_addr != addr.sin_addr.s_addr) {
                continue; // not our connection, ignore and try again
            } else {
                return_buffer[rc] = '0'; // 0-terminate the string
                return return_buffer;
            }
        }
        else {
            break; // no more messages, stop trying to receive
        }
    }

    return 0;
}
<commit_msg>Linux compile-fix for DLLSocket/main.cpp<commit_after>// OS-specific networking includes
// -------------------------------
#ifdef __WIN32
    #include <winsock2.h>
    typedef int socklen_t;
#else
    extern "C" {
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <string.h>
    }

    typedef int SOCKET;
    typedef sockaddr_in SOCKADDR_IN;
    typedef sockaddr SOCKADDR;
    #define SOCKET_ERROR -1
#endif

// Socket used for all communications
SOCKET sock;

// Address of the remote server
SOCKADDR_IN addr;

// Buffer used to return dynamic strings to the caller
#define BUFFER_SIZE 1024
char return_buffer[BUFFER_SIZE];

// exposed functions
// ------------------------------

const char* SUCCESS = "1\0"; // string representing success

#ifdef __WIN32
    #define DLL_EXPORT __declspec(dllexport)
#else
    #define DLL_EXPORT _attribute__ ((visibility ("default")))
#endif

// arg1: ip(in the xx.xx.xx.xx format)
// arg2: port(a short)
// return: NULL on failure, SUCCESS otherwise
extern "C" DLL_EXPORT const char* establish_connection(int n, char *v[])
{
    // extract args
    // ------------
    if(n < 2) return 0;
    const char* ip = v[0];
    const char* port_s = v[1];
    unsigned short port = atoi(port_s);

    // set up network stuff
    // --------------------
    #ifdef __WIN32
        WSADATA wsa;
        WSAStartup(MAKEWORD(2,0),&wsa);
    #endif
    sock = socket(AF_INET,SOCK_DGRAM,0);

    // make the socket non-blocking
    // ----------------------------
    #ifdef __WIN32
        unsigned long iMode=1;
        ioctlsocket(sock,FIONBIO,&iMode);
    #else
        fcntl(sock, F_SETFL, O_NONBLOCK);
    #endif

    // establish a connection to the server
    // ------------------------------------
    memset(&addr,0,sizeof(SOCKADDR_IN));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);

    // convert the string representation of the ip to a byte representation
    addr.sin_addr.s_addr=inet_addr(ip);

    return SUCCESS;
}

// arg1: string message to send
// return: NULL on failure, SUCCESS otherwise
extern "C" DLL_EXPORT const char* send_message(int n, char *v[])
{
    // extract the args
    if(n < 1) return 0;
    const char* msg = v[0];

    // send the message
    int rc = sendto(sock,msg,strlen(msg),0,(SOCKADDR*)&addr,sizeof(SOCKADDR));

    // check for errors
    if (rc != -1) {
       return SUCCESS;
    }
    else {
       return 0;
    }
}

// no args
// return: message if any received, NULL otherwise
extern "C" DLL_EXPORT const char* recv_message(int n, char *v[])
{
    SOCKADDR_IN sender; // we will store the sender address here

    socklen_t sender_byte_length = sizeof(sender);

    // Try receiving messages until we receive one that's valid, or there are no more messages
    while(1) {
        int rc = recvfrom(sock, return_buffer, BUFFER_SIZE,0,(SOCKADDR*) &sender,&sender_byte_length);
        if(rc > 0) {
            // we could read something

            if(sender.sin_addr.s_addr != addr.sin_addr.s_addr) {
                continue; // not our connection, ignore and try again
            } else {
                return_buffer[rc] = '0'; // 0-terminate the string
                return return_buffer;
            }
        }
        else {
            break; // no more messages, stop trying to receive
        }
    }

    return 0;
}
<|endoftext|>