<commit_before>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// 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.

/// \file
/// Tests for the SixenseDevice class.

#include <memory>
#include <string>
#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#include <gtest/gtest.h>
#include "SurgSim/Devices/Sixense/SixenseDevice.h"
//#include "SurgSim/Devices/Sixense/SixenseScaffold.h"  // only needed if calling SixenseScaffold::setDefaultLogLevel()
#include "SurgSim/DataStructures/DataGroup.h"
#include "SurgSim/Input/InputConsumerInterface.h"
#include "SurgSim/Input/OutputProducerInterface.h"
#include "SurgSim/Math/RigidTransform.h"
#include "SurgSim/Math/Matrix.h"

using SurgSim::Device::SixenseDevice;
using SurgSim::Device::SixenseScaffold;
using SurgSim::DataStructures::DataGroup;
using SurgSim::Input::InputConsumerInterface;
using SurgSim::Input::OutputProducerInterface;
using SurgSim::Math::RigidTransform3d;
using SurgSim::Math::Matrix44d;

struct TestListener : public InputConsumerInterface, public OutputProducerInterface
{
public:
	TestListener() :
		m_numTimesInitializedInput(0),
		m_numTimesReceivedInput(0),
		m_numTimesRequestedOutput(0)
	{
	}

	virtual void initializeInput(const std::string& device, const DataGroup& inputData);
	virtual void handleInput(const std::string& device, const DataGroup& inputData);
	virtual bool requestOutput(const std::string& device, DataGroup* outputData);

	int m_numTimesInitializedInput;
	int m_numTimesReceivedInput;
	int m_numTimesRequestedOutput;
	DataGroup m_lastReceivedInput;
};

void TestListener::initializeInput(const std::string& device, const DataGroup& inputData)
{
	++m_numTimesInitializedInput;
}

void TestListener::handleInput(const std::string& device, const DataGroup& inputData)
{
	++m_numTimesReceivedInput;
	m_lastReceivedInput = inputData;
}

bool TestListener::requestOutput(const std::string& device, DataGroup* outputData)
{
	++m_numTimesRequestedOutput;
	return false;
}

TEST(SixenseDeviceTest, CreateUninitializedDevice)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
}

TEST(SixenseDeviceTest, CreateAndInitializeDevice)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
	EXPECT_FALSE(device->isInitialized());
	ASSERT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";
	EXPECT_TRUE(device->isInitialized());
}

TEST(SixenseDeviceTest, Name)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
	EXPECT_EQ("TestSixense", device->getName());
	EXPECT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";
	EXPECT_EQ("TestSixense", device->getName());
}

TEST(SixenseDeviceTest, CreateDeviceSeveralTimes)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	for (int i = 0;  i < 6;  ++i)
	{
		std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
		ASSERT_TRUE(device != nullptr) << "Device creation failed.";
		ASSERT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";
		// the device will be destroyed here
	}
}

TEST(SixenseDeviceTest, CreateSeveralDevices)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device1 = std::make_shared<SixenseDevice>("Sixense1");
	ASSERT_TRUE(device1 != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device1->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";

	std::shared_ptr<SixenseDevice> device2 = std::make_shared<SixenseDevice>("Sixense1");
	ASSERT_TRUE(device2 != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device2->initialize()) << "Initialization failed for second controller." <<
		"  Is only one controller plugged in?";

	// We can't check what happens with the scaffolds, since those are no longer a part of the device's API...

	std::shared_ptr<SixenseDevice> device3 = std::make_shared<SixenseDevice>("Sixense3");
	ASSERT_TRUE(device3 != nullptr) << "Device creation failed.";
	if (! device3->initialize())
	{
		std::cerr << "[Warning: third Sixense/Hydra controller did not come up; is it plugged in?]" << std::endl;
	}
}

// Create a string representation from an int.
// C++11 adds std::to_string() to do this for various types, but VS2010 only half-supports that.
template <typename T>
inline std::string makeString(T value)
{
	std::ostringstream out;
	out << value;
	return out.str();
}

TEST(SixenseDeviceTest, CreateAllDevices)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::vector<std::shared_ptr<SixenseDevice>> devices;

	for (int i = 1;  ;  ++i)
	{
		std::string name = "Sixense" + makeString(i);
		std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>(name);
		ASSERT_TRUE(device != nullptr) << "Device creation failed.";
		if (! device->initialize())
		{
			break;
		}
		devices.emplace_back(std::move(device));
	}

	std::cout << devices.size() << " devices initialized." << std::endl;
	ASSERT_GT(devices.size(), 0U) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";
	EXPECT_GT(devices.size(), 1U) << "Controller #2 initialization failed.  Is only one controller plugged in?";
}

TEST(SixenseDeviceTest, InputConsumer)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";

	std::shared_ptr<TestListener> consumer = std::make_shared<TestListener>();
	EXPECT_EQ(0, consumer->m_numTimesInitializedInput);
	EXPECT_EQ(0, consumer->m_numTimesReceivedInput);

	EXPECT_FALSE(device->removeInputConsumer(consumer));
	EXPECT_EQ(0, consumer->m_numTimesInitializedInput);
	EXPECT_EQ(0, consumer->m_numTimesReceivedInput);

	EXPECT_TRUE(device->addInputConsumer(consumer));

	// Adding the same input consumer again should fail.
	EXPECT_FALSE(device->addInputConsumer(consumer));

	// Sleep for a second, to see how many times the consumer is invoked.
	// (A Sixense device updates internally at 60Hz, but our code currently runs at 120Hz to reduce latency.)
	boost::this_thread::sleep_until(boost::chrono::steady_clock::now() + boost::chrono::milliseconds(1000));

	EXPECT_TRUE(device->removeInputConsumer(consumer));

	// Removing the same input consumer again should fail.
	EXPECT_FALSE(device->removeInputConsumer(consumer));

	// Check the number of invocations.
	EXPECT_EQ(1, consumer->m_numTimesInitializedInput);
	EXPECT_GE(consumer->m_numTimesReceivedInput, 100);
	EXPECT_LE(consumer->m_numTimesReceivedInput, 140);

	EXPECT_TRUE(consumer->m_lastReceivedInput.poses().hasData("pose"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.scalars().hasData("trigger"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.scalars().hasData("joystickX"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.scalars().hasData("joystickY"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("buttonTrigger"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("buttonBumper"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("button1"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("button2"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("button3"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("button4"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("buttonStart"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("buttonJoystick"));
}

TEST(SixenseDeviceTest, OutputProducer)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";

	std::shared_ptr<TestListener> producer = std::make_shared<TestListener>();
	EXPECT_EQ(0, producer->m_numTimesRequestedOutput);

	EXPECT_FALSE(device->removeOutputProducer(producer));
	EXPECT_EQ(0, producer->m_numTimesRequestedOutput);

	EXPECT_TRUE(device->setOutputProducer(producer));

	// Sleep for a second, to see how many times the producer is invoked.
	// (A Sixense device is does not request any output.)
	boost::this_thread::sleep_until(boost::chrono::steady_clock::now() + boost::chrono::milliseconds(1000));

	EXPECT_TRUE(device->removeOutputProducer(producer));

	// Removing the same input producer again should fail.
	EXPECT_FALSE(device->removeOutputProducer(producer));

	// Check the number of invocations.
	EXPECT_EQ(0, producer->m_numTimesRequestedOutput);
}
<commit_msg>Add test for uniqueness of device names.<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// 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.

/// \file
/// Tests for the SixenseDevice class.

#include <memory>
#include <string>
#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#include <gtest/gtest.h>
#include "SurgSim/Devices/Sixense/SixenseDevice.h"
//#include "SurgSim/Devices/Sixense/SixenseScaffold.h"  // only needed if calling SixenseScaffold::setDefaultLogLevel()
#include "SurgSim/DataStructures/DataGroup.h"
#include "SurgSim/Input/InputConsumerInterface.h"
#include "SurgSim/Input/OutputProducerInterface.h"
#include "SurgSim/Math/RigidTransform.h"
#include "SurgSim/Math/Matrix.h"

using SurgSim::Device::SixenseDevice;
using SurgSim::Device::SixenseScaffold;
using SurgSim::DataStructures::DataGroup;
using SurgSim::Input::InputConsumerInterface;
using SurgSim::Input::OutputProducerInterface;
using SurgSim::Math::RigidTransform3d;
using SurgSim::Math::Matrix44d;

struct TestListener : public InputConsumerInterface, public OutputProducerInterface
{
public:
	TestListener() :
		m_numTimesInitializedInput(0),
		m_numTimesReceivedInput(0),
		m_numTimesRequestedOutput(0)
	{
	}

	virtual void initializeInput(const std::string& device, const DataGroup& inputData);
	virtual void handleInput(const std::string& device, const DataGroup& inputData);
	virtual bool requestOutput(const std::string& device, DataGroup* outputData);

	int m_numTimesInitializedInput;
	int m_numTimesReceivedInput;
	int m_numTimesRequestedOutput;
	DataGroup m_lastReceivedInput;
};

void TestListener::initializeInput(const std::string& device, const DataGroup& inputData)
{
	++m_numTimesInitializedInput;
}

void TestListener::handleInput(const std::string& device, const DataGroup& inputData)
{
	++m_numTimesReceivedInput;
	m_lastReceivedInput = inputData;
}

bool TestListener::requestOutput(const std::string& device, DataGroup* outputData)
{
	++m_numTimesRequestedOutput;
	return false;
}

TEST(SixenseDeviceTest, CreateUninitializedDevice)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
}

TEST(SixenseDeviceTest, CreateAndInitializeDevice)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
	EXPECT_FALSE(device->isInitialized());
	ASSERT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";
	EXPECT_TRUE(device->isInitialized());
}

TEST(SixenseDeviceTest, Name)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
	EXPECT_EQ("TestSixense", device->getName());
	EXPECT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";
	EXPECT_EQ("TestSixense", device->getName());
}

TEST(SixenseDeviceTest, CreateDeviceSeveralTimes)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	for (int i = 0;  i < 6;  ++i)
	{
		std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
		ASSERT_TRUE(device != nullptr) << "Device creation failed.";
		ASSERT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";
		// the device will be destroyed here
	}
}

TEST(SixenseDeviceTest, CreateSeveralDevices)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device1 = std::make_shared<SixenseDevice>("Sixense1");
	ASSERT_TRUE(device1 != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device1->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";

	std::shared_ptr<SixenseDevice> device2 = std::make_shared<SixenseDevice>("Sixense2");
	ASSERT_TRUE(device2 != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device2->initialize()) << "Initialization failed for second controller." <<
		"  Is only one controller plugged in?";

	// We can't check what happens with the scaffolds, since those are no longer a part of the device's API...

	std::shared_ptr<SixenseDevice> device3 = std::make_shared<SixenseDevice>("Sixense3");
	ASSERT_TRUE(device3 != nullptr) << "Device creation failed.";
	if (! device3->initialize())
	{
		std::cerr << "[Warning: third Sixense/Hydra controller did not come up; is it plugged in?]" << std::endl;
	}
}

TEST(SixenseDeviceTest, CreateDevicesWithSameName)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device1 = std::make_shared<SixenseDevice>("Sixense");
	ASSERT_TRUE(device1 != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device1->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";

	std::shared_ptr<SixenseDevice> device2 = std::make_shared<SixenseDevice>("Sixense");
	ASSERT_TRUE(device2 != nullptr) << "Device creation failed.";
	ASSERT_FALSE(device2->initialize()) << "Initialization succeeded despite duplicate name.";
}

// Create a string representation from an int.
// C++11 adds std::to_string() to do this for various types, but VS2010 only half-supports that.
template <typename T>
inline std::string makeString(T value)
{
	std::ostringstream out;
	out << value;
	return out.str();
}

TEST(SixenseDeviceTest, CreateAllDevices)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::vector<std::shared_ptr<SixenseDevice>> devices;

	for (int i = 1;  ;  ++i)
	{
		std::string name = "Sixense" + makeString(i);
		std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>(name);
		ASSERT_TRUE(device != nullptr) << "Device creation failed.";
		if (! device->initialize())
		{
			break;
		}
		devices.emplace_back(std::move(device));
	}

	std::cout << devices.size() << " devices initialized." << std::endl;
	ASSERT_GT(devices.size(), 0U) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";
	EXPECT_GT(devices.size(), 1U) << "Controller #2 initialization failed.  Is only one controller plugged in?";
}

TEST(SixenseDeviceTest, InputConsumer)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";

	std::shared_ptr<TestListener> consumer = std::make_shared<TestListener>();
	EXPECT_EQ(0, consumer->m_numTimesInitializedInput);
	EXPECT_EQ(0, consumer->m_numTimesReceivedInput);

	EXPECT_FALSE(device->removeInputConsumer(consumer));
	EXPECT_EQ(0, consumer->m_numTimesInitializedInput);
	EXPECT_EQ(0, consumer->m_numTimesReceivedInput);

	EXPECT_TRUE(device->addInputConsumer(consumer));

	// Adding the same input consumer again should fail.
	EXPECT_FALSE(device->addInputConsumer(consumer));

	// Sleep for a second, to see how many times the consumer is invoked.
	// (A Sixense device updates internally at 60Hz, but our code currently runs at 120Hz to reduce latency.)
	boost::this_thread::sleep_until(boost::chrono::steady_clock::now() + boost::chrono::milliseconds(1000));

	EXPECT_TRUE(device->removeInputConsumer(consumer));

	// Removing the same input consumer again should fail.
	EXPECT_FALSE(device->removeInputConsumer(consumer));

	// Check the number of invocations.
	EXPECT_EQ(1, consumer->m_numTimesInitializedInput);
	EXPECT_GE(consumer->m_numTimesReceivedInput, 100);
	EXPECT_LE(consumer->m_numTimesReceivedInput, 140);

	EXPECT_TRUE(consumer->m_lastReceivedInput.poses().hasData("pose"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.scalars().hasData("trigger"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.scalars().hasData("joystickX"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.scalars().hasData("joystickY"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("buttonTrigger"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("buttonBumper"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("button1"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("button2"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("button3"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("button4"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("buttonStart"));
	EXPECT_TRUE(consumer->m_lastReceivedInput.booleans().hasData("buttonJoystick"));
}

TEST(SixenseDeviceTest, OutputProducer)
{
	//SixenseScaffold::setDefaultLogLevel(SurgSim::Framework::LOG_LEVEL_DEBUG);
	std::shared_ptr<SixenseDevice> device = std::make_shared<SixenseDevice>("TestSixense");
	ASSERT_TRUE(device != nullptr) << "Device creation failed.";
	ASSERT_TRUE(device->initialize()) << "Initialization failed.  Is a Sixense/Hydra device plugged in?";

	std::shared_ptr<TestListener> producer = std::make_shared<TestListener>();
	EXPECT_EQ(0, producer->m_numTimesRequestedOutput);

	EXPECT_FALSE(device->removeOutputProducer(producer));
	EXPECT_EQ(0, producer->m_numTimesRequestedOutput);

	EXPECT_TRUE(device->setOutputProducer(producer));

	// Sleep for a second, to see how many times the producer is invoked.
	// (A Sixense device is does not request any output.)
	boost::this_thread::sleep_until(boost::chrono::steady_clock::now() + boost::chrono::milliseconds(1000));

	EXPECT_TRUE(device->removeOutputProducer(producer));

	// Removing the same input producer again should fail.
	EXPECT_FALSE(device->removeOutputProducer(producer));

	// Check the number of invocations.
	EXPECT_EQ(0, producer->m_numTimesRequestedOutput);
}
<|endoftext|>


<commit_before>// ///////////////////////////////////////////////////////////////////////////
// tenh/implementation/alt.hpp by Victor Dods, created 2013/09/26
// Copyright Leap Motion Inc.
// ///////////////////////////////////////////////////////////////////////////

#ifndef TENH_IMPLEMENTATION_ALT_HPP_
#define TENH_IMPLEMENTATION_ALT_HPP_

#include "tenh/core.hpp"

#include "tenh/conceptual/exteriorpower.hpp"
#include "tenh/implementation/tensor.hpp"
#include "tenh/implementation/wedge.hpp"

namespace Tenh {

namespace ComponentGeneratorEvaluator {

// this would be better defined as a private static method in Alt_f below, but g++
// complains about that being invalid in a const expression (whereas clang++ is fine with it)
template <Uint32 ORDER_, typename Factor_, Uint32 COMPONENT_COUNT_, typename Scalar_>
Scalar_ alt (ComponentIndex_t<COMPONENT_COUNT_> const &i)
{
    typedef ComponentIndex_t<COMPONENT_COUNT_> ComponentIndex;
    typedef ExteriorPowerOfBasedVectorSpace_c<ORDER_,Factor_> ExteriorPower;
    typedef typename TensorPowerOfBasedVectorSpace_f<ORDER_,Factor_>::T TensorPower;
    typedef TypeList_t<ExteriorPower,
            TypeList_t<typename DualOf_f<TensorPower>::T> > FactorTypeList;
    typedef TensorProductOfBasedVectorSpaces_c<FactorTypeList> Projection;
    STATIC_ASSERT(DimensionOf_f<Projection>::V == COMPONENT_COUNT_, DIMENSIONS_MUST_MATCH);

    // for using bundle_index_map -- the use of UsePreallocatedArray is somewhat arbitrary,
    // since only the static method bundle_index_map will be used.
    typedef ImplementationOf_t<Projection,Scalar_,UsePreallocatedArray> BootstrappingImplementation;
    typedef ImplementationOf_t<ExteriorPower,Scalar_,UsePreallocatedArray> Factor0BootstrappingImplementation;
    typedef ImplementationOf_t<TensorPower,Scalar_,UsePreallocatedArray> Factor1BootstrappingImplementation;

    typedef typename BootstrappingImplementation::MultiIndex MultiIndex;
    typedef typename MultiIndex::IndexTypeList IndexTypeList;
    STATIC_ASSERT(IndexTypeList::LENGTH == 2, LENGTH_MUST_BE_EXACTLY_2);
    MultiIndex m(BootstrappingImplementation::template bundle_index_map<IndexTypeList,ComponentIndex>(i));

    typedef typename Factor1BootstrappingImplementation::MultiIndex::IndexTypeList Factor1IndexTypeList;
    STATIC_ASSERT(Factor1IndexTypeList::LENGTH == ORDER_, LENGTHS_MUST_BE_EQUAL);
    typedef MultiIndex_t<Factor1IndexTypeList> TensorPowerMultiIndex;
    typedef typename Factor1BootstrappingImplementation::ComponentIndex TensorPowerComponentIndex;
    TensorPowerMultiIndex m1(Factor1BootstrappingImplementation::template bundle_index_map<Factor1IndexTypeList,TensorPowerComponentIndex>(m.template el<1>()));

    // if there is no memory location for the multiindex value, the component is zero
    if (Factor0BootstrappingImplementation::component_is_immutable_zero(m1))
    {
        return Scalar_(0);
    }
    // vector_index_of sorts the multiindex and returns the vector index -- if this
    // matches the symmetric vector index, then return 1; otherwise 0.
    else if (m.template el<0>() == Factor0BootstrappingImplementation::vector_index_of(m1))
    {
        typedef typename Factor0BootstrappingImplementation::MultiIndex ExteriorPowerMultiIndex;
        typedef typename ExteriorPowerMultiIndex::IndexTypeList Factor0IndexTypeList;
        typedef typename Factor0BootstrappingImplementation::ComponentIndex ExteriorPowerComponentIndex;
        ExteriorPowerMultiIndex m0(Factor0BootstrappingImplementation::template bundle_index_map<Factor0IndexTypeList,ExteriorPowerComponentIndex>(m.template el<0>()));
        return Factor0BootstrappingImplementation::scalar_factor_for_component(m1);
    }
    else
    {
        return Scalar_(0);
    }
}

} // end of namespace ComponentGeneratorEvaluator

template <Uint32 ORDER_, typename Factor_>
struct AltId_t
{
    static std::string type_as_string () { return "Alt<" + AS_STRING(ORDER_) + ',' + TypeStringOf_t<Factor_>::eval() + '>'; }
};

// template specialization for standard inner product on a based vector space having orthonormal basis
template <Uint32 ORDER_, typename Factor_, typename Scalar_>
struct Alt_f
{
private:
    typedef ExteriorPowerOfBasedVectorSpace_c<ORDER_,Factor_> ExteriorPower;
    typedef typename TensorPowerOfBasedVectorSpace_f<ORDER_,Factor_>::T TensorPower;
    typedef TypeList_t<ExteriorPower,
            TypeList_t<typename DualOf_f<TensorPower>::T> > FactorTypeList;
    typedef TensorProductOfBasedVectorSpaces_c<FactorTypeList> Projection;
    typedef ComponentGenerator_t<Scalar_,
                                 DimensionOf_f<Projection>::V,
                                 ComponentGeneratorEvaluator::alt<ORDER_,Factor_,DimensionOf_f<Projection>::V,Scalar_>,
                                 AltId_t<ORDER_,Factor_> > ComponentGenerator;
public:
    typedef ImplementationOf_t<Projection,Scalar_,UseImmutableArray_t<ComponentGenerator> > T;
};

} // end of namespace Tenh

#endif // TENH_IMPLEMENTATION_ALT_HPP_
<commit_msg>Fixed an unused variable warning/error.<commit_after>// ///////////////////////////////////////////////////////////////////////////
// tenh/implementation/alt.hpp by Victor Dods, created 2013/09/26
// Copyright Leap Motion Inc.
// ///////////////////////////////////////////////////////////////////////////

#ifndef TENH_IMPLEMENTATION_ALT_HPP_
#define TENH_IMPLEMENTATION_ALT_HPP_

#include "tenh/core.hpp"

#include "tenh/conceptual/exteriorpower.hpp"
#include "tenh/implementation/tensor.hpp"
#include "tenh/implementation/wedge.hpp"

namespace Tenh {

namespace ComponentGeneratorEvaluator {

// this would be better defined as a private static method in Alt_f below, but g++
// complains about that being invalid in a const expression (whereas clang++ is fine with it)
template <Uint32 ORDER_, typename Factor_, Uint32 COMPONENT_COUNT_, typename Scalar_>
Scalar_ alt (ComponentIndex_t<COMPONENT_COUNT_> const &i)
{
    typedef ComponentIndex_t<COMPONENT_COUNT_> ComponentIndex;
    typedef ExteriorPowerOfBasedVectorSpace_c<ORDER_,Factor_> ExteriorPower;
    typedef typename TensorPowerOfBasedVectorSpace_f<ORDER_,Factor_>::T TensorPower;
    typedef TypeList_t<ExteriorPower,
            TypeList_t<typename DualOf_f<TensorPower>::T> > FactorTypeList;
    typedef TensorProductOfBasedVectorSpaces_c<FactorTypeList> Projection;
    STATIC_ASSERT(DimensionOf_f<Projection>::V == COMPONENT_COUNT_, DIMENSIONS_MUST_MATCH);

    // for using bundle_index_map -- the use of UsePreallocatedArray is somewhat arbitrary,
    // since only the static method bundle_index_map will be used.
    typedef ImplementationOf_t<Projection,Scalar_,UsePreallocatedArray> BootstrappingImplementation;
    typedef ImplementationOf_t<ExteriorPower,Scalar_,UsePreallocatedArray> Factor0BootstrappingImplementation;
    typedef ImplementationOf_t<TensorPower,Scalar_,UsePreallocatedArray> Factor1BootstrappingImplementation;

    typedef typename BootstrappingImplementation::MultiIndex MultiIndex;
    typedef typename MultiIndex::IndexTypeList IndexTypeList;
    STATIC_ASSERT(IndexTypeList::LENGTH == 2, LENGTH_MUST_BE_EXACTLY_2);
    MultiIndex m(BootstrappingImplementation::template bundle_index_map<IndexTypeList,ComponentIndex>(i));

    typedef typename Factor1BootstrappingImplementation::MultiIndex::IndexTypeList Factor1IndexTypeList;
    STATIC_ASSERT(Factor1IndexTypeList::LENGTH == ORDER_, LENGTHS_MUST_BE_EQUAL);
    typedef MultiIndex_t<Factor1IndexTypeList> TensorPowerMultiIndex;
    typedef typename Factor1BootstrappingImplementation::ComponentIndex TensorPowerComponentIndex;
    TensorPowerMultiIndex m1(Factor1BootstrappingImplementation::template bundle_index_map<Factor1IndexTypeList,TensorPowerComponentIndex>(m.template el<1>()));

    // if there is no memory location for the multiindex value, the component is zero
    if (Factor0BootstrappingImplementation::component_is_immutable_zero(m1))
    {
        return Scalar_(0);
    }
    // vector_index_of sorts the multiindex and returns the vector index -- if this
    // matches the symmetric vector index, then return 1.
    else if (m.template el<0>() == Factor0BootstrappingImplementation::vector_index_of(m1))
    {
        return Factor0BootstrappingImplementation::scalar_factor_for_component(m1);
    }
    // otherwise 0.
    else
    {
        return Scalar_(0);
    }
}

} // end of namespace ComponentGeneratorEvaluator

template <Uint32 ORDER_, typename Factor_>
struct AltId_t
{
    static std::string type_as_string () { return "Alt<" + AS_STRING(ORDER_) + ',' + TypeStringOf_t<Factor_>::eval() + '>'; }
};

// template specialization for standard inner product on a based vector space having orthonormal basis
template <Uint32 ORDER_, typename Factor_, typename Scalar_>
struct Alt_f
{
private:
    typedef ExteriorPowerOfBasedVectorSpace_c<ORDER_,Factor_> ExteriorPower;
    typedef typename TensorPowerOfBasedVectorSpace_f<ORDER_,Factor_>::T TensorPower;
    typedef TypeList_t<ExteriorPower,
            TypeList_t<typename DualOf_f<TensorPower>::T> > FactorTypeList;
    typedef TensorProductOfBasedVectorSpaces_c<FactorTypeList> Projection;
    typedef ComponentGenerator_t<Scalar_,
                                 DimensionOf_f<Projection>::V,
                                 ComponentGeneratorEvaluator::alt<ORDER_,Factor_,DimensionOf_f<Projection>::V,Scalar_>,
                                 AltId_t<ORDER_,Factor_> > ComponentGenerator;
public:
    typedef ImplementationOf_t<Projection,Scalar_,UseImmutableArray_t<ComponentGenerator> > T;
};

} // end of namespace Tenh

#endif // TENH_IMPLEMENTATION_ALT_HPP_
<|endoftext|>


<commit_before>// Copyright (c) 2015 The Khronos Group Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
//    https://www.khronos.org/registry/
//
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.

// Assembler tests for instructions in the "Group Instrucions" section of the
// SPIR-V spec.

#include "UnitSPIRV.h"

#include "gmock/gmock.h"
#include "TestFixture.h"

namespace {

using spvtest::EnumCase;
using spvtest::MakeInstruction;
using ::testing::Eq;

// Test GroupOperation enum

using GroupOperationTest = spvtest::TextToBinaryTestBase<
    ::testing::TestWithParam<EnumCase<spv::GroupOperation>>>;

TEST_P(GroupOperationTest, AnyGroupOperation) {
  const std::string input =
      "%result = OpGroupIAdd %type %scope " + GetParam().name() + " %x";
  EXPECT_THAT(
      CompiledInstructions(input),
      Eq(MakeInstruction(spv::OpGroupIAdd, {1, 2, 3, GetParam().value(), 4})));
}

// clang-format off
#define CASE(NAME) { spv::GroupOperation##NAME, #NAME}
INSTANTIATE_TEST_CASE_P(TextToBinaryGroupOperation, GroupOperationTest,
                        ::testing::ValuesIn(std::vector<EnumCase<spv::GroupOperation>>{
                            CASE(Reduce),
                            CASE(InclusiveScan),
                            CASE(ExclusiveScan),
                        }));
#undef CASE
// clang-format on

// TODO(dneto): OpAsyncGroupCopy
// TODO(dneto): OpWaitGroupEvents
// TODO(dneto): OpGroupAll
// TODO(dneto): OpGroupAny
// TODO(dneto): OpGroupBroadcast
// TODO(dneto): OpGroupIAdd
// TODO(dneto): OpGroupFAdd
// TODO(dneto): OpGroupFMin
// TODO(dneto): OpGroupUMin
// TODO(dneto): OpGroupSMin
// TODO(dneto): OpGroupFMax
// TODO(dneto): OpGroupUMax
// TODO(dneto): OpGroupSMax

}  // anonymous namespace
<commit_msg>Negative test for GroupOperation.<commit_after>// Copyright (c) 2015 The Khronos Group Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and/or associated documentation files (the
// "Materials"), to deal in the Materials without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Materials, and to
// permit persons to whom the Materials are furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Materials.
//
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
//    https://www.khronos.org/registry/
//
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.

// Assembler tests for instructions in the "Group Instrucions" section of the
// SPIR-V spec.

#include "UnitSPIRV.h"

#include "gmock/gmock.h"
#include "TestFixture.h"

namespace {

using spvtest::EnumCase;
using spvtest::MakeInstruction;
using ::testing::Eq;

// Test GroupOperation enum

using GroupOperationTest = spvtest::TextToBinaryTestBase<
    ::testing::TestWithParam<EnumCase<spv::GroupOperation>>>;

TEST_P(GroupOperationTest, AnyGroupOperation) {
  const std::string input =
      "%result = OpGroupIAdd %type %scope " + GetParam().name() + " %x";
  EXPECT_THAT(
      CompiledInstructions(input),
      Eq(MakeInstruction(spv::OpGroupIAdd, {1, 2, 3, GetParam().value(), 4})));
}

// clang-format off
#define CASE(NAME) { spv::GroupOperation##NAME, #NAME}
INSTANTIATE_TEST_CASE_P(TextToBinaryGroupOperation, GroupOperationTest,
                        ::testing::ValuesIn(std::vector<EnumCase<spv::GroupOperation>>{
                            CASE(Reduce),
                            CASE(InclusiveScan),
                            CASE(ExclusiveScan),
                        }));
#undef CASE
// clang-format on

TEST_F(GroupOperationTest, WrongGroupOperation) {
  EXPECT_THAT(CompileFailure("%r = OpGroupUMin %t %e xxyyzz %x"),
              Eq("Invalid group operation 'xxyyzz'."));
}

// TODO(dneto): OpAsyncGroupCopy
// TODO(dneto): OpWaitGroupEvents
// TODO(dneto): OpGroupAll
// TODO(dneto): OpGroupAny
// TODO(dneto): OpGroupBroadcast
// TODO(dneto): OpGroupIAdd
// TODO(dneto): OpGroupFAdd
// TODO(dneto): OpGroupFMin
// TODO(dneto): OpGroupUMin
// TODO(dneto): OpGroupSMin
// TODO(dneto): OpGroupFMax
// TODO(dneto): OpGroupUMax
// TODO(dneto): OpGroupSMax

}  // anonymous namespace
<|endoftext|>


<commit_before>// 11 may 2015
// TODO split out winapi includes
#include "tablepriv.h"
// before we start, why is this file C++? because the following is not a valid C header file!
// namely, UIAutomationCoreApi.h forgets to typedef enum xxx { ... } xxx; and typedef struct xxx xxx; so it just uses xxx incorrectly
// and because these are enums, we can't just do typedef enum xxx xxx; here ourselves, as that's illegal in both C and C++!
// thanks, Microsoft!
// (if it was just structs I would just typedef them here but even that's not really futureproof)
#include <uiautomation.h>

// TODO
//#include <stdio.h>
extern "C" int printf(const char *,...);

// TODOs
// - make sure RPC_E_DISCONNECTED is correct; source it
// - make sure E_POINTER is correct

// well if we're stuck with C++, we might as well make the most of it
class tableAcc : public IRawElementProviderSimple {
	struct table *t;
	ULONG refcount;
public:
	tableAcc(struct table *);

	// internal methods
	void Invalidate(void);

	// IUnknown
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef(void);
	STDMETHODIMP_(ULONG) Release(void);

	// IRawElementProviderSimple
	STDMETHODIMP GetPatternProvider(PATTERNID patternId, IUnknown **pRetVal);
	STDMETHODIMP GetPropertyValue(PROPERTYID propertyId, VARIANT *pRetVal);
	STDMETHODIMP get_HostRawElementProvider(IRawElementProviderSimple **pRetVal);
	STDMETHODIMP get_ProviderOptions(ProviderOptions *pRetVal);
};

tableAcc::tableAcc(struct table *t)
{
printf("construct\n");
	this->t = t;
	this->refcount = 1;		// first instance goes to the table
}

void tableAcc::Invalidate(void)
{
printf("invalidate\n");
	// this will always be called before the tableAcc is destroyed because there's one ref given to the table itself
	this->t = NULL;
}

STDMETHODIMP tableAcc::QueryInterface(REFIID riid, void **ppvObject)
{
printf("query interface\n");
	if (ppvObject == NULL)
		return E_POINTER;
	if (IsEqualIID(riid, IID_IUnknown) ||
		IsEqualIID(riid, IID_IRawElementProviderSimple)) {
		this->AddRef();
		*ppvObject = this;
		return S_OK;
	}
	return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) tableAcc::AddRef(void)
{
printf("add ref\n");
	// http://blogs.msdn.com/b/oldnewthing/archive/2005/09/27/474384.aspx
	if (this->refcount == 0)
		logLastError("tableAcc::AddRef() called during destruction");
	this->refcount++;
	return this->refcount;
}

STDMETHODIMP_(ULONG) tableAcc::Release(void)
{
printf("release\n");
	this->refcount--;
	if (this->refcount == 0) {
printf("destroy\n");
		delete this;
		return 0;
	}
	return this->refcount;
}

STDMETHODIMP tableAcc::GetPatternProvider(PATTERNID patternId, IUnknown **pRetVal)
{
printf("get pattern provider\n");
	if (pRetVal == NULL)
		return E_POINTER;
	// TODO
	*pRetVal = NULL;
//	return S_OK;
	return E_NOTIMPL;
}

STDMETHODIMP tableAcc::GetPropertyValue(PROPERTYID propertyId, VARIANT *pRetVal)
{
printf("get property value\n");
	BSTR bstr;

	if (pRetVal == NULL)
		return E_POINTER;
	pRetVal->vt = VT_EMPTY;		// behavior on unknown property is to keep it VT_EMPTY and return S_OK
	switch (propertyId) {
	case UIA_NamePropertyId:
		bstr = SysAllocString(L"test string");
		if (bstr == NULL)
			return E_OUTOFMEMORY;
		pRetVal->vt = VT_BSTR;
		pRetVal->bstrVal = bstr;
		break;
	}
	return S_OK;
}

STDMETHODIMP tableAcc::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal)
{
printf("get host raw element provider\n");
	if (this->t != NULL) {
		if (pRetVal == NULL)
			return E_POINTER;
		// TODO correct?
		*pRetVal = NULL;
		return RPC_E_DISCONNECTED;
	}
	return UiaHostProviderFromHwnd(this->t->hwnd, pRetVal);
}

STDMETHODIMP tableAcc::get_ProviderOptions(ProviderOptions *pRetVal)
{
printf("get provider options\n");
	if (pRetVal == NULL)
		return E_POINTER;
	// TODO ProviderOptions_UseClientCoordinates?
	*pRetVal = ProviderOptions_ServerSideProvider;
	return S_OK;
}

void initTableAcc(struct table *t)
{
	tableAcc *a;

	a = new tableAcc(t);
	t->tableAcc = a;
}

void uninitTableAcc(struct table *t)
{
	tableAcc *a;

	a = (tableAcc *) (t->tableAcc);
	a->Invalidate();
	a->Release();
	t->tableAcc = NULL;
}

HANDLER(accessibilityHandler)
{
	if (uMsg != WM_GETOBJECT)
		return FALSE;
	// OBJID_CLIENT evaluates to an expression of type LONG
	// the documentation for WM_GETOBJECT says to cast "it" to a DWORD before comparing
	// https://msdn.microsoft.com/en-us/library/windows/desktop/dd373624%28v=vs.85%29.aspx casts them both to DWORDs; let's do that
	// its two siblings only cast lParam, resulting in an erroneous DWORD to LONG comparison
	// The Old New Thing book does not cast anything
	// Microsoft's MSAA sample casts lParam to LONG instead!
	// (As you can probably tell, the biggest problem with MSAA is that its documentation is ambiguous and/or self-contradictory...)
	// and https://msdn.microsoft.com/en-us/library/windows/desktop/ff625912%28v=vs.85%29.aspx casts them both to long!
	// Note that we're not using Active Accessibility, but the above applies even more, because UiaRootObjectId is *NEGATIVE*!
	if (((DWORD) lParam) != ((DWORD) UiaRootObjectId))
		return FALSE;
	*lResult = UiaReturnRawElementProvider(t->hwnd, wParam, lParam, (IRawElementProviderSimple *) (t->tableAcc));
	return TRUE;
}
<commit_msg>More tableAcc work.<commit_after>// 11 may 2015
// TODO split out winapi includes
#include "tablepriv.h"
// before we start, why is this file C++? because the following is not a valid C header file!
// namely, UIAutomationCoreApi.h forgets to typedef enum xxx { ... } xxx; and typedef struct xxx xxx; so it just uses xxx incorrectly
// and because these are enums, we can't just do typedef enum xxx xxx; here ourselves, as that's illegal in both C and C++!
// thanks, Microsoft!
// (if it was just structs I would just typedef them here but even that's not really futureproof)
#include <uiautomation.h>

// TODO
//#include <stdio.h>
extern "C" int printf(const char *,...);

// TODOs
// - make sure RPC_E_DISCONNECTED is correct; source it
// - make sure E_POINTER is correct

// well if we're stuck with C++, we might as well make the most of it
class tableAcc : public IRawElementProviderSimple/*TODO, public ITableProvider, public IGridProvider*/ {
	struct table *t;
	ULONG refcount;
public:
	tableAcc(struct table *);

	// internal methods
	void Invalidate(void);

	// IUnknown
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef(void);
	STDMETHODIMP_(ULONG) Release(void);

	// IRawElementProviderSimple
	STDMETHODIMP GetPatternProvider(PATTERNID patternId, IUnknown **pRetVal);
	STDMETHODIMP GetPropertyValue(PROPERTYID propertyId, VARIANT *pRetVal);
	STDMETHODIMP get_HostRawElementProvider(IRawElementProviderSimple **pRetVal);
	STDMETHODIMP get_ProviderOptions(ProviderOptions *pRetVal);

#if 0
	// ITableProvider
	STDMETHODIMP GetColumnHeaders(SAFEARRAY **pRetVal);
	STDMETHODIMP GetRowHeaders(SAFEARRAY **pRetVal);
	STDMETHODIMP get_RowOrColumnMajor(RowOrColumnMajor *pRetVal);
#endif
};

tableAcc::tableAcc(struct table *t)
{
printf("construct\n");
	this->t = t;
	this->refcount = 1;		// first instance goes to the table
}

void tableAcc::Invalidate(void)
{
printf("invalidate\n");
	// this will always be called before the tableAcc is destroyed because there's one ref given to the table itself
	this->t = NULL;
}

STDMETHODIMP tableAcc::QueryInterface(REFIID riid, void **ppvObject)
{
printf("query interface\n");
	if (ppvObject == NULL)
		return E_POINTER;
	if (IsEqualIID(riid, IID_IUnknown) ||
		IsEqualIID(riid, IID_IRawElementProviderSimple)) {
		this->AddRef();
		*ppvObject = this;
		return S_OK;
	}
	return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) tableAcc::AddRef(void)
{
printf("add ref\n");
	// http://blogs.msdn.com/b/oldnewthing/archive/2005/09/27/474384.aspx
	if (this->refcount == 0)
		logLastError("tableAcc::AddRef() called during destruction");
	this->refcount++;
	return this->refcount;
}

STDMETHODIMP_(ULONG) tableAcc::Release(void)
{
printf("release\n");
	this->refcount--;
	if (this->refcount == 0) {
printf("destroy\n");
		delete this;
		return 0;
	}
	return this->refcount;
}

STDMETHODIMP tableAcc::GetPatternProvider(PATTERNID patternId, IUnknown **pRetVal)
{
printf("get pattern provider\n");
	if (pRetVal == NULL)
		return E_POINTER;
//TODO	if (patternId != UIA_TablePatternId) {
		*pRetVal = NULL;
		return S_OK;
#if 0
	}
	*pRetVal = xxxxx;
	return S_OK;
#endif
}

STDMETHODIMP tableAcc::GetPropertyValue(PROPERTYID propertyId, VARIANT *pRetVal)
{
printf("get property value\n");
	BSTR bstr;

	if (pRetVal == NULL)
		return E_POINTER;
	pRetVal->vt = VT_EMPTY;		// behavior on unknown property is to keep it VT_EMPTY and return S_OK
	switch (propertyId) {
	case UIA_NamePropertyId:
		// TODO this doesn't show up
		bstr = SysAllocString(L"test string");
		if (bstr == NULL)
			return E_OUTOFMEMORY;
		pRetVal->vt = VT_BSTR;
		pRetVal->bstrVal = bstr;
		break;
	}
	return S_OK;
}

STDMETHODIMP tableAcc::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal)
{
printf("get host raw element provider\n");
	if (this->t != NULL) {
		if (pRetVal == NULL)
			return E_POINTER;
		// TODO correct?
		*pRetVal = NULL;
		return RPC_E_DISCONNECTED;
	}
	// TODO wait should we?
//	return UiaHostProviderFromHwnd(this->t->hwnd, pRetVal);
	*pRetVal = NULL;
	return S_OK;
}

STDMETHODIMP tableAcc::get_ProviderOptions(ProviderOptions *pRetVal)
{
printf("get provider options\n");
	if (pRetVal == NULL)
		return E_POINTER;
	// TODO ProviderOptions_UseClientCoordinates?
	*pRetVal = ProviderOptions_ServerSideProvider;
	return S_OK;
}

#if 0
STDMETHODIMP tableAcc::GetColumnHeaders(SAFEARRAY **pRetVal)
{
	if (pRetVal == NULL)
		return E_POINTER;
	*pRetVal = NULL;
	return E_NOTIMPL;
}

STDMETHODIMP tableAcc::GetRowHeaders(SAFEARRAY **pRetVal)
{
	if (pRetVal == NULL)
		return E_POINTER;
	*pRetVal = NULL;
	return E_NOTIMPL;
}

STDMETHODIMP tableAcc::get_RowOrColumnMajor(RowOrColumnMajor *pRetVal)
{
	if (pRetVal == NULL)
		return E_POINTER;
	*pRetVal = RowOrColumnMajor_RowMajor;
	return S_OK;
}
#endif

void initTableAcc(struct table *t)
{
	tableAcc *a;

	a = new tableAcc(t);
	t->tableAcc = a;
}

void uninitTableAcc(struct table *t)
{
	tableAcc *a;

	a = (tableAcc *) (t->tableAcc);
	a->Invalidate();
	a->Release();
	t->tableAcc = NULL;
}

HANDLER(accessibilityHandler)
{
	if (uMsg != WM_GETOBJECT)
		return FALSE;
	// OBJID_CLIENT evaluates to an expression of type LONG
	// the documentation for WM_GETOBJECT says to cast "it" to a DWORD before comparing
	// https://msdn.microsoft.com/en-us/library/windows/desktop/dd373624%28v=vs.85%29.aspx casts them both to DWORDs; let's do that
	// its two siblings only cast lParam, resulting in an erroneous DWORD to LONG comparison
	// The Old New Thing book does not cast anything
	// Microsoft's MSAA sample casts lParam to LONG instead!
	// (As you can probably tell, the biggest problem with MSAA is that its documentation is ambiguous and/or self-contradictory...)
	// and https://msdn.microsoft.com/en-us/library/windows/desktop/ff625912%28v=vs.85%29.aspx casts them both to long!
	// Note that we're not using Active Accessibility, but the above applies even more, because UiaRootObjectId is *NEGATIVE*!
	if (((DWORD) lParam) != ((DWORD) UiaRootObjectId))
		return FALSE;
	*lResult = UiaReturnRawElementProvider(t->hwnd, wParam, lParam, (IRawElementProviderSimple *) (t->tableAcc));
	return TRUE;
}
<|endoftext|>


<commit_before>#include "SkCanvas.h"
#include "SkDocument.h"
#include "SkFontMgr.h"
#include "SkGradientShader.h"
#include "SkPaint.h"
#include "SkStream.h"
#include "SkString.h"
#include "SkTypeface.h"
#include <hb.h>
#include <hb-ft.h>
#include <cairo.h>
#include <cairo-ft.h>

struct Config {
  double page_width;
  double page_height;

  SkString title;
  SkString author;
  SkString subject;
  SkString keywords;
  SkString creator;

  std::string font_file;
  double font_size;

  Config(int argc, char **argv) :
    page_width(800.f), page_height(600.f),
    title("..."), author("..."), subject("..."), keywords("..."),
    creator("..."),
    font_file("fonts/NotoNastaliqUrdu-Regular.ttf"),
    font_size(24.0f) {
    for (int i = 1; i < argc; i++) {
      printf("Config arg[%d]=%s\n", argc, argv[i]);
      if (*argv[i] == '-') {
        char option_char = *(argv[i] + 1);
        if (i >= argc) {
          break;
        }
        const char *option_value = argv[i + 1];
        printf("\toption_char=%c\n", option_char);
        printf("\toption_value=%s\n", option_value);
        switch (option_char) {
          case 'w':
            page_width = atof(option_value);
            i++;
            break;
          case 'h':
            page_height = atof(option_value);
            i++;
            break;
          case 't':
            title = option_value;
            i++;
            break;
          case 'a':
            author = option_value;
            i++;
            break;
          case 's':
            subject = option_value;
            i++;
            break;
          case 'k':
            keywords = option_value;
            i++;
            break;
          case 'c':
            creator = option_value;
            i++;
            break;

          case 'f':
            font_file = option_value;
            i++;
            break;
          case 'z':
            font_size = atof(option_value);
            i++;
            break;
          default:
            printf("Unrecognized option: %c. Skipping.\n", option_char);
        }
      }
    }

  } // end of Config::Config
};

class Placement {
  Config config;

  FT_Library ft_library;
  FT_Face ft_face;
  hb_font_t *hb_font;
  hb_buffer_t *hb_buffer;

  const char *text;
  unsigned len;

  hb_glyph_position_t *pos;
  hb_glyph_info_t *info;

 public:
  Placement(Config &_config, const char *_text) : config(_config) {
    text = _text;
    FT_Error ft_error;

    if ((ft_error = FT_Init_FreeType (&ft_library)))
      abort();
    if ((ft_error = FT_New_Face (ft_library, config.font_file.c_str(), 0, &ft_face)))
      abort();
    if ((ft_error = FT_Set_Char_Size (ft_face, config.font_size*64,
        config.font_size*64, 0, 0)))
      abort();

    /* Create hb-ft font. */
    hb_font = hb_ft_font_create (ft_face, NULL);

    /* Create hb-buffer and populate. */
    hb_buffer = hb_buffer_create ();
    hb_buffer_add_utf8 (hb_buffer, text, -1, 0, -1);
    hb_buffer_guess_segment_properties (hb_buffer);

    /* Shape it! */
    hb_shape (hb_font, hb_buffer, NULL, 0);

    dump_hb_buffer(hb_buffer);
  } // end of Placement

  ~Placement() {
    hb_buffer_destroy (hb_buffer);
    hb_font_destroy (hb_font);

    FT_Done_Face (ft_face);
    FT_Done_FreeType (ft_library);
  }

  void DrawGlyphsUsingCairo(double margin) {
    /* Draw, using cairo. */
    double width = 2 * margin;
    double height = 2 * margin;
    for (unsigned int i = 0; i < len; i++)
    {
      width  += pos[i].x_advance / 64.;
      height -= pos[i].y_advance / 64.;
    }
    if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer)))
      height += config.font_size;
    else
      width  += config.font_size;

    /* Set up cairo surface. */
    cairo_surface_t *cairo_surface;
    cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                            ceil(width),
                            ceil(height));
    cairo_t *cr;
    cr = cairo_create (cairo_surface);
    cairo_set_source_rgba (cr, 1., 1., 1., 1.);
    cairo_paint (cr);
    cairo_set_source_rgba (cr, 0., 0., 0., 1.);
    cairo_translate (cr, margin, margin);

    /* Set up cairo font face. */
    cairo_font_face_t *cairo_face;
    cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
    cairo_set_font_face (cr, cairo_face);
    cairo_set_font_size (cr, config.font_size);

    /* Set up baseline. */
    if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer)))
    {
      cairo_font_extents_t font_extents;
      cairo_font_extents (cr, &font_extents);
      double baseline = (config.font_size - font_extents.height) * .5 + font_extents.ascent;
      cairo_translate (cr, 0, baseline);
    }
    else
    {
      cairo_translate (cr, config.font_size * .5, 0);
    }

    cairo_glyph_t *cairo_glyphs = cairo_glyph_allocate (len);
    double current_x = 0;
    double current_y = 0;
    for (unsigned int i = 0; i < len; i++)
    {
      cairo_glyphs[i].index = info[i].codepoint;
      cairo_glyphs[i].x = current_x + pos[i].x_offset / 64.;
      cairo_glyphs[i].y = -(current_y + pos[i].y_offset / 64.);
      current_x += pos[i].x_advance / 64.;
      current_y += pos[i].y_advance / 64.;
    }
    cairo_show_glyphs (cr, cairo_glyphs, len);
    cairo_glyph_free (cairo_glyphs);

    cairo_surface_write_to_png (cairo_surface, "out-cairo.png");

    cairo_font_face_destroy (cairo_face);
    cairo_destroy (cr);
    cairo_surface_destroy (cairo_surface);
  } // end of DrawGlyphsUsingCairo

  bool DrawGlyphsUsingSkia(SkWStream* outputStream) {
    sk_sp<SkDocument> pdfDocument(SkDocument::CreatePDF(outputStream));
    typedef SkDocument::Attribute Attr;
    Attr pdf_info[] = {
        Attr(SkString("Title"),    config.title),
        Attr(SkString("Author"),   config.author),
        Attr(SkString("Subject"),  config.subject),
        Attr(SkString("Keywords"), config.keywords),
        Attr(SkString("Creator"),  config.creator),
    };
    int pdf_infoCount = sizeof(pdf_info) / sizeof(pdf_info[0]);
    std::unique_ptr<SkTime::DateTime> now = GetCurrentDateTime();

    pdfDocument->setMetadata(pdf_info, pdf_infoCount, now.get(), now.get());

    int numberOfPages = 1;
    for (int page = 0; page < numberOfPages; ++page) {
      SkScalar pageWidth = SkIntToScalar(800);
      SkScalar pageHeight = SkIntToScalar(600);
      SkCanvas* pageCanvas = pdfDocument->beginPage(pageWidth, pageHeight);

      SkScalar s = SkIntToScalar(600);
      static const SkPoint     kPts0[] = { { 0, 0 }, { s, s } };
      static const SkScalar    kPos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
      static const SkColor kColors0[] = {0x80F00080, 0xF0F08000, 0x800080F0 };

      SkPaint paint;
      paint.setShader(SkGradientShader::MakeLinear(
          kPts0, kColors0, kPos, SK_ARRAY_COUNT(kColors0),
          SkShader::kClamp_TileMode));
      pageCanvas->drawPaint(paint);

      SkPaint textPaint;
      // Draw a message with a nice black paint.
      textPaint.setFlags(
          SkPaint::kAntiAlias_Flag |
          SkPaint::kSubpixelText_Flag | // ... avoid waggly text when rotating.
          SkPaint::kUnderlineText_Flag);
      textPaint.setColor(SK_ColorBLACK);
      textPaint.setTextSize(config.font_size);
      pageCanvas->save();
      pageCanvas->translate(40, 20);
      pageCanvas->rotate(45.0f);
      SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
      SkTypeface * typeface = SkTypeface::CreateFromFile(config.font_file.c_str(), 0);
      textPaint.setTypeface(typeface);

      pageCanvas->drawText(text, strlen(text), 0, 0, textPaint);

      SkPoint *skpos = (SkPoint*)malloc(sizeof(SkPoint) * len);

      double current_x = 10;
      double current_y = 50;

      SkPaint glyphPaint(textPaint);
      glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
      SkAutoSTMalloc<128, uint16_t> glyphStorage(len);
      uint16_t* glyphs = glyphStorage.get();
      for (unsigned int i = 0; i < len; i++)
      {
        glyphs[i] = info[i].codepoint;
        skpos[i] = SkPoint::Make(
          current_x + pos[i].x_offset / 64.,
          current_y - pos[i].y_offset / 64.);
        current_x += pos[i].x_advance / 64.;
        current_y += pos[i].y_advance / 64.;
      }

      pageCanvas->drawPosText(glyphs, len * sizeof(uint16_t), skpos, glyphPaint);
      pageCanvas->restore();

      free(skpos);

      pdfDocument->endPage();
    }
    return pdfDocument->close();
  } // end of DrawGlyphsUsingSkia

private:
  void dump_hb_buffer(hb_buffer_t *hb_buffer) {
    /* Get glyph information and positions out of the buffer. */
    len = hb_buffer_get_length (hb_buffer);
    info = hb_buffer_get_glyph_infos (hb_buffer, NULL);
    pos = hb_buffer_get_glyph_positions (hb_buffer, NULL);

    /* Print them out as is. */
    printf ("Raw buffer contents:\n");
    for (unsigned int i = 0; i < len; i++)
    {
      hb_codepoint_t gid   = info[i].codepoint;
      unsigned int cluster = info[i].cluster;
      double x_advance = pos[i].x_advance / 64.;
      double y_advance = pos[i].y_advance / 64.;
      double x_offset  = pos[i].x_offset / 64.;
      double y_offset  = pos[i].y_offset / 64.;

      char glyphname[32];
      hb_font_get_glyph_name (hb_font, gid, glyphname, sizeof (glyphname));

      printf ("glyph='%s' cluster=%d  advance=(%g,%g) offset=(%g,%g)\n",
              glyphname, cluster, x_advance, y_advance, x_offset, y_offset);
    }
  }

  std::unique_ptr<SkTime::DateTime> GetCurrentDateTime() {
    time_t m_time;
    time(&m_time);
    struct tm* tstruct;
    tstruct = gmtime(&m_time);

    std::unique_ptr<SkTime::DateTime> now(new SkTime::DateTime());
    now->fTimeZoneMinutes = 0;
    now->fYear       = tstruct->tm_year + 1900;
    now->fMonth      = SkToU8(tstruct->tm_mon + 1);
    now->fDayOfWeek  = SkToU8(tstruct->tm_wday);
    now->fDay        = SkToU8(tstruct->tm_mday);
    now->fHour       = SkToU8(tstruct->tm_hour);
    now->fMinute     = SkToU8(tstruct->tm_min);
    now->fSecond     = SkToU8(tstruct->tm_sec);
    return now;
  }
}; // end of Placement class

int main(int argc, char** argv) {
    Config config(argc, argv);

    SkPaint paint;
    paint.setColor(SK_ColorRED);

    SkString* str = new SkString();
    paint.toString(str);

    fprintf(stdout, "%s\n", str->c_str());

    // CreateAndPlaceGlyphs(36, 36 * .5, "fonts/TSCu_SaiIndira.ttf", "டஉடு");
    // CreateAndPlaceGlyphs(36, 36 * .5, "fonts/DejaVuSans.ttf", "حرف‌باز");
    // Placement placement("fonts/NotoNastaliqUrdu-Regular.ttf", "حرف‌باز Привет, Вася! Hello, world! டஉடு", 24 /* font_size */);
    Placement placement(config, "حرف‌باز");
    placement.DrawGlyphsUsingCairo(24 * .5 /* margin */);
    placement.DrawGlyphsUsingSkia(new SkFILEWStream("out-skiahf.pdf"));

    return 0;
}
<commit_msg>Create Config.<commit_after>#include "SkCanvas.h"
#include "SkDocument.h"
#include "SkFontMgr.h"
#include "SkGradientShader.h"
#include "SkPaint.h"
#include "SkStream.h"
#include "SkString.h"
#include "SkTypeface.h"
#include <hb.h>
#include <hb-ft.h>
#include <cairo.h>
#include <cairo-ft.h>

struct Config {
  double page_width;
  double page_height;

  SkString title;
  SkString author;
  SkString subject;
  SkString keywords;
  SkString creator;

  std::string font_file;
  double font_size;

  Config(int argc, char **argv) :
    page_width(800.f), page_height(600.f),
    title("..."), author("..."), subject("..."), keywords("..."), creator("..."),
    font_file("fonts/NotoNastaliqUrdu-Regular.ttf"), font_size(24.0f) {
    for (int i = 1; i < argc; i++) {
      printf("Config arg[%d]=%s\n", argc, argv[i]);
      if (*argv[i] == '-') {
        char option_char = *(argv[i] + 1);
        if (i >= argc) {
          break;
        }
        const char *option_value = argv[i + 1];
        printf("\toption_char=%c\n", option_char);
        printf("\toption_value=%s\n", option_value);
        switch (option_char) {
          case 'w':
            page_width = atof(option_value);
            i++;
            break;
          case 'h':
            page_height = atof(option_value);
            i++;
            break;
          case 't':
            title = option_value;
            i++;
            break;
          case 'a':
            author = option_value;
            i++;
            break;
          case 's':
            subject = option_value;
            i++;
            break;
          case 'k':
            keywords = option_value;
            i++;
            break;
          case 'c':
            creator = option_value;
            i++;
            break;

          case 'f':
            font_file = option_value;
            i++;
            break;
          case 'z':
            font_size = atof(option_value);
            i++;
            break;
          default:
            printf("Unrecognized option: %c. Skipping.\n", option_char);
        }
      }
    }

  } // end of Config::Config
};

class Placement {
  Config config;

  FT_Library ft_library;
  FT_Face ft_face;
  hb_font_t *hb_font;
  hb_buffer_t *hb_buffer;

  const char *text;
  unsigned len;

  hb_glyph_position_t *pos;
  hb_glyph_info_t *info;

 public:
  Placement(Config &_config, const char *_text) : config(_config) {
    text = _text;
    FT_Error ft_error;

    if ((ft_error = FT_Init_FreeType (&ft_library)))
      abort();
    if ((ft_error = FT_New_Face (ft_library, config.font_file.c_str(), 0, &ft_face)))
      abort();
    if ((ft_error = FT_Set_Char_Size (ft_face, config.font_size*64,
        config.font_size*64, 0, 0)))
      abort();

    /* Create hb-ft font. */
    hb_font = hb_ft_font_create (ft_face, NULL);

    /* Create hb-buffer and populate. */
    hb_buffer = hb_buffer_create ();
    hb_buffer_add_utf8 (hb_buffer, text, -1, 0, -1);
    hb_buffer_guess_segment_properties (hb_buffer);

    /* Shape it! */
    hb_shape (hb_font, hb_buffer, NULL, 0);

    dump_hb_buffer(hb_buffer);
  } // end of Placement

  ~Placement() {
    hb_buffer_destroy (hb_buffer);
    hb_font_destroy (hb_font);

    FT_Done_Face (ft_face);
    FT_Done_FreeType (ft_library);
  }

  void DrawGlyphsUsingCairo(double margin) {
    /* Draw, using cairo. */
    double width = 2 * margin;
    double height = 2 * margin;
    for (unsigned int i = 0; i < len; i++)
    {
      width  += pos[i].x_advance / 64.;
      height -= pos[i].y_advance / 64.;
    }
    if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer)))
      height += config.font_size;
    else
      width  += config.font_size;

    /* Set up cairo surface. */
    cairo_surface_t *cairo_surface;
    cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                            ceil(width),
                            ceil(height));
    cairo_t *cr;
    cr = cairo_create (cairo_surface);
    cairo_set_source_rgba (cr, 1., 1., 1., 1.);
    cairo_paint (cr);
    cairo_set_source_rgba (cr, 0., 0., 0., 1.);
    cairo_translate (cr, margin, margin);

    /* Set up cairo font face. */
    cairo_font_face_t *cairo_face;
    cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
    cairo_set_font_face (cr, cairo_face);
    cairo_set_font_size (cr, config.font_size);

    /* Set up baseline. */
    if (HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer)))
    {
      cairo_font_extents_t font_extents;
      cairo_font_extents (cr, &font_extents);
      double baseline = (config.font_size - font_extents.height) * .5 + font_extents.ascent;
      cairo_translate (cr, 0, baseline);
    }
    else
    {
      cairo_translate (cr, config.font_size * .5, 0);
    }

    cairo_glyph_t *cairo_glyphs = cairo_glyph_allocate (len);
    double current_x = 0;
    double current_y = 0;
    for (unsigned int i = 0; i < len; i++)
    {
      cairo_glyphs[i].index = info[i].codepoint;
      cairo_glyphs[i].x = current_x + pos[i].x_offset / 64.;
      cairo_glyphs[i].y = -(current_y + pos[i].y_offset / 64.);
      current_x += pos[i].x_advance / 64.;
      current_y += pos[i].y_advance / 64.;
    }
    cairo_show_glyphs (cr, cairo_glyphs, len);
    cairo_glyph_free (cairo_glyphs);

    cairo_surface_write_to_png (cairo_surface, "out-cairo.png");

    cairo_font_face_destroy (cairo_face);
    cairo_destroy (cr);
    cairo_surface_destroy (cairo_surface);
  } // end of DrawGlyphsUsingCairo

  bool DrawGlyphsUsingSkia(SkWStream* outputStream) {
    sk_sp<SkDocument> pdfDocument(SkDocument::CreatePDF(outputStream));
    typedef SkDocument::Attribute Attr;
    Attr pdf_info[] = {
        Attr(SkString("Title"),    config.title),
        Attr(SkString("Author"),   config.author),
        Attr(SkString("Subject"),  config.subject),
        Attr(SkString("Keywords"), config.keywords),
        Attr(SkString("Creator"),  config.creator),
    };
    int pdf_infoCount = sizeof(pdf_info) / sizeof(pdf_info[0]);
    std::unique_ptr<SkTime::DateTime> now = GetCurrentDateTime();

    pdfDocument->setMetadata(pdf_info, pdf_infoCount, now.get(), now.get());

    int numberOfPages = 1;
    for (int page = 0; page < numberOfPages; ++page) {
      SkScalar pageWidth = SkIntToScalar(800);
      SkScalar pageHeight = SkIntToScalar(600);
      SkCanvas* pageCanvas = pdfDocument->beginPage(pageWidth, pageHeight);

      SkScalar s = SkIntToScalar(600);
      static const SkPoint     kPts0[] = { { 0, 0 }, { s, s } };
      static const SkScalar    kPos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
      static const SkColor kColors0[] = {0x80F00080, 0xF0F08000, 0x800080F0 };

      SkPaint paint;
      paint.setShader(SkGradientShader::MakeLinear(
          kPts0, kColors0, kPos, SK_ARRAY_COUNT(kColors0),
          SkShader::kClamp_TileMode));
      pageCanvas->drawPaint(paint);

      SkPaint textPaint;
      // Draw a message with a nice black paint.
      textPaint.setFlags(
          SkPaint::kAntiAlias_Flag |
          SkPaint::kSubpixelText_Flag | // ... avoid waggly text when rotating.
          SkPaint::kUnderlineText_Flag);
      textPaint.setColor(SK_ColorBLACK);
      textPaint.setTextSize(config.font_size);
      pageCanvas->save();
      pageCanvas->translate(40, 20);
      pageCanvas->rotate(45.0f);
      SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
      SkTypeface * typeface = SkTypeface::CreateFromFile(config.font_file.c_str(), 0);
      textPaint.setTypeface(typeface);

      pageCanvas->drawText(text, strlen(text), 0, 0, textPaint);

      SkPoint *skpos = (SkPoint*)malloc(sizeof(SkPoint) * len);

      double current_x = 10;
      double current_y = 50;

      SkPaint glyphPaint(textPaint);
      glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
      SkAutoSTMalloc<128, uint16_t> glyphStorage(len);
      uint16_t* glyphs = glyphStorage.get();
      for (unsigned int i = 0; i < len; i++)
      {
        glyphs[i] = info[i].codepoint;
        skpos[i] = SkPoint::Make(
          current_x + pos[i].x_offset / 64.,
          current_y - pos[i].y_offset / 64.);
        current_x += pos[i].x_advance / 64.;
        current_y += pos[i].y_advance / 64.;
      }

      pageCanvas->drawPosText(glyphs, len * sizeof(uint16_t), skpos, glyphPaint);

      current_x += 50;
      current_y += 50;

      SkTextBlobBuilder textBlobBuilder;
      auto runBuffer = textBlobBuilder.allocRunPos(glyphPaint, len);
      for (unsigned int i = 0; i < len; i++)
      {
        runBuffer.glyphs[i] = info[i].codepoint;
        skpos[i] = SkPoint::Make(
          current_x + pos[i].x_offset / 64.,
          current_y - pos[i].y_offset / 64.);
        current_x += pos[i].x_advance / 64.;
        current_y += pos[i].y_advance / 64.;
      }

      pageCanvas->drawTextBlob(textBlobBuilder.build(), x, y, glyphPaint);

      pageCanvas->restore();

      free(skpos);

      pdfDocument->endPage();
    }
    return pdfDocument->close();
  } // end of DrawGlyphsUsingSkia

private:
  void dump_hb_buffer(hb_buffer_t *hb_buffer) {
    /* Get glyph information and positions out of the buffer. */
    len = hb_buffer_get_length (hb_buffer);
    info = hb_buffer_get_glyph_infos (hb_buffer, NULL);
    pos = hb_buffer_get_glyph_positions (hb_buffer, NULL);

    /* Print them out as is. */
    printf ("Raw buffer contents:\n");
    for (unsigned int i = 0; i < len; i++)
    {
      hb_codepoint_t gid   = info[i].codepoint;
      unsigned int cluster = info[i].cluster;
      double x_advance = pos[i].x_advance / 64.;
      double y_advance = pos[i].y_advance / 64.;
      double x_offset  = pos[i].x_offset / 64.;
      double y_offset  = pos[i].y_offset / 64.;

      char glyphname[32];
      hb_font_get_glyph_name (hb_font, gid, glyphname, sizeof (glyphname));

      printf ("glyph='%s' cluster=%d  advance=(%g,%g) offset=(%g,%g)\n",
              glyphname, cluster, x_advance, y_advance, x_offset, y_offset);
    }
  }

  std::unique_ptr<SkTime::DateTime> GetCurrentDateTime() {
    time_t m_time;
    time(&m_time);
    struct tm* tstruct;
    tstruct = gmtime(&m_time);

    std::unique_ptr<SkTime::DateTime> now(new SkTime::DateTime());
    now->fTimeZoneMinutes = 0;
    now->fYear       = tstruct->tm_year + 1900;
    now->fMonth      = SkToU8(tstruct->tm_mon + 1);
    now->fDayOfWeek  = SkToU8(tstruct->tm_wday);
    now->fDay        = SkToU8(tstruct->tm_mday);
    now->fHour       = SkToU8(tstruct->tm_hour);
    now->fMinute     = SkToU8(tstruct->tm_min);
    now->fSecond     = SkToU8(tstruct->tm_sec);
    return now;
  }
}; // end of Placement class

int main(int argc, char** argv) {
    Config config(argc, argv);

    SkPaint paint;
    paint.setColor(SK_ColorRED);

    SkString* str = new SkString();
    paint.toString(str);

    fprintf(stdout, "%s\n", str->c_str());

    // CreateAndPlaceGlyphs(36, 36 * .5, "fonts/TSCu_SaiIndira.ttf", "டஉடு");
    // CreateAndPlaceGlyphs(36, 36 * .5, "fonts/DejaVuSans.ttf", "حرف‌باز");
    // Placement placement("fonts/NotoNastaliqUrdu-Regular.ttf", "حرف‌باز Привет, Вася! Hello, world! டஉடு", 24 /* font_size */);
    Placement placement(config, "حرف‌باز");
    placement.DrawGlyphsUsingCairo(24 * .5 /* margin */);
    placement.DrawGlyphsUsingSkia(new SkFILEWStream("out-skiahf.pdf"));

    return 0;
}
<|endoftext|>


<commit_before>/*
* ----------------------------------------------------------------------------
* “THE COFFEEWARE LICENSE” (Revision 1):
* <ihsan@kehribar.me> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a coffee in return.
* -----------------------------------------------------------------------------
* This library is based on this library:
*   https://github.com/aaronds/arduino-nrf24l01
* Which is based on this library:
*   http://www.tinkerer.eu/AVRLib/nRF24L01
* -----------------------------------------------------------------------------
*/
#include "nrf24.hpp"

uint8_t payload_len;

// FIXME: correct pin definitions
typedef GpioA0 Ce;
typedef GpioA0 Csn;
typedef GpioA0 Sck;
typedef GpioA0 Mosi;
typedef GpioA0 Miso;

/* init the hardware pins */
void nrf24_init()
{
/*    - Set MISO pin input
 *    - Set MOSI pin output
 *    - Set SCK pin output
 *    - Set CSN pin output
 *    - Set CE pin output     */
    Miso::setInput();
    Mosi::setOutput(xpcc::Gpio::Low);
    Sck::setOutput(xpcc::Gpio::Low);
    Csn::setOutput(xpcc::Gpio::Low);
    Ce::setOutput(xpcc::Gpio::Low);

    Ce::reset();
    Csn::set();
}

/* configure the module */
void nrf24_config(uint8_t channel, uint8_t pay_length)
{
    /* Use static payload length ... */
    payload_len = pay_length;

    // Set RF channel
    nrf24_configRegister(RF_CH,channel);

    // Set length of incoming payload
    nrf24_configRegister(RX_PW_P0, 0x00); // Auto-ACK pipe ...
    nrf24_configRegister(RX_PW_P1, payload_len); // Data payload pipe
    nrf24_configRegister(RX_PW_P2, 0x00); // Pipe not used
    nrf24_configRegister(RX_PW_P3, 0x00); // Pipe not used
    nrf24_configRegister(RX_PW_P4, 0x00); // Pipe not used
    nrf24_configRegister(RX_PW_P5, 0x00); // Pipe not used

    // 1 Mbps, TX gain: 0dbm
    nrf24_configRegister(RF_SETUP, (0<<RF_DR)|((0x03)<<RF_PWR));

    // CRC enable, 1 byte CRC length
    nrf24_configRegister(CONFIG,nrf24_CONFIG);

    // Auto Acknowledgment
    nrf24_configRegister(EN_AA,(1<<ENAA_P0)|(1<<ENAA_P1)|(0<<ENAA_P2)|(0<<ENAA_P3)|(0<<ENAA_P4)|(0<<ENAA_P5));

    // Enable RX addresses
    nrf24_configRegister(EN_RXADDR,(1<<ERX_P0)|(1<<ERX_P1)|(0<<ERX_P2)|(0<<ERX_P3)|(0<<ERX_P4)|(0<<ERX_P5));

    // Auto retransmit delay: 1000 us and Up to 15 retransmit trials
    nrf24_configRegister(SETUP_RETR,(0x04<<ARD)|(0x0F<<ARC));

    // Dynamic length configurations: No dynamic length
    nrf24_configRegister(DYNPD,(0<<DPL_P0)|(0<<DPL_P1)|(0<<DPL_P2)|(0<<DPL_P3)|(0<<DPL_P4)|(0<<DPL_P5));

    // Start listening
    nrf24_powerUpRx();
}

/* Set the RX address */
void nrf24_rx_address(uint8_t * adr)
{
    Ce::reset();
    nrf24_writeRegister(RX_ADDR_P1,adr,nrf24_ADDR_LEN);
    Ce::set();
}

/* Returns the payload length */
uint8_t nrf24_payload_length()
{
    return payload_len;
}

/* Set the TX address */
void nrf24_tx_address(uint8_t* adr)
{
    /* RX_ADDR_P0 must be set to the sending addr for auto ack to work. */
    nrf24_writeRegister(RX_ADDR_P0,adr,nrf24_ADDR_LEN);
    nrf24_writeRegister(TX_ADDR,adr,nrf24_ADDR_LEN);
}

/* Checks if data is available for reading */
/* Returns 1 if data is ready ... */
uint8_t nrf24_dataReady()
{
    // See note in getData() function - just checking RX_DR isn't good enough
    uint8_t status = nrf24_getStatus();

    // We can short circuit on RX_DR, but if it's not set, we still need
    // to check the FIFO for any pending packets
    if ( status & (1 << RX_DR) )
    {
        return 1;
    }

    return !nrf24_rxFifoEmpty();;
}

/* Checks if receive FIFO is empty or not */
uint8_t nrf24_rxFifoEmpty()
{
    uint8_t fifoStatus;

    nrf24_readRegister(FIFO_STATUS,&fifoStatus,1);

    return (fifoStatus & (1 << RX_EMPTY));
}

/* Returns the length of data waiting in the RX fifo */
uint8_t nrf24_payloadLength()
{
    uint8_t status;
    Csn::reset();
    spi_transfer(R_RX_PL_WID);
    status = spi_transfer(0x00);
    Csn::set();
    return status;
}

/* Reads payload bytes into data array */
void nrf24_getData(uint8_t* data)
{
    /* Pull down chip select */
    Csn::reset();

    /* Send cmd to read rx payload */
    spi_transfer( R_RX_PAYLOAD );

    /* Read payload */
    nrf24_transferSync(data,data,payload_len);

    /* Pull up chip select */
    Csn::set();

    /* Reset status register */
    nrf24_configRegister(STATUS,(1<<RX_DR));
}

/* Returns the number of retransmissions occured for the last message */
uint8_t nrf24_retransmissionCount()
{
    uint8_t rv;
    nrf24_readRegister(OBSERVE_TX,&rv,1);
    rv = rv & 0x0F;
    return rv;
}

// Sends a data package to the default address. Be sure to send the correct
// amount of bytes as configured as payload on the receiver.
void nrf24_send(uint8_t* value)
{
    /* Go to Standby-I first */
    Ce::reset();

    /* Set to transmitter mode , Power up if needed */
    nrf24_powerUpTx();

    /* Do we really need to flush TX fifo each time ? */
    #if 1
        /* Pull down chip select */
        Csn::reset();

        /* Write cmd to flush transmit FIFO */
        spi_transfer(FLUSH_TX);

        /* Pull up chip select */
        Csn::set();
    #endif

    /* Pull down chip select */
    Csn::reset();

    /* Write cmd to write payload */
    spi_transfer(W_TX_PAYLOAD);

    /* Write payload */
    nrf24_transmitSync(value,payload_len);

    /* Pull up chip select */
    Csn::set();

    /* Start the transmission */
    Ce::set();
}

uint8_t nrf24_isSending()
{
    uint8_t status;

    /* read the current status */
    status = nrf24_getStatus();

    /* if sending successful (TX_DS) or max retries exceded (MAX_RT). */
    if((status & ((1 << TX_DS)  | (1 << MAX_RT))))
    {
        return 0; /* false */
    }

    return 1; /* true */

}

uint8_t nrf24_getStatus()
{
    uint8_t rv;
    Csn::reset();
    rv = spi_transfer(NOP);
    Csn::set();
    return rv;
}

uint8_t nrf24_lastMessageStatus()
{
    uint8_t rv;

    rv = nrf24_getStatus();

    /* Transmission went OK */
    if((rv & ((1 << TX_DS))))
    {
        return NRF24_TRANSMISSON_OK;
    }
    /* Maximum retransmission count is reached */
    /* Last message probably went missing ... */
    else if((rv & ((1 << MAX_RT))))
    {
        return NRF24_MESSAGE_LOST;
    }
    /* Probably still sending ... */
    else
    {
        return 0xFF;
    }
}

void nrf24_powerUpRx()
{
    Csn::reset();
    spi_transfer(FLUSH_RX);
    Csn::set();

    nrf24_configRegister(STATUS,(1<<RX_DR)|(1<<TX_DS)|(1<<MAX_RT));

    Ce::reset();
    nrf24_configRegister(CONFIG,nrf24_CONFIG|((1<<PWR_UP)|(1<<PRIM_RX)));
    Ce::set();
}

void nrf24_powerUpTx()
{
    nrf24_configRegister(STATUS,(1<<RX_DR)|(1<<TX_DS)|(1<<MAX_RT));

    nrf24_configRegister(CONFIG,nrf24_CONFIG|((1<<PWR_UP)|(0<<PRIM_RX)));
}

void nrf24_powerDown()
{
    Ce::reset();
    nrf24_configRegister(CONFIG,nrf24_CONFIG);
}

/* software spi routine */
uint8_t spi_transfer(uint8_t tx)
{
    uint8_t i = 0;
    uint8_t rx = 0;

    Sck::reset();

    for(i=0;i<8;i++)
    {

        if(tx & (1<<(7-i)))
        {
            Mosi::set();
        }
        else
        {
            Mosi::reset();
        }

        Sck::set();

        rx = rx << 1;
        if(Miso_digitalRead())
        {
            rx |= 0x01;
        }

        Sck::reset();

    }

    return rx;
}

/* send and receive multiple bytes over SPI */
void nrf24_transferSync(uint8_t* dataout,uint8_t* datain,uint8_t len)
{
    uint8_t i;

    for(i=0;i<len;i++)
    {
        datain[i] = spi_transfer(dataout[i]);
    }

}

/* send multiple bytes over SPI */
void nrf24_transmitSync(uint8_t* dataout,uint8_t len)
{
    uint8_t i;

    for(i=0;i<len;i++)
    {
        spi_transfer(dataout[i]);
    }

}

/* Clocks only one byte into the given nrf24 register */
void nrf24_configRegister(uint8_t reg, uint8_t value)
{
    Csn::reset();
    spi_transfer(W_REGISTER | (REGISTER_MASK & reg));
    spi_transfer(value);
    Csn::set();
}

/* Read single register from nrf24 */
void nrf24_readRegister(uint8_t reg, uint8_t* value, uint8_t len)
{
    Csn::reset();
    spi_transfer(R_REGISTER | (REGISTER_MASK & reg));
    nrf24_transferSync(value,value,len);
    Csn::set();
}

/* Write to a single register of nrf24 */
void nrf24_writeRegister(uint8_t reg, uint8_t* value, uint8_t len)
{
    Csn::reset();
    spi_transfer(W_REGISTER | (REGISTER_MASK & reg));
    nrf24_transmitSync(value,len);
    Csn::set();
}
<commit_msg>Nrf24: use correct namespace<commit_after>/*
* ----------------------------------------------------------------------------
* “THE COFFEEWARE LICENSE” (Revision 1):
* <ihsan@kehribar.me> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a coffee in return.
* -----------------------------------------------------------------------------
* This library is based on this library:
*   https://github.com/aaronds/arduino-nrf24l01
* Which is based on this library:
*   http://www.tinkerer.eu/AVRLib/nRF24L01
* -----------------------------------------------------------------------------
*/
#include "nrf24.hpp"

uint8_t payload_len;

// FIXME: correct pin definitions
typedef xpcc::stm32::GpioA0 Ce;
typedef xpcc::stm32::GpioA0 Csn;
typedef xpcc::stm32::GpioA0 Sck;
typedef xpcc::stm32::GpioA0 Mosi;
typedef xpcc::stm32::GpioA0 Miso;

/* init the hardware pins */
void nrf24_init()
{
/*    - Set MISO pin input
 *    - Set MOSI pin output
 *    - Set SCK pin output
 *    - Set CSN pin output
 *    - Set CE pin output     */
    Miso::setInput();
    Mosi::setOutput(xpcc::Gpio::Low);
    Sck::setOutput(xpcc::Gpio::Low);
    Csn::setOutput(xpcc::Gpio::Low);
    Ce::setOutput(xpcc::Gpio::Low);

    Ce::reset();
    Csn::set();
}

/* configure the module */
void nrf24_config(uint8_t channel, uint8_t pay_length)
{
    /* Use static payload length ... */
    payload_len = pay_length;

    // Set RF channel
    nrf24_configRegister(RF_CH,channel);

    // Set length of incoming payload
    nrf24_configRegister(RX_PW_P0, 0x00); // Auto-ACK pipe ...
    nrf24_configRegister(RX_PW_P1, payload_len); // Data payload pipe
    nrf24_configRegister(RX_PW_P2, 0x00); // Pipe not used
    nrf24_configRegister(RX_PW_P3, 0x00); // Pipe not used
    nrf24_configRegister(RX_PW_P4, 0x00); // Pipe not used
    nrf24_configRegister(RX_PW_P5, 0x00); // Pipe not used

    // 1 Mbps, TX gain: 0dbm
    nrf24_configRegister(RF_SETUP, (0<<RF_DR)|((0x03)<<RF_PWR));

    // CRC enable, 1 byte CRC length
    nrf24_configRegister(CONFIG,nrf24_CONFIG);

    // Auto Acknowledgment
    nrf24_configRegister(EN_AA,(1<<ENAA_P0)|(1<<ENAA_P1)|(0<<ENAA_P2)|(0<<ENAA_P3)|(0<<ENAA_P4)|(0<<ENAA_P5));

    // Enable RX addresses
    nrf24_configRegister(EN_RXADDR,(1<<ERX_P0)|(1<<ERX_P1)|(0<<ERX_P2)|(0<<ERX_P3)|(0<<ERX_P4)|(0<<ERX_P5));

    // Auto retransmit delay: 1000 us and Up to 15 retransmit trials
    nrf24_configRegister(SETUP_RETR,(0x04<<ARD)|(0x0F<<ARC));

    // Dynamic length configurations: No dynamic length
    nrf24_configRegister(DYNPD,(0<<DPL_P0)|(0<<DPL_P1)|(0<<DPL_P2)|(0<<DPL_P3)|(0<<DPL_P4)|(0<<DPL_P5));

    // Start listening
    nrf24_powerUpRx();
}

/* Set the RX address */
void nrf24_rx_address(uint8_t * adr)
{
    Ce::reset();
    nrf24_writeRegister(RX_ADDR_P1,adr,nrf24_ADDR_LEN);
    Ce::set();
}

/* Returns the payload length */
uint8_t nrf24_payload_length()
{
    return payload_len;
}

/* Set the TX address */
void nrf24_tx_address(uint8_t* adr)
{
    /* RX_ADDR_P0 must be set to the sending addr for auto ack to work. */
    nrf24_writeRegister(RX_ADDR_P0,adr,nrf24_ADDR_LEN);
    nrf24_writeRegister(TX_ADDR,adr,nrf24_ADDR_LEN);
}

/* Checks if data is available for reading */
/* Returns 1 if data is ready ... */
uint8_t nrf24_dataReady()
{
    // See note in getData() function - just checking RX_DR isn't good enough
    uint8_t status = nrf24_getStatus();

    // We can short circuit on RX_DR, but if it's not set, we still need
    // to check the FIFO for any pending packets
    if ( status & (1 << RX_DR) )
    {
        return 1;
    }

    return !nrf24_rxFifoEmpty();;
}

/* Checks if receive FIFO is empty or not */
uint8_t nrf24_rxFifoEmpty()
{
    uint8_t fifoStatus;

    nrf24_readRegister(FIFO_STATUS,&fifoStatus,1);

    return (fifoStatus & (1 << RX_EMPTY));
}

/* Returns the length of data waiting in the RX fifo */
uint8_t nrf24_payloadLength()
{
    uint8_t status;
    Csn::reset();
    spi_transfer(R_RX_PL_WID);
    status = spi_transfer(0x00);
    Csn::set();
    return status;
}

/* Reads payload bytes into data array */
void nrf24_getData(uint8_t* data)
{
    /* Pull down chip select */
    Csn::reset();

    /* Send cmd to read rx payload */
    spi_transfer( R_RX_PAYLOAD );

    /* Read payload */
    nrf24_transferSync(data,data,payload_len);

    /* Pull up chip select */
    Csn::set();

    /* Reset status register */
    nrf24_configRegister(STATUS,(1<<RX_DR));
}

/* Returns the number of retransmissions occured for the last message */
uint8_t nrf24_retransmissionCount()
{
    uint8_t rv;
    nrf24_readRegister(OBSERVE_TX,&rv,1);
    rv = rv & 0x0F;
    return rv;
}

// Sends a data package to the default address. Be sure to send the correct
// amount of bytes as configured as payload on the receiver.
void nrf24_send(uint8_t* value)
{
    /* Go to Standby-I first */
    Ce::reset();

    /* Set to transmitter mode , Power up if needed */
    nrf24_powerUpTx();

    /* Do we really need to flush TX fifo each time ? */
    #if 1
        /* Pull down chip select */
        Csn::reset();

        /* Write cmd to flush transmit FIFO */
        spi_transfer(FLUSH_TX);

        /* Pull up chip select */
        Csn::set();
    #endif

    /* Pull down chip select */
    Csn::reset();

    /* Write cmd to write payload */
    spi_transfer(W_TX_PAYLOAD);

    /* Write payload */
    nrf24_transmitSync(value,payload_len);

    /* Pull up chip select */
    Csn::set();

    /* Start the transmission */
    Ce::set();
}

uint8_t nrf24_isSending()
{
    uint8_t status;

    /* read the current status */
    status = nrf24_getStatus();

    /* if sending successful (TX_DS) or max retries exceded (MAX_RT). */
    if((status & ((1 << TX_DS)  | (1 << MAX_RT))))
    {
        return 0; /* false */
    }

    return 1; /* true */

}

uint8_t nrf24_getStatus()
{
    uint8_t rv;
    Csn::reset();
    rv = spi_transfer(NOP);
    Csn::set();
    return rv;
}

uint8_t nrf24_lastMessageStatus()
{
    uint8_t rv;

    rv = nrf24_getStatus();

    /* Transmission went OK */
    if((rv & ((1 << TX_DS))))
    {
        return NRF24_TRANSMISSON_OK;
    }
    /* Maximum retransmission count is reached */
    /* Last message probably went missing ... */
    else if((rv & ((1 << MAX_RT))))
    {
        return NRF24_MESSAGE_LOST;
    }
    /* Probably still sending ... */
    else
    {
        return 0xFF;
    }
}

void nrf24_powerUpRx()
{
    Csn::reset();
    spi_transfer(FLUSH_RX);
    Csn::set();

    nrf24_configRegister(STATUS,(1<<RX_DR)|(1<<TX_DS)|(1<<MAX_RT));

    Ce::reset();
    nrf24_configRegister(CONFIG,nrf24_CONFIG|((1<<PWR_UP)|(1<<PRIM_RX)));
    Ce::set();
}

void nrf24_powerUpTx()
{
    nrf24_configRegister(STATUS,(1<<RX_DR)|(1<<TX_DS)|(1<<MAX_RT));

    nrf24_configRegister(CONFIG,nrf24_CONFIG|((1<<PWR_UP)|(0<<PRIM_RX)));
}

void nrf24_powerDown()
{
    Ce::reset();
    nrf24_configRegister(CONFIG,nrf24_CONFIG);
}

/* software spi routine */
uint8_t spi_transfer(uint8_t tx)
{
    uint8_t i = 0;
    uint8_t rx = 0;

    Sck::reset();

    for(i=0;i<8;i++)
    {

        if(tx & (1<<(7-i)))
        {
            Mosi::set();
        }
        else
        {
            Mosi::reset();
        }

        Sck::set();

        rx = rx << 1;
        if(Miso::read())
        {
            rx |= 0x01;
        }

        Sck::reset();

    }

    return rx;
}

/* send and receive multiple bytes over SPI */
void nrf24_transferSync(uint8_t* dataout,uint8_t* datain,uint8_t len)
{
    uint8_t i;

    for(i=0;i<len;i++)
    {
        datain[i] = spi_transfer(dataout[i]);
    }

}

/* send multiple bytes over SPI */
void nrf24_transmitSync(uint8_t* dataout,uint8_t len)
{
    uint8_t i;

    for(i=0;i<len;i++)
    {
        spi_transfer(dataout[i]);
    }

}

/* Clocks only one byte into the given nrf24 register */
void nrf24_configRegister(uint8_t reg, uint8_t value)
{
    Csn::reset();
    spi_transfer(W_REGISTER | (REGISTER_MASK & reg));
    spi_transfer(value);
    Csn::set();
}

/* Read single register from nrf24 */
void nrf24_readRegister(uint8_t reg, uint8_t* value, uint8_t len)
{
    Csn::reset();
    spi_transfer(R_REGISTER | (REGISTER_MASK & reg));
    nrf24_transferSync(value,value,len);
    Csn::set();
}

/* Write to a single register of nrf24 */
void nrf24_writeRegister(uint8_t reg, uint8_t* value, uint8_t len)
{
    Csn::reset();
    spi_transfer(W_REGISTER | (REGISTER_MASK & reg));
    nrf24_transmitSync(value,len);
    Csn::set();
}
<|endoftext|>


<commit_before>// Copyright 2016 Alex Silva Torres
//
// 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 "runner.h"

#include <cstdlib>

#include <string>
#include <memory>
#include <iostream>
#include <fstream>
#include <readline/readline.h>
#include <boost/filesystem.hpp>

#include "env-shell.h"
#include "objects/str-object.h"
#include "utils/dir.h"

namespace seti {

Runner::Runner() {
  using namespace internal;

  internal::EnvShell::instance()->InitShell();
}

void Runner::Exec(std::string name) {
  try {
    internal::ScriptStream file(name);

    if (!file.IsOpen()) {
      throw RunTimeError(RunTimeError::ErrorCode::INTERPRETER_FILE,
                         boost::format("can't open file: %1%")%name,
                         internal::Position{0, 0});
    }

    interpreter_.Exec(file);
  } catch (RunTimeError& e) {
    std::cout << "Error: " << e.pos().line << ": " << e.pos().col
              << ": " << e.what() << "\n\n";

    for (auto& msg: e.messages()) {
      std::cout << "Error: " << msg.line() << ": " << msg.pos()
                << ": " << msg.msg() << "\n";
    }
  }
}

void Runner::ExecInterative() {
  namespace fs = boost::filesystem;

  fs::path path_rc = fs::path(internal::GetHome() + "/.setirc");

  if (fs::exists(path_rc)) {
    Exec(path_rc.string());
  }

  while (true) {
    try {
      interpreter_.ExecInterative([&](internal::Executor* exec, bool concat) {
        char *input;
        std::string str_source;
        std::string prompt;

        if (concat) {
          prompt = "| ";

          internal::ObjectPtr obj_func = interpreter_.LookupSymbol("PS2");

          if (obj_func) {
            std::vector<internal::ObjectPtr> params;
            internal::ObjectPtr obj = obj_func->Call(exec, std::move(params));

            if (obj->type() == internal::Object::ObjectType::STRING) {
              prompt = static_cast<internal::StringObject&>(*obj).value();
            }
          }
        } else {
          prompt = "> ";

          internal::ObjectPtr obj_func = interpreter_.LookupSymbol("PS1");

          if (obj_func) {
            std::vector<internal::ObjectPtr> params;
            internal::ObjectPtr obj = obj_func->Call(exec, std::move(params));

            if (obj->type() == internal::Object::ObjectType::STRING) {
              prompt = static_cast<internal::StringObject&>(*obj).value();
            }
          }
        }

        input = readline(prompt.c_str());

        if (input == nullptr) {
          exit(0);
        }

        str_source = input;
        free(input);
        return str_source;
      });
    } catch (RunTimeError& e) {
      std::cout << "Error: " << e.pos().line << ": " << e.pos().col
                << ": " << e.what() << "\n\n";

      for (auto& msg: e.messages()) {
        std::cout << "Error: " << msg.line() << ": " << msg.pos()
                  << ": " << msg.msg() << "\n";
      }
    }
  }
}

}
<commit_msg>save history of commands<commit_after>// Copyright 2016 Alex Silva Torres
//
// 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 "runner.h"

#include <cstdlib>

#include <string>
#include <memory>
#include <iostream>
#include <fstream>
#include <readline/readline.h>
#include <readline/history.h>
#include <boost/filesystem.hpp>

#include "env-shell.h"
#include "objects/str-object.h"
#include "utils/dir.h"

namespace seti {

Runner::Runner() {
  using namespace internal;

  internal::EnvShell::instance()->InitShell();
}

void Runner::Exec(std::string name) {
  try {
    internal::ScriptStream file(name);

    if (!file.IsOpen()) {
      throw RunTimeError(RunTimeError::ErrorCode::INTERPRETER_FILE,
                         boost::format("can't open file: %1%")%name,
                         internal::Position{0, 0});
    }

    interpreter_.Exec(file);
  } catch (RunTimeError& e) {
    std::cout << "Error: " << e.pos().line << ": " << e.pos().col
              << ": " << e.what() << "\n\n";

    for (auto& msg: e.messages()) {
      std::cout << "Error: " << msg.line() << ": " << msg.pos()
                << ": " << msg.msg() << "\n";
    }
  }
}

void Runner::ExecInterative() {
  namespace fs = boost::filesystem;

  fs::path path_rc = fs::path(internal::GetHome() + "/.setirc");

  if (fs::exists(path_rc)) {
    Exec(path_rc.string());
  }

  while (true) {
    try {
      interpreter_.ExecInterative([&](internal::Executor* exec, bool concat) {
        char *input;
        std::string str_source;
        std::string prompt;

        if (concat) {
          prompt = "| ";

          internal::ObjectPtr obj_func = interpreter_.LookupSymbol("PS2");

          if (obj_func) {
            std::vector<internal::ObjectPtr> params;
            internal::ObjectPtr obj = obj_func->Call(exec, std::move(params));

            if (obj->type() == internal::Object::ObjectType::STRING) {
              prompt = static_cast<internal::StringObject&>(*obj).value();
            }
          }
        } else {
          prompt = "> ";

          internal::ObjectPtr obj_func = interpreter_.LookupSymbol("PS1");

          if (obj_func) {
            std::vector<internal::ObjectPtr> params;
            internal::ObjectPtr obj = obj_func->Call(exec, std::move(params));

            if (obj->type() == internal::Object::ObjectType::STRING) {
              prompt = static_cast<internal::StringObject&>(*obj).value();
            }
          }
        }

        input = readline(prompt.c_str());

        if (input == nullptr) {
          exit(0);
        }

        str_source = input;
        add_history(input);
        free(input);
        return str_source;
      });
    } catch (RunTimeError& e) {
      std::cout << "Error: " << e.pos().line << ": " << e.pos().col
                << ": " << e.what() << "\n\n";

      for (auto& msg: e.messages()) {
        std::cout << "Error: " << msg.line() << ": " << msg.pos()
                  << ": " << msg.msg() << "\n";
      }
    }
  }
}

}
<|endoftext|>