<commit_before>#pragma once

#include <type_traits>
#include <tuple>
#include <ostream>

#include "blackhole/detail/traits/attributes/pack/feeder.hpp"
#include "blackhole/format/message/extraction.hpp"
#include "blackhole/format/message/insitu.hpp"
#include "blackhole/keyword/message.hpp"
#include "blackhole/utils/format.hpp"

namespace blackhole {

namespace aux {

namespace logger {

template<typename Log>
class pusher_t {
public:
    typedef Log logger_type;

private:
    const logger_type& log;
    record_t& record;

public:
    template<typename... Args>
    pusher_t(const logger_type& log, record_t& record, Args&&... args) :
        log(log),
        record(record)
    {
        //!@todo: Catch exceptions from message inline formatting.
        record.insert(
            keyword::message() =
                aux::format(record.attributes(), std::forward<Args>(args)...)
        );
    }

    ~pusher_t() {
        log.push(std::move(record));
    }

    /*!
     * Using initializer lists is problematic, since GCC 4.6 has an extension
     * which can deduce {{ "value", "42" }} expressions in function call to
     * the initializer list without looking at the overload resolution.
     * This behaviour violates C++11 standard, which allows such deducing only
     * for `auto` keyword (including range-based `for` loop).
     * Please, specify `attribute::list` type explicitly when using initializer
     * lists to provide attributes.
     */
    pusher_t&
    operator()(std::initializer_list<
                   std::pair<std::string, attribute::value_t>
               >&& args)
    {
        for (auto it = args.begin(); it != args.end(); ++it) {
            record.insert(
                std::make_pair(it->first, attribute_t(it->second))
            );
        }

        return *this;
    }

    template<typename... Args>
    pusher_t&
    operator()(Args&&... args) {
        static_assert(
            (is_keyword_pack<Args...>::value || is_emplace_pack<Args...>::value),
            "parameter pack must be either attribute pack or emplace pack"
        );
        typedef typename pack_determiner<Args...>::type pack_type;
        pack_feeder<pack_type>::feed(record, std::forward<Args>(args)...);

        return *this;
    }
};

template<typename Log, typename... Args>
pusher_t<Log>
make_pusher(Log& log, record_t& record, Args&&... args) {
    return pusher_t<Log>(log, record, std::forward<Args>(args)...);
}

} // namespace logger

} // namespace aux

} // namespace blackhole
<commit_msg>[Performance] Do not try to format message that has no arguments.<commit_after>#pragma once

#include <type_traits>
#include <tuple>
#include <ostream>

#include "blackhole/detail/traits/attributes/pack/feeder.hpp"
#include "blackhole/format/message/extraction.hpp"
#include "blackhole/format/message/insitu.hpp"
#include "blackhole/keyword/message.hpp"
#include "blackhole/utils/format.hpp"

namespace blackhole {

namespace aux {

namespace logger {

template<typename Log>
class pusher_t {
public:
    typedef Log logger_type;

private:
    const logger_type& log;
    record_t& record;

public:
    pusher_t(const logger_type& log, record_t& record, const char* message) :
        log(log),
        record(record)
    {
        record.insert(keyword::message() = message);
    }

    template<typename T, typename... Args>
    pusher_t(const logger_type& log, record_t& record, T&& arg, Args&&... args) :
        log(log),
        record(record)
    {
        //!@todo: Catch exceptions from message inline formatting.
        record.insert(
            keyword::message() =
                aux::format(
                    record.attributes(),
                    std::forward<T>(arg),
                    std::forward<Args>(args)...
                )
        );
    }

    ~pusher_t() {
        log.push(std::move(record));
    }

    /*!
     * Using initializer lists is problematic, since GCC 4.6 has an extension
     * which can deduce {{ "value", "42" }} expressions in function call to
     * the initializer list without looking at the overload resolution.
     * This behaviour violates C++11 standard, which allows such deducing only
     * for `auto` keyword (including range-based `for` loop).
     * Please, specify `attribute::list` type explicitly when using initializer
     * lists to provide attributes.
     */
    pusher_t&
    operator()(std::initializer_list<
                   std::pair<std::string, attribute::value_t>
               >&& args)
    {
        for (auto it = args.begin(); it != args.end(); ++it) {
            record.insert(
                std::make_pair(it->first, attribute_t(it->second))
            );
        }

        return *this;
    }

    template<typename... Args>
    pusher_t&
    operator()(Args&&... args) {
        static_assert(
            (is_keyword_pack<Args...>::value || is_emplace_pack<Args...>::value),
            "parameter pack must be either attribute pack or emplace pack"
        );
        typedef typename pack_determiner<Args...>::type pack_type;
        pack_feeder<pack_type>::feed(record, std::forward<Args>(args)...);

        return *this;
    }
};

template<typename Log, typename... Args>
pusher_t<Log>
make_pusher(Log& log, record_t& record, Args&&... args) {
    return pusher_t<Log>(log, record, std::forward<Args>(args)...);
}

} // namespace logger

} // namespace aux

} // namespace blackhole
<|endoftext|>


<commit_before>//   Copyright 2016-2020 Jean-Francois Poilpret
//
//   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.

/*
 * Simple ranger example, using VL53L0X Time-of-flight range sensor I2C device.
 * This program uses FastArduino VL53L0X support API.
 *
 * Wiring:
 * TODO (not sure)  NB: you should add pullup resistors (10K-22K typically) on both SDA and SCL lines.
 * - on ATmega328P based boards (including Arduino UNO):
 *   - A4 (PC4, SDA): connected to VL53L0X SDA pin
 *   - A5 (PC5, SCL): connected to VL53L0X SCL pin
 *   - direct USB access
 */

#include <fastarduino/time.h>
#include <fastarduino/uart.h>
#include <fastarduino/i2c_handler.h>
#include <fastarduino/i2c_debug.h>
#include <fastarduino/i2c_status.h>
#include <fastarduino/devices/vl53l0x.h>

#define DEBUG_I2C
#define FORCE_SYNC

static constexpr const board::USART UART = board::USART::USART0;
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 64;
static char output_buffer[OUTPUT_BUFFER_SIZE];

REGISTER_UATX_ISR(0)

#ifdef DEBUG_I2C
static constexpr const uint8_t DEBUG_SIZE = 96;
#	if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
using DEBUGGER = i2c::debug::I2CDebugStatusRecorder<DEBUG_SIZE, DEBUG_SIZE>;
#define DEBUG(OUT) debugger.trace(OUT)
using MANAGER = i2c::I2CAsyncStatusDebugManager<
	i2c::I2CMode::FAST, i2c::I2CErrorPolicy::CLEAR_ALL_COMMANDS, DEBUGGER&, DEBUGGER&>;
static MANAGER::I2CCOMMAND i2c_buffer[I2C_BUFFER_SIZE];
#	else
using DEBUGGER = i2c::debug::I2CDebugStatusLiveLogger;
#define DEBUG(OUT)
using MANAGER = i2c::I2CSyncStatusDebugManager<i2c::I2CMode::FAST, DEBUGGER&, DEBUGGER&>;
#	endif
#define RESET_DEBUG() debugger.reset()

#else

#	if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
using MANAGER = i2c::I2CAsyncManager<
	i2c::I2CMode::FAST, i2c::I2CErrorPolicy::CLEAR_ALL_COMMANDS>;
static MANAGER::I2CCOMMAND i2c_buffer[I2C_BUFFER_SIZE];
#	else
using MANAGER = i2c::I2CSyncManager<i2c::I2CMode::FAST>;
#	endif
#define DEBUG(OUT)
#define RESET_DEBUG()

#endif

#if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
REGISTER_I2C_ISR(MANAGER)
#endif

using streams::dec;
using streams::hex;
using streams::fixed;
using streams::endl;
using streams::flush;

using namespace devices::vl53l0x;
using TOF = VL53L0X<MANAGER>;

void trace(streams::ostream& out, SequenceSteps steps)
{
	out << F("TCC=") << steps.is_tcc()
		<< F(", DSS=") << steps.is_dss()
		<< F(", MSRC=") << steps.is_msrc()
		<< F(", PRE_RANGE=") << steps.is_pre_range()
		<< F(", FINAL_RANGE=") << steps.is_final_range() << endl;
}

int main() __attribute__((OS_main));
int main()
{
	board::init();
	sei();

	// open UART for traces
	serial::hard::UATX<UART> uart{output_buffer};
	streams::ostream out = uart.out();
	uart.begin(115200);
	out << streams::boolalpha;
	out << F("Start") << endl;

	// Initialize I2C async handler
#ifdef DEBUG_I2C
#	if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
	DEBUGGER debugger;
#	else
	DEBUGGER debugger{out};
#	endif
#endif

#if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
#	ifdef DEBUG_I2C
	MANAGER manager{i2c_buffer, debugger, debugger};
#	else
	MANAGER manager{i2c_buffer};
#	endif
#else
#	ifdef DEBUG_I2C
	MANAGER manager{debugger, debugger};
#	else
	MANAGER manager;
#	endif
#endif

	out << F("Instantiate VL53L0X") << endl;
	TOF tof{manager};

	out << F("Start I2C manager") << endl;
	manager.begin();

	out << F("Read VL53L0X status") << endl;
	uint8_t result = 0;
	bool ok = tof.get_revision(result);
	out << F("tof.get_revision(result) = ") << ok << F(", result = ") << hex << result << endl;
	DEBUG(out);

	ok = tof.get_model(result);
	out << F("tof.get_model(result) = ") << ok << F(", result = ") << hex << result << endl;
	DEBUG(out);

	PowerMode mode = PowerMode::STANDBY;
	ok = tof.get_power_mode(mode);
	out << F("tof.get_power_mode(mode) = ") << ok << F(", mode = ") << dec << uint8_t(mode) << endl;
	DEBUG(out);

	DeviceStatus status;
	ok = tof.get_range_status(status);
	out << F("tof.get_range_status(status) = ") << ok
		<< F(", error = ") << dec << uint8_t(status.error())
		<< F(", data_ready = ") << status.data_ready() << endl;
	DEBUG(out);

	SequenceSteps steps1;
	ok = tof.get_sequence_steps(steps1);
	out << F("tof.get_sequence_steps(status) = ") << ok << F(", steps =") << flush;
	trace(out, steps1);
	DEBUG(out);

	constexpr SequenceSteps steps2 = SequenceSteps::create().tcc().pre_range().final_range();
	out << F("steps2 = ") << hex << steps2.value() << endl;
	ok = tof.set_sequence_steps(steps2);
	out << F("tof.get_sequence_steps(status) = ") << ok << endl;
	DEBUG(out);

	SequenceSteps steps3;
	ok = tof.get_sequence_steps(steps3);
	out << F("tof.get_sequence_steps(status) = ") << ok << F(", steps =") << flush;
	trace(out, steps3);
	DEBUG(out);

	uint8_t period = 0;
	ok = tof.get_vcsel_pulse_period<VcselPeriodType::PRE_RANGE>(period);
	out << F("tof.get_vcsel_pulse_period<PRE_RANGE>(period) = ") << ok << F(", period = ") << dec << period << endl;
	ok = tof.get_vcsel_pulse_period<VcselPeriodType::FINAL_RANGE>(period);
	out << F("tof.get_vcsel_pulse_period<FINAL_RANGE>(period) = ") << ok << F(", period = ") << dec << period << endl;
	DEBUG(out);

	// The following block adds 4KB to program size (float arithmetic libs)
	// float signal_rate = 0.0;
	// ok = tof.get_signal_rate_limit(signal_rate);
	// out << F("tof.get_signal_rate_limit(signal_rate) = ") << ok << F(", signal_rate = ")
	// 	<< fixed << signal_rate << endl;
	// ok = tof.set_signal_rate_limit(0.5f);
	// out << F("tof.set_signal_rate_limit(0.5) = ") << ok << endl;
	// ok = tof.get_signal_rate_limit(signal_rate);
	// out << F("tof.get_signal_rate_limit(signal_rate) = ") << ok << F(", signal_rate = ")
	// 	<< fixed << signal_rate << endl;
	// DEBUG(out);

	// Call first initialization step
	out << F("Calling init_data_first()...") << endl;
	TOF::InitDataFuture future{};
	int error = tof.init_data_first(future);
	out << F("tof.init_data_first(future) = ") << dec << error << endl;
	time::delay_ms(100);
	DEBUG(out);
	out << F("future.status() = ") << future.status() << endl;

	manager.end();
}
<commit_msg>Fix compile error in async I2C mode<commit_after>//   Copyright 2016-2020 Jean-Francois Poilpret
//
//   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.

/*
 * Simple ranger example, using VL53L0X Time-of-flight range sensor I2C device.
 * This program uses FastArduino VL53L0X support API.
 *
 * Wiring:
 * - on ATmega328P based boards (including Arduino UNO):
 *   - A4 (PC4, SDA): connected to VL53L0X SDA pin
 *   - A5 (PC5, SCL): connected to VL53L0X SCL pin
 *   - direct USB access
 */

#include <fastarduino/time.h>
#include <fastarduino/uart.h>
#include <fastarduino/i2c_handler.h>
#include <fastarduino/i2c_debug.h>
#include <fastarduino/i2c_status.h>
#include <fastarduino/devices/vl53l0x.h>

#define DEBUG_I2C
// #define FORCE_SYNC

static constexpr const board::USART UART = board::USART::USART0;
static constexpr const uint8_t OUTPUT_BUFFER_SIZE = 64;
static char output_buffer[OUTPUT_BUFFER_SIZE];

REGISTER_UATX_ISR(0)

#ifdef DEBUG_I2C
static constexpr const uint8_t DEBUG_SIZE = 96;
#	if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
using DEBUGGER = i2c::debug::I2CDebugStatusRecorder<DEBUG_SIZE, DEBUG_SIZE>;
#define DEBUG(OUT) debugger.trace(OUT)
#define RESET_DEBUG() debugger.reset()
using MANAGER = i2c::I2CAsyncStatusDebugManager<
	i2c::I2CMode::FAST, i2c::I2CErrorPolicy::CLEAR_ALL_COMMANDS, DEBUGGER&, DEBUGGER&>;
static constexpr const uint8_t I2C_BUFFER_SIZE = 32;
static MANAGER::I2CCOMMAND i2c_buffer[I2C_BUFFER_SIZE];
#	else
using DEBUGGER = i2c::debug::I2CDebugStatusLiveLogger;
#define DEBUG(OUT)
#define RESET_DEBUG()
using MANAGER = i2c::I2CSyncStatusDebugManager<i2c::I2CMode::FAST, DEBUGGER&, DEBUGGER&>;
#	endif

#else

#	if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
using MANAGER = i2c::I2CAsyncManager<
	i2c::I2CMode::FAST, i2c::I2CErrorPolicy::CLEAR_ALL_COMMANDS>;
static constexpr const uint8_t I2C_BUFFER_SIZE = 32;
static MANAGER::I2CCOMMAND i2c_buffer[I2C_BUFFER_SIZE];
#	else
using MANAGER = i2c::I2CSyncManager<i2c::I2CMode::FAST>;
#	endif
#define DEBUG(OUT)
#define RESET_DEBUG()

#endif

#if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
REGISTER_I2C_ISR(MANAGER)
#endif

using streams::dec;
using streams::hex;
using streams::fixed;
using streams::endl;
using streams::flush;

using namespace devices::vl53l0x;
using TOF = VL53L0X<MANAGER>;

void trace(streams::ostream& out, SequenceSteps steps)
{
	out << F("TCC=") << steps.is_tcc()
		<< F(", DSS=") << steps.is_dss()
		<< F(", MSRC=") << steps.is_msrc()
		<< F(", PRE_RANGE=") << steps.is_pre_range()
		<< F(", FINAL_RANGE=") << steps.is_final_range() << endl;
}

int main() __attribute__((OS_main));
int main()
{
	board::init();
	sei();

	// open UART for traces
	serial::hard::UATX<UART> uart{output_buffer};
	streams::ostream out = uart.out();
	uart.begin(115200);
	out << streams::boolalpha;
	out << F("Start") << endl;

	// Initialize I2C async handler
#ifdef DEBUG_I2C
#	if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
	DEBUGGER debugger;
#	else
	DEBUGGER debugger{out};
#	endif
#endif

#if I2C_TRUE_ASYNC and not defined(FORCE_SYNC)
#	ifdef DEBUG_I2C
	MANAGER manager{i2c_buffer, debugger, debugger};
#	else
	MANAGER manager{i2c_buffer};
#	endif
#else
#	ifdef DEBUG_I2C
	MANAGER manager{debugger, debugger};
#	else
	MANAGER manager;
#	endif
#endif

	out << F("Instantiate VL53L0X") << endl;
	TOF tof{manager};

	out << F("Start I2C manager") << endl;
	manager.begin();

	out << F("Read VL53L0X status") << endl;
	uint8_t result = 0;
	bool ok = tof.get_revision(result);
	out << F("tof.get_revision(result) = ") << ok << F(", result = ") << hex << result << endl;
	DEBUG(out);

	ok = tof.get_model(result);
	out << F("tof.get_model(result) = ") << ok << F(", result = ") << hex << result << endl;
	DEBUG(out);

	PowerMode mode = PowerMode::STANDBY;
	ok = tof.get_power_mode(mode);
	out << F("tof.get_power_mode(mode) = ") << ok << F(", mode = ") << dec << uint8_t(mode) << endl;
	DEBUG(out);

	DeviceStatus status;
	ok = tof.get_range_status(status);
	out << F("tof.get_range_status(status) = ") << ok
		<< F(", error = ") << dec << uint8_t(status.error())
		<< F(", data_ready = ") << status.data_ready() << endl;
	DEBUG(out);

	SequenceSteps steps1;
	ok = tof.get_sequence_steps(steps1);
	out << F("tof.get_sequence_steps(status) = ") << ok << F(", steps =") << flush;
	trace(out, steps1);
	DEBUG(out);

	constexpr SequenceSteps steps2 = SequenceSteps::create().tcc().pre_range().final_range();
	out << F("steps2 = ") << hex << steps2.value() << endl;
	ok = tof.set_sequence_steps(steps2);
	out << F("tof.get_sequence_steps(status) = ") << ok << endl;
	DEBUG(out);

	SequenceSteps steps3;
	ok = tof.get_sequence_steps(steps3);
	out << F("tof.get_sequence_steps(status) = ") << ok << F(", steps =") << flush;
	trace(out, steps3);
	DEBUG(out);

	uint8_t period = 0;
	ok = tof.get_vcsel_pulse_period<VcselPeriodType::PRE_RANGE>(period);
	out << F("tof.get_vcsel_pulse_period<PRE_RANGE>(period) = ") << ok << F(", period = ") << dec << period << endl;
	ok = tof.get_vcsel_pulse_period<VcselPeriodType::FINAL_RANGE>(period);
	out << F("tof.get_vcsel_pulse_period<FINAL_RANGE>(period) = ") << ok << F(", period = ") << dec << period << endl;
	DEBUG(out);

	// The following block adds 4KB to program size (float arithmetic libs)
	// float signal_rate = 0.0;
	// ok = tof.get_signal_rate_limit(signal_rate);
	// out << F("tof.get_signal_rate_limit(signal_rate) = ") << ok << F(", signal_rate = ")
	// 	<< fixed << signal_rate << endl;
	// ok = tof.set_signal_rate_limit(0.5f);
	// out << F("tof.set_signal_rate_limit(0.5) = ") << ok << endl;
	// ok = tof.get_signal_rate_limit(signal_rate);
	// out << F("tof.get_signal_rate_limit(signal_rate) = ") << ok << F(", signal_rate = ")
	// 	<< fixed << signal_rate << endl;
	// DEBUG(out);

	// Call first initialization step
	out << F("Calling init_data_first()...") << endl;
	TOF::InitDataFuture future{};
	int error = tof.init_data_first(future);
	out << F("tof.init_data_first(future) = ") << dec << error << endl;
	time::delay_ms(100);
	DEBUG(out);
	out << F("future.status() = ") << future.status() << endl;

	ok = tof.get_range_status(status);
	out << F("tof.get_range_status(status) = ") << ok
		<< F(", error = ") << dec << uint8_t(status.error())
		<< F(", data_ready = ") << status.data_ready() << endl;
	DEBUG(out);

	manager.end();
}
<|endoftext|>


<commit_before>#include "common.h"
#include <iostream>
#include <vector>
#include <memory>
#include "LuaBinding.h"

// Up casting example.
// Needed when you need to call methods or functions expecting
// a base class with a derived class. You might do this when
// you need to process different derived classes in the same way,
// for example rendering different GUI elements in a list.
// Maintains correct refcounts in the shared pointers.

using namespace ManualBind;
using std::cout;
using std::endl;
using std::hex;
using std::dec;

// Abstract base / interface (doesn't have to be abstract)
class Renderable
{
public:
    virtual bool Render() = 0;
};

class Square: public Renderable
{
public:
    bool Render() {
        cout << hex << this << dec << " - I'm a Square!" << endl;
        return true;
    }
};

class Circle: public Renderable
{
public:
    bool Render() {
        cout << hex << this << dec << " - I'm a Circle!" << endl;
        return true;
    }
};

using RenderablePtr = std::shared_ptr<Renderable>;
using SquarePtr = std::shared_ptr<Square>;
using CirclePtr = std::shared_ptr<Circle>;
using RenderList = std::vector<RenderablePtr>;

void RenderAll( const RenderList& rl )
{
    cout << "Rendering list:" << endl;

    for( const auto& obj : rl )
    {
        obj->Render();
    }
}

void AddObject( RenderList& rl, RenderablePtr& rp )
{
    rl.push_back( rp );
}

/****************************** Bindings ***************************/

struct RenderableBinding: public Binding<RenderableBinding, Renderable> {

    static constexpr const char* class_name = "Renderable";

    static luaL_Reg* members()
    {
        return nullptr;
    }

    static bind_properties* properties()
    {
        return nullptr;
    }

    static int create( lua_State *L )
    {
        return luaL_error( L, "Can not create an instance of an abstract class." );
    }
};

struct SquareBinding: public Binding<SquareBinding, Square> {

    static constexpr const char* class_name = "Square";

    static luaL_Reg* members()
    {
        static luaL_Reg members[] = {
            { "__upcast", upcast },
            { NULL, NULL }
        };
        return members;
    }

    static bind_properties* properties()
    {
        return nullptr;
    }

    static int create( lua_State *L )
    {
        SquarePtr p = std::make_shared<Square>();
        push( L, p );
        return 1;
    }

    static int upcast( lua_State *L )
    {
        SquarePtr p = fromStack( L, 1 );

        RenderablePtr rp = std::dynamic_pointer_cast<Renderable>( p );

        RenderableBinding::push( L, rp );

        return 1;
    }
};

struct CircleBinding: public Binding<CircleBinding, Circle> {

    static constexpr const char* class_name = "Circle";

    static luaL_Reg* members()
    {
        static luaL_Reg members[] = {
            { "__upcast", upcast },
            { NULL, NULL }
        };
        return members;
    }

    static bind_properties* properties()
    {
        return nullptr;
    }

    static int create( lua_State *L )
    {
        CirclePtr p = std::make_shared<Circle>();
        push( L, p );
        return 1;
    }

    static int upcast( lua_State *L )
    {
        CirclePtr p = fromStack( L, 1 );

        RenderablePtr rp = std::dynamic_pointer_cast<Renderable>( p );

        RenderableBinding::push( L, rp );

        return 1;
    }
};

// Lua render list operations on a global list...
// Normally the list would not be global but in the interest
// of making this example smaller...

RenderList gRenderList;

int lua_render_all( lua_State* L )
{
    RenderAll( gRenderList );
    return 0;
}

int lua_add_object( lua_State* L )
{
    // We are expecting an object with an __upcast function
    // returning a Renderable.
    LuaBindingUpCast( L, 1 );

    // Here you could check for nil, meaning the upcast failed.

    RenderablePtr rp = RenderableBinding::fromStack( L, 1 );

    AddObject( gRenderList, rp );

    return 0;
}

int main()
{
    lua_State* L = luaL_newstate();
    luaL_openlibs( L );

    RenderableBinding::register_class( L );
    SquareBinding::register_class( L );
    CircleBinding::register_class( L );

    lua_register( L, "add", lua_add_object );
    lua_register( L, "render", lua_render_all );

    run( L, "print 'Upcast Example'" );
    run( L, "add( Square() )" );
    run( L, "add( Circle() )" );
    run( L, "add( Circle() )" );
    run( L, "add( Square() )" );
    run( L, "render()" );

    lua_close(L);

    return 0;
}
<commit_msg>Add clearer explaination<commit_after>#include "common.h"
#include <iostream>
#include <vector>
#include <memory>
#include "LuaBinding.h"

// Up casting example.
// Needed when you need to call methods or functions expecting
// a base class when you have a derived class. You might do this when
// you need to process different derived classes in the same way,
// for example rendering different GUI elements in a list.
// Maintains correct refcounts in the shared pointers.

using namespace ManualBind;
using std::cout;
using std::endl;
using std::hex;
using std::dec;

// base / interface (doesn't have to be abstract)
class Renderable
{
public:
    virtual bool Render() = 0;
};

class Square: public Renderable
{
public:
    bool Render() {
        cout << hex << this << dec << " - I'm a Square!" << endl;
        return true;
    }
};

class Circle: public Renderable
{
public:
    bool Render() {
        cout << hex << this << dec << " - I'm a Circle!" << endl;
        return true;
    }
};

using RenderablePtr = std::shared_ptr<Renderable>;
using SquarePtr = std::shared_ptr<Square>;
using CirclePtr = std::shared_ptr<Circle>;
using RenderList = std::vector<RenderablePtr>;

void RenderAll( const RenderList& rl )
{
    cout << "Rendering list:" << endl;

    for( const auto& obj : rl )
    {
        obj->Render();
    }
}

void AddObject( RenderList& rl, RenderablePtr& rp )
{
    rl.push_back( rp );
}

/****************************** Bindings ***************************/

struct RenderableBinding: public Binding<RenderableBinding, Renderable> {

    static constexpr const char* class_name = "Renderable";

    static luaL_Reg* members()
    {
        return nullptr;
    }

    static bind_properties* properties()
    {
        return nullptr;
    }

    static int create( lua_State *L )
    {
        return luaL_error( L, "Can not create an instance of an abstract class." );
    }
};

struct SquareBinding: public Binding<SquareBinding, Square> {

    static constexpr const char* class_name = "Square";

    static luaL_Reg* members()
    {
        static luaL_Reg members[] = {
            { "__upcast", upcast },
            { NULL, NULL }
        };
        return members;
    }

    static bind_properties* properties()
    {
        return nullptr;
    }

    static int create( lua_State *L )
    {
        SquarePtr p = std::make_shared<Square>();
        push( L, p );
        return 1;
    }

    static int upcast( lua_State *L )
    {
        SquarePtr p = fromStack( L, 1 );

        RenderablePtr rp = std::dynamic_pointer_cast<Renderable>( p );

        RenderableBinding::push( L, rp );

        return 1;
    }
};

struct CircleBinding: public Binding<CircleBinding, Circle> {

    static constexpr const char* class_name = "Circle";

    static luaL_Reg* members()
    {
        static luaL_Reg members[] = {
            { "__upcast", upcast },
            { NULL, NULL }
        };
        return members;
    }

    static bind_properties* properties()
    {
        return nullptr;
    }

    static int create( lua_State *L )
    {
        CirclePtr p = std::make_shared<Circle>();
        push( L, p );
        return 1;
    }

    static int upcast( lua_State *L )
    {
        CirclePtr p = fromStack( L, 1 );

        RenderablePtr rp = std::dynamic_pointer_cast<Renderable>( p );

        RenderableBinding::push( L, rp );

        return 1;
    }
};

// Lua render list operations on a global list...
// Normally the list would not be global but in the interest
// of making this example smaller...

RenderList gRenderList;

int lua_render_all( lua_State* L )
{
    RenderAll( gRenderList );
    return 0;
}

int lua_add_object( lua_State* L )
{
    // Before upcast'ing, you may want to check if the
    // class is the type you want already.
    // In this example the base is abstract so it will
    // not happen.

    // We are expecting an object with an __upcast function
    // returning a Renderable.
    LuaBindingUpCast( L, 1 );

    // Here you could check for nil, meaning the upcast failed.

    RenderablePtr rp = RenderableBinding::fromStack( L, 1 );

    AddObject( gRenderList, rp );

    return 0;
}

int main()
{
    lua_State* L = luaL_newstate();
    luaL_openlibs( L );

    RenderableBinding::register_class( L );
    SquareBinding::register_class( L );
    CircleBinding::register_class( L );

    lua_register( L, "add", lua_add_object );
    lua_register( L, "render", lua_render_all );

    run( L, "print 'Upcast Example'" );
    run( L, "add( Square() )" );
    run( L, "add( Circle() )" );
    run( L, "add( Circle() )" );
    run( L, "add( Square() )" );
    run( L, "render()" );

    lua_close(L);

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrBitmapTextGeoProc.h"

#include "GrTexture.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryProcessor.h"
#include "glsl/GrGLSLProgramDataManager.h"
#include "glsl/GrGLSLUniformHandler.h"
#include "glsl/GrGLSLVarying.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"

class GrGLBitmapTextGeoProc : public GrGLSLGeometryProcessor {
public:
    GrGLBitmapTextGeoProc() : fColor(GrColor_ILLEGAL) {}

    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
        const GrBitmapTextGeoProc& cte = args.fGP.cast<GrBitmapTextGeoProc>();

        GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
        GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
        GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

        // emit attributes
        varyingHandler->emitAttributes(cte);

        // compute numbers to be hardcoded to convert texture coordinates from int to float
        SkASSERT(cte.numTextureSamplers() == 1);
        SkDEBUGCODE(GrTexture* atlas = cte.textureSampler(0).peekTexture());
        SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));

        GrGLSLVertToFrag v(kVec2f_GrSLType);
        varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision);
        vertBuilder->codeAppendf("%s = %s;", v.vsOut(),
                                 cte.inTextureCoords()->fName);

        GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
        // Setup pass through color
        if (cte.hasVertexColor()) {
            varyingHandler->addPassThroughAttribute(cte.inColor(), args.fOutputColor);
        } else {
            this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
                                    &fColorUniform);
        }

        // Setup position
        this->setupPosition(vertBuilder, gpArgs, cte.inPosition()->fName);

        // emit transforms
        this->emitTransforms(vertBuilder,
                             varyingHandler,
                             uniformHandler,
                             gpArgs->fPositionVar,
                             cte.inPosition()->fName,
                             cte.localMatrix(),
                             args.fFPCoordTransformHandler);

        if (cte.maskFormat() == kARGB_GrMaskFormat) {
            fragBuilder->codeAppendf("%s = ", args.fOutputColor);
            fragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
                                                        args.fTexSamplers[0],
                                                        v.fsIn(),
                                                        kVec2f_GrSLType);
            fragBuilder->codeAppend(";");
            fragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
        } else {
            fragBuilder->codeAppendf("%s = ", args.fOutputCoverage);
            fragBuilder->appendTextureLookup(args.fTexSamplers[0], v.fsIn(), kVec2f_GrSLType);
            fragBuilder->codeAppend(";");
        }
    }

    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
                 FPCoordTransformIter&& transformIter) override {
        const GrBitmapTextGeoProc& btgp = gp.cast<GrBitmapTextGeoProc>();
        if (btgp.color() != fColor && !btgp.hasVertexColor()) {
            float c[4];
            GrColorToRGBAFloat(btgp.color(), c);
            pdman.set4fv(fColorUniform, 1, c);
            fColor = btgp.color();
        }
        this->setTransformDataHelper(btgp.localMatrix(), pdman, &transformIter);
    }

    static inline void GenKey(const GrGeometryProcessor& proc,
                              const GrShaderCaps&,
                              GrProcessorKeyBuilder* b) {
        const GrBitmapTextGeoProc& gp = proc.cast<GrBitmapTextGeoProc>();
        uint32_t key = 0;
        key |= (gp.usesLocalCoords() && gp.localMatrix().hasPerspective()) ? 0x1 : 0x0;
        key |= gp.maskFormat() << 1;
        b->add32(key);

        // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
        SkASSERT(gp.numTextureSamplers() == 1);
        GrTextureProxy* atlas = gp.textureSampler(0).proxy();
        if (atlas) {
            b->add32(atlas->width());
            b->add32(atlas->height());
        }
    }

private:
    GrColor fColor;
    UniformHandle fColorUniform;

    typedef GrGLSLGeometryProcessor INHERITED;
};

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

GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color,
                                         sk_sp<GrTextureProxy> proxy,
                                         const GrSamplerParams& params, GrMaskFormat format,
                                         const SkMatrix& localMatrix, bool usesLocalCoords)
    : fColor(color)
    , fLocalMatrix(localMatrix)
    , fUsesLocalCoords(usesLocalCoords)
    , fTextureSampler(std::move(proxy), params)
    , fInColor(nullptr)
    , fMaskFormat(format) {
    this->initClassID<GrBitmapTextGeoProc>();
    fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);

    bool hasVertexColor = kA8_GrMaskFormat == fMaskFormat ||
                          kA565_GrMaskFormat == fMaskFormat;
    if (hasVertexColor) {
        fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
    }
    fInTextureCoords = &this->addVertexAttrib("inTextureCoords",  kVec2us_GrVertexAttribType,
                                              kHigh_GrSLPrecision);
    this->addTextureSampler(&fTextureSampler);
}

void GrBitmapTextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps,
                                              GrProcessorKeyBuilder* b) const {
    GrGLBitmapTextGeoProc::GenKey(*this, caps, b);
}

GrGLSLPrimitiveProcessor* GrBitmapTextGeoProc::createGLSLInstance(const GrShaderCaps& caps) const {
    return new GrGLBitmapTextGeoProc();
}

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

GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrBitmapTextGeoProc);

#if GR_TEST_UTILS
sk_sp<GrGeometryProcessor> GrBitmapTextGeoProc::TestCreate(GrProcessorTestData* d) {
    int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
                                        : GrProcessorUnitTest::kAlphaTextureIdx;
    sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);

    static const SkShader::TileMode kTileModes[] = {
        SkShader::kClamp_TileMode,
        SkShader::kRepeat_TileMode,
        SkShader::kMirror_TileMode,
    };
    SkShader::TileMode tileModes[] = {
        kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
        kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
    };
    GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode
                                                             : GrSamplerParams::kNone_FilterMode);

    GrMaskFormat format = kARGB_GrMaskFormat; // init to avoid warning
    switch (d->fRandom->nextULessThan(3)) {
        case 0:
            format = kA8_GrMaskFormat;
            break;
        case 1:
            format = kA565_GrMaskFormat;
            break;
        case 2:
            format = kARGB_GrMaskFormat;
            break;
    }

    return GrBitmapTextGeoProc::Make(GrRandomColor(d->fRandom), std::move(proxy),
                                     params, format, GrTest::TestMatrix(d->fRandom),
                                     d->fRandom->nextBool());
}
#endif
<commit_msg>Make bmp text position attribute be highp<commit_after>/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrBitmapTextGeoProc.h"

#include "GrTexture.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLGeometryProcessor.h"
#include "glsl/GrGLSLProgramDataManager.h"
#include "glsl/GrGLSLUniformHandler.h"
#include "glsl/GrGLSLVarying.h"
#include "glsl/GrGLSLVertexShaderBuilder.h"

class GrGLBitmapTextGeoProc : public GrGLSLGeometryProcessor {
public:
    GrGLBitmapTextGeoProc() : fColor(GrColor_ILLEGAL) {}

    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
        const GrBitmapTextGeoProc& cte = args.fGP.cast<GrBitmapTextGeoProc>();

        GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
        GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
        GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

        // emit attributes
        varyingHandler->emitAttributes(cte);

        // compute numbers to be hardcoded to convert texture coordinates from int to float
        SkASSERT(cte.numTextureSamplers() == 1);
        SkDEBUGCODE(GrTexture* atlas = cte.textureSampler(0).peekTexture());
        SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height()));

        GrGLSLVertToFrag v(kVec2f_GrSLType);
        varyingHandler->addVarying("TextureCoords", &v, kHigh_GrSLPrecision);
        vertBuilder->codeAppendf("%s = %s;", v.vsOut(),
                                 cte.inTextureCoords()->fName);

        GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
        // Setup pass through color
        if (cte.hasVertexColor()) {
            varyingHandler->addPassThroughAttribute(cte.inColor(), args.fOutputColor);
        } else {
            this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
                                    &fColorUniform);
        }

        // Setup position
        this->setupPosition(vertBuilder, gpArgs, cte.inPosition()->fName);

        // emit transforms
        this->emitTransforms(vertBuilder,
                             varyingHandler,
                             uniformHandler,
                             gpArgs->fPositionVar,
                             cte.inPosition()->fName,
                             cte.localMatrix(),
                             args.fFPCoordTransformHandler);

        if (cte.maskFormat() == kARGB_GrMaskFormat) {
            fragBuilder->codeAppendf("%s = ", args.fOutputColor);
            fragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
                                                        args.fTexSamplers[0],
                                                        v.fsIn(),
                                                        kVec2f_GrSLType);
            fragBuilder->codeAppend(";");
            fragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
        } else {
            fragBuilder->codeAppendf("%s = ", args.fOutputCoverage);
            fragBuilder->appendTextureLookup(args.fTexSamplers[0], v.fsIn(), kVec2f_GrSLType);
            fragBuilder->codeAppend(";");
        }
    }

    void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
                 FPCoordTransformIter&& transformIter) override {
        const GrBitmapTextGeoProc& btgp = gp.cast<GrBitmapTextGeoProc>();
        if (btgp.color() != fColor && !btgp.hasVertexColor()) {
            float c[4];
            GrColorToRGBAFloat(btgp.color(), c);
            pdman.set4fv(fColorUniform, 1, c);
            fColor = btgp.color();
        }
        this->setTransformDataHelper(btgp.localMatrix(), pdman, &transformIter);
    }

    static inline void GenKey(const GrGeometryProcessor& proc,
                              const GrShaderCaps&,
                              GrProcessorKeyBuilder* b) {
        const GrBitmapTextGeoProc& gp = proc.cast<GrBitmapTextGeoProc>();
        uint32_t key = 0;
        key |= (gp.usesLocalCoords() && gp.localMatrix().hasPerspective()) ? 0x1 : 0x0;
        key |= gp.maskFormat() << 1;
        b->add32(key);

        // Currently we hardcode numbers to convert atlas coordinates to normalized floating point
        SkASSERT(gp.numTextureSamplers() == 1);
        GrTextureProxy* atlas = gp.textureSampler(0).proxy();
        if (atlas) {
            b->add32(atlas->width());
            b->add32(atlas->height());
        }
    }

private:
    GrColor fColor;
    UniformHandle fColorUniform;

    typedef GrGLSLGeometryProcessor INHERITED;
};

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

GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color,
                                         sk_sp<GrTextureProxy> proxy,
                                         const GrSamplerParams& params, GrMaskFormat format,
                                         const SkMatrix& localMatrix, bool usesLocalCoords)
    : fColor(color)
    , fLocalMatrix(localMatrix)
    , fUsesLocalCoords(usesLocalCoords)
    , fTextureSampler(std::move(proxy), params)
    , fInColor(nullptr)
    , fMaskFormat(format) {
    this->initClassID<GrBitmapTextGeoProc>();
    fInPosition =
            &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType, kHigh_GrSLPrecision);

    bool hasVertexColor = kA8_GrMaskFormat == fMaskFormat ||
                          kA565_GrMaskFormat == fMaskFormat;
    if (hasVertexColor) {
        fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
    }
    fInTextureCoords = &this->addVertexAttrib("inTextureCoords",  kVec2us_GrVertexAttribType,
                                              kHigh_GrSLPrecision);
    this->addTextureSampler(&fTextureSampler);
}

void GrBitmapTextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps,
                                              GrProcessorKeyBuilder* b) const {
    GrGLBitmapTextGeoProc::GenKey(*this, caps, b);
}

GrGLSLPrimitiveProcessor* GrBitmapTextGeoProc::createGLSLInstance(const GrShaderCaps& caps) const {
    return new GrGLBitmapTextGeoProc();
}

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

GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrBitmapTextGeoProc);

#if GR_TEST_UTILS
sk_sp<GrGeometryProcessor> GrBitmapTextGeoProc::TestCreate(GrProcessorTestData* d) {
    int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
                                        : GrProcessorUnitTest::kAlphaTextureIdx;
    sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);

    static const SkShader::TileMode kTileModes[] = {
        SkShader::kClamp_TileMode,
        SkShader::kRepeat_TileMode,
        SkShader::kMirror_TileMode,
    };
    SkShader::TileMode tileModes[] = {
        kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
        kTileModes[d->fRandom->nextULessThan(SK_ARRAY_COUNT(kTileModes))],
    };
    GrSamplerParams params(tileModes, d->fRandom->nextBool() ? GrSamplerParams::kBilerp_FilterMode
                                                             : GrSamplerParams::kNone_FilterMode);

    GrMaskFormat format = kARGB_GrMaskFormat; // init to avoid warning
    switch (d->fRandom->nextULessThan(3)) {
        case 0:
            format = kA8_GrMaskFormat;
            break;
        case 1:
            format = kA565_GrMaskFormat;
            break;
        case 2:
            format = kARGB_GrMaskFormat;
            break;
    }

    return GrBitmapTextGeoProc::Make(GrRandomColor(d->fRandom), std::move(proxy),
                                     params, format, GrTest::TestMatrix(d->fRandom),
                                     d->fRandom->nextBool());
}
#endif
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtGui/QPaintDevice>
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
#include <QtGui/QWidget>

#include "private/qt_x11_p.h"
#include "private/qpixmap_x11_p.h"
#include "private/qwidget_p.h"
#include "qx11info_x11.h"
#include "qwindowsurface_x11_p.h"

QT_BEGIN_NAMESPACE

extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp

struct QX11WindowSurfacePrivate
{
    QWidget *widget;
    QPixmap device;
#ifndef QT_NO_XRENDER
    bool translucentBackground;
#endif
};

QX11WindowSurface::QX11WindowSurface(QWidget *widget)
    : QWindowSurface(widget), d_ptr(new QX11WindowSurfacePrivate), gc(0)
{
    d_ptr->widget = widget;
#ifndef QT_NO_XRENDER
    d_ptr->translucentBackground = X11->use_xrender
        && widget->x11Info().depth() == 32;
    setStaticContentsSupport(!d_ptr->translucentBackground);
#else
    setStaticContentsSupport(true);
#endif
}

QX11WindowSurface::~QX11WindowSurface()
{
    delete d_ptr;
    if (gc) {
        XFreeGC(X11->display, gc);
        gc = 0;
    }
}

QPaintDevice *QX11WindowSurface::paintDevice()
{
    return &d_ptr->device;
}

void QX11WindowSurface::beginPaint(const QRegion &rgn)
{
#ifndef QT_NO_XRENDER
    if (d_ptr->translucentBackground) {
        if (d_ptr->device.depth() != 32)
            static_cast<QX11PixmapData *>(d_ptr->device.data_ptr().data())->convertToARGB32();
        ::Picture src = X11->getSolidFill(d_ptr->device.x11Info().screen(), Qt::transparent);
        ::Picture dst = d_ptr->device.x11PictureHandle();
        const QVector<QRect> rects = rgn.rects();
        const int w = d_ptr->device.width();
        const int h = d_ptr->device.height();
        for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it)
            XRenderComposite(X11->display, PictOpSrc, src, 0, dst,
                             0, 0, w, h, it->x(), it->y(),
                             it->width(), it->height());
    }
#endif
}

void QX11WindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
{
    if (d_ptr->device.isNull())
        return;

    QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
    QRegion wrgn(rgn);
    QRect br = rgn.boundingRect();
    if (!wOffset.isNull())
        wrgn.translate(-wOffset);
    QRect wbr = wrgn.boundingRect();

    int num;
    XRectangle *rects = (XRectangle *)qt_getClipRects(wrgn, num);
    if (num <= 0)
        return;
//         qDebug() << "XSetClipRectangles";
//         for  (int i = 0; i < num; ++i)
//             qDebug() << ' ' << i << rects[i].x << rects[i].x << rects[i].y << rects[i].width << rects[i].height;
    XSetClipRectangles(X11->display, gc, 0, 0, rects, num, YXBanded);
    XCopyArea(X11->display, d_ptr->device.handle(), widget->handle(), gc,
              br.x() + offset.x(), br.y() + offset.y(), br.width(), br.height(), wbr.x(), wbr.y());
}

void QX11WindowSurface::setGeometry(const QRect &rect)
{
    QWindowSurface::setGeometry(rect);

    const QSize size = rect.size();
    if (d_ptr->device.size() == size)
        return;
#ifndef QT_NO_XRENDER
    if (d_ptr->translucentBackground) {
        QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
        data->xinfo = d_ptr->widget->x11Info();
        data->resize(size.width(), size.height());
        d_ptr->device = QPixmap(data);
    } else
#endif
    {
        QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen());

        QX11PixmapData *oldData = static_cast<QX11PixmapData *>(d_ptr->device.pixmapData());
        Q_ASSERT(oldData);
        if (!(oldData->flags & QX11PixmapData::Uninitialized) && hasStaticContents()) {
            // Copy the content of the old pixmap into the new one.
            QX11PixmapData *newData = new QX11PixmapData(QPixmapData::PixmapType);
            newData->resize(size.width(), size.height());
            Q_ASSERT(oldData->d == newData->d);

            QRegion staticRegion(staticContents());
            // Make sure we're inside the boundaries of the old pixmap.
            staticRegion &= QRect(0, 0, oldData->w, oldData->h);
            const QRect boundingRect(staticRegion.boundingRect());
            const int dx = boundingRect.x();
            const int dy = boundingRect.y();

            int num;
            XRectangle *rects = (XRectangle *)qt_getClipRects(staticRegion, num);
            GC tmpGc = XCreateGC(X11->display, oldData->hd, 0, 0);
            XSetClipRectangles(X11->display, tmpGc, 0, 0, rects, num, YXBanded);
            XCopyArea(X11->display, oldData->hd, newData->hd, tmpGc,
                      dx, dy, qMin(boundingRect.width(), size.width()),
                      qMin(boundingRect.height(), size.height()), dx, dy);
            XFreeGC(X11->display, tmpGc);
            newData->flags &= ~QX11PixmapData::Uninitialized;

            d_ptr->device = QPixmap(newData);
        } else {
            d_ptr->device = QPixmap(size);
        }
    }

    if (gc)
        XFreeGC(X11->display, gc);
    gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
    XSetGraphicsExposures(X11->display, gc, False);
}

bool QX11WindowSurface::scroll(const QRegion &area, int dx, int dy)
{
    QRect rect = area.boundingRect();

    if (d_ptr->device.isNull())
        return false;

    GC gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
    XCopyArea(X11->display, d_ptr->device.handle(), d_ptr->device.handle(), gc,
              rect.x(), rect.y(), rect.width(), rect.height(),
              rect.x()+dx, rect.y()+dy);
    XFreeGC(X11->display, gc);

    return true;
}

QPixmap QX11WindowSurface::grabWidget(const QWidget *widget,
                                      const QRect& rect) const
{
    if (!widget || d_ptr->device.isNull())
        return QPixmap();

    QRect srcRect;

    // make sure the rect is inside the widget & clip to widget's rect
    if (!rect.isEmpty())
        srcRect = rect & widget->rect();
    else
        srcRect = widget->rect();

    if (srcRect.isEmpty())
        return QPixmap();

    // If it's a child widget we have to translate the coordinates
    if (widget != window())
        srcRect.translate(widget->mapTo(window(), QPoint(0, 0)));

    QPixmap::x11SetDefaultScreen(widget->x11Info().screen());
    QPixmap px(srcRect.width(), srcRect.height());

    GC tmpGc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);

    // Copy srcRect from the backing store to the new pixmap
    XSetGraphicsExposures(X11->display, tmpGc, False);
    XCopyArea(X11->display, d_ptr->device.handle(), px.handle(), tmpGc,
              srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0);

    XFreeGC(X11->display, tmpGc);

    return px;
}

QT_END_NAMESPACE
<commit_msg>Fixed passing a zero drawable to X11 api causing X errors.<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtGui/QPaintDevice>
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
#include <QtGui/QWidget>

#include "private/qt_x11_p.h"
#include "private/qpixmap_x11_p.h"
#include "private/qwidget_p.h"
#include "qx11info_x11.h"
#include "qwindowsurface_x11_p.h"

QT_BEGIN_NAMESPACE

extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp

struct QX11WindowSurfacePrivate
{
    QWidget *widget;
    QPixmap device;
#ifndef QT_NO_XRENDER
    bool translucentBackground;
#endif
};

QX11WindowSurface::QX11WindowSurface(QWidget *widget)
    : QWindowSurface(widget), d_ptr(new QX11WindowSurfacePrivate), gc(0)
{
    d_ptr->widget = widget;
#ifndef QT_NO_XRENDER
    d_ptr->translucentBackground = X11->use_xrender
        && widget->x11Info().depth() == 32;
    setStaticContentsSupport(!d_ptr->translucentBackground);
#else
    setStaticContentsSupport(true);
#endif
}

QX11WindowSurface::~QX11WindowSurface()
{
    delete d_ptr;
    if (gc) {
        XFreeGC(X11->display, gc);
        gc = 0;
    }
}

QPaintDevice *QX11WindowSurface::paintDevice()
{
    return &d_ptr->device;
}

void QX11WindowSurface::beginPaint(const QRegion &rgn)
{
#ifndef QT_NO_XRENDER
    if (d_ptr->translucentBackground) {
        if (d_ptr->device.depth() != 32)
            static_cast<QX11PixmapData *>(d_ptr->device.data_ptr().data())->convertToARGB32();
        ::Picture src = X11->getSolidFill(d_ptr->device.x11Info().screen(), Qt::transparent);
        ::Picture dst = d_ptr->device.x11PictureHandle();
        const QVector<QRect> rects = rgn.rects();
        const int w = d_ptr->device.width();
        const int h = d_ptr->device.height();
        for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it)
            XRenderComposite(X11->display, PictOpSrc, src, 0, dst,
                             0, 0, w, h, it->x(), it->y(),
                             it->width(), it->height());
    }
#endif
}

void QX11WindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
{
    if (d_ptr->device.isNull())
        return;

    QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
    QRegion wrgn(rgn);
    QRect br = rgn.boundingRect();
    if (!wOffset.isNull())
        wrgn.translate(-wOffset);
    QRect wbr = wrgn.boundingRect();

    int num;
    XRectangle *rects = (XRectangle *)qt_getClipRects(wrgn, num);
    if (num <= 0)
        return;
//         qDebug() << "XSetClipRectangles";
//         for  (int i = 0; i < num; ++i)
//             qDebug() << ' ' << i << rects[i].x << rects[i].x << rects[i].y << rects[i].width << rects[i].height;
    XSetClipRectangles(X11->display, gc, 0, 0, rects, num, YXBanded);
    XCopyArea(X11->display, d_ptr->device.handle(), widget->handle(), gc,
              br.x() + offset.x(), br.y() + offset.y(), br.width(), br.height(), wbr.x(), wbr.y());
}

void QX11WindowSurface::setGeometry(const QRect &rect)
{
    QWindowSurface::setGeometry(rect);

    const QSize size = rect.size();

    if (d_ptr->device.size() == size || size.width() <= 0 || size.height() <= 0)
        return;
#ifndef QT_NO_XRENDER
    if (d_ptr->translucentBackground) {
        QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
        data->xinfo = d_ptr->widget->x11Info();
        data->resize(size.width(), size.height());
        d_ptr->device = QPixmap(data);
    } else
#endif
    {
        QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen());

        QX11PixmapData *oldData = static_cast<QX11PixmapData *>(d_ptr->device.pixmapData());
        Q_ASSERT(oldData);
        if (!(oldData->flags & QX11PixmapData::Uninitialized) && hasStaticContents()) {
            // Copy the content of the old pixmap into the new one.
            QX11PixmapData *newData = new QX11PixmapData(QPixmapData::PixmapType);
            newData->resize(size.width(), size.height());
            Q_ASSERT(oldData->d == newData->d);

            QRegion staticRegion(staticContents());
            // Make sure we're inside the boundaries of the old pixmap.
            staticRegion &= QRect(0, 0, oldData->w, oldData->h);
            const QRect boundingRect(staticRegion.boundingRect());
            const int dx = boundingRect.x();
            const int dy = boundingRect.y();

            int num;
            XRectangle *rects = (XRectangle *)qt_getClipRects(staticRegion, num);
            GC tmpGc = XCreateGC(X11->display, oldData->hd, 0, 0);
            XSetClipRectangles(X11->display, tmpGc, 0, 0, rects, num, YXBanded);
            XCopyArea(X11->display, oldData->hd, newData->hd, tmpGc,
                      dx, dy, qMin(boundingRect.width(), size.width()),
                      qMin(boundingRect.height(), size.height()), dx, dy);
            XFreeGC(X11->display, tmpGc);
            newData->flags &= ~QX11PixmapData::Uninitialized;

            d_ptr->device = QPixmap(newData);
        } else {
            d_ptr->device = QPixmap(size);
        }
    }

    if (gc) {
        XFreeGC(X11->display, gc);
        gc = 0;
    }
    if (!d_ptr->device.isNull()) {
        gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
        XSetGraphicsExposures(X11->display, gc, False);
    }
}

bool QX11WindowSurface::scroll(const QRegion &area, int dx, int dy)
{
    QRect rect = area.boundingRect();

    if (d_ptr->device.isNull())
        return false;

    GC gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
    XCopyArea(X11->display, d_ptr->device.handle(), d_ptr->device.handle(), gc,
              rect.x(), rect.y(), rect.width(), rect.height(),
              rect.x()+dx, rect.y()+dy);
    XFreeGC(X11->display, gc);

    return true;
}

QPixmap QX11WindowSurface::grabWidget(const QWidget *widget,
                                      const QRect& rect) const
{
    if (!widget || d_ptr->device.isNull())
        return QPixmap();

    QRect srcRect;

    // make sure the rect is inside the widget & clip to widget's rect
    if (!rect.isEmpty())
        srcRect = rect & widget->rect();
    else
        srcRect = widget->rect();

    if (srcRect.isEmpty())
        return QPixmap();

    // If it's a child widget we have to translate the coordinates
    if (widget != window())
        srcRect.translate(widget->mapTo(window(), QPoint(0, 0)));

    QPixmap::x11SetDefaultScreen(widget->x11Info().screen());
    QPixmap px(srcRect.width(), srcRect.height());

    GC tmpGc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);

    // Copy srcRect from the backing store to the new pixmap
    XSetGraphicsExposures(X11->display, tmpGc, False);
    XCopyArea(X11->display, d_ptr->device.handle(), px.handle(), tmpGc,
              srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0);

    XFreeGC(X11->display, tmpGc);

    return px;
}

QT_END_NAMESPACE
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "condor_common.h"
#include "condor_config.h"
#include "string_list.h"

#include "ec2resource.h"
#include "gridmanager.h"

#define HASH_TABLE_SIZE	500

HashTable <HashKey, EC2Resource *>
	EC2Resource::ResourcesByName( HASH_TABLE_SIZE, hashFunction );

const char * EC2Resource::HashName( const char * resource_name,
		const char * public_key_file, const char * private_key_file )
{
	static std::string hash_name;
	formatstr( hash_name, "ec2 %s#%s#%s", resource_name, public_key_file, private_key_file );
	return hash_name.c_str();
}

EC2Resource* EC2Resource::FindOrCreateResource(const char * resource_name,
	const char * public_key_file, const char * private_key_file )
{
	int rc;
	EC2Resource *resource = NULL;

	rc = ResourcesByName.lookup( HashKey( HashName( resource_name, public_key_file, private_key_file ) ), resource );
	if ( rc != 0 ) {
		resource = new EC2Resource( resource_name, public_key_file, private_key_file );
		ASSERT(resource);
		resource->Reconfig();
		ResourcesByName.insert( HashKey( HashName( resource_name, public_key_file, private_key_file ) ), resource );
	} else {
		ASSERT(resource);
	}

	return resource;
}

EC2Resource::EC2Resource( const char *resource_name,
	const char * public_key_file, const char * private_key_file ) :
		BaseResource( resource_name ),
		jobsByInstanceID( hashFunction ),
		spotJobsByRequestID( hashFunction ),
		m_hadAuthFailure( false ),
		m_checkSpotNext( false )
{
	// although no one will use resource_name, we still keep it for base class constructor

	m_public_key_file = strdup(public_key_file);
	m_private_key_file = strdup(private_key_file);

	gahp = NULL;

	char * gahp_path = param( "EC2_GAHP" );
	if ( gahp_path == NULL ) {
		dprintf(D_ALWAYS, "EC2_GAHP not defined! \n");
		return;
	}

	ArgList args;
	args.AppendArg("-f");

	gahp = new GahpClient( EC2_RESOURCE_NAME, gahp_path, &args );
	gahp->setNotificationTimerId( pingTimerId );
	gahp->setMode( GahpClient::normal );
	gahp->setTimeout( EC2Job::gahpCallTimeout );

	status_gahp = new GahpClient( EC2_RESOURCE_NAME, gahp_path, &args );

	StartBatchStatusTimer();

    status_gahp->setNotificationTimerId( BatchPollTid() );
	status_gahp->setMode( GahpClient::normal );
	status_gahp->setTimeout( EC2Job::gahpCallTimeout );

	free(gahp_path);
}

EC2Resource::~EC2Resource()
{
	ResourcesByName.remove( HashKey( HashName( resourceName, m_public_key_file,
											   m_private_key_file ) ) );
	if ( gahp ) delete gahp;
	if (m_public_key_file) free(m_public_key_file);
	if (m_private_key_file) free(m_private_key_file);
}

void EC2Resource::Reconfig()
{
	BaseResource::Reconfig();
	gahp->setTimeout( EC2Job::gahpCallTimeout );
}

const char *EC2Resource::ResourceType()
{
	return "ec2";
}

const char *EC2Resource::GetHashName()
{
	return HashName( resourceName, m_public_key_file, m_private_key_file );
}

void EC2Resource::PublishResourceAd( ClassAd *resource_ad )
{
	BaseResource::PublishResourceAd( resource_ad );

	resource_ad->Assign( ATTR_EC2_ACCESS_KEY_ID, m_public_key_file );
	resource_ad->Assign( ATTR_EC2_SECRET_ACCESS_KEY, m_private_key_file );
}

// we will use ec2 command "status_all" to do the Ping work
void EC2Resource::DoPing( time_t& ping_delay, bool& ping_complete, bool& ping_succeeded )
{
	// Since EC2 doesn't use proxy, we should use Startup() to replace isInitialized()
	if ( gahp->isStarted() == false ) {
		dprintf( D_ALWAYS,"gahp server not up yet, delaying ping\n" );
		ping_delay = 5;
		return;
	}

	ping_delay = 0;

	char * error_code = NULL;
	int rc = gahp->ec2_ping( resourceName, m_public_key_file, m_private_key_file, error_code );

	if ( rc == GAHPCLIENT_COMMAND_PENDING ) {
		ping_complete = false;
	}
	else if ( rc != 0 ) {
		ping_complete = true;

		// If the service returns an authorization failure, that means
		// the service is up, so return true.  Individual jobs with
		// invalid authentication tokens will then go on hold, which is
		// what we want (rather than saying idle).
		if( error_code != NULL ) {
			if( strstr( error_code, "(401)" ) != NULL ) {
				ping_succeeded = true;
				m_hadAuthFailure = true;
				formatstr( authFailureMessage, "(%s): '%s'", error_code, gahp->getErrorString() );
			}
			free( error_code );
		} else {
		    ping_succeeded = false;
		}
	}
	else {
		ping_complete = true;
		ping_succeeded = true;
	}

	return;
}

EC2Resource::BatchStatusResult EC2Resource::StartBatchStatus() {
    ASSERT( status_gahp );

    // m_checkSpotNext starts out false
    if( ! m_checkSpotNext ) {
        StringList returnStatus;
        char * errorCode = NULL;
        int rc = status_gahp->ec2_vm_status_all( resourceName,
                    m_public_key_file, m_private_key_file,
                    returnStatus, errorCode );

        if( rc == GAHPCLIENT_COMMAND_PENDING ) { return BSR_PENDING; }

        if( rc != 0 ) {
            std::string errorString = status_gahp->getErrorString();
            dprintf( D_ALWAYS, "Error doing batched EC2 status query: %s: %s.\n",
                        errorCode, errorString.c_str() );
            return BSR_ERROR;
        }

        //
        // We have to let a job know if we can't find a status report for it.
        //
        List<EC2Job> myJobs;
        EC2Job * nextJob = NULL;
        jobsByInstanceID.startIterations();
        while( jobsByInstanceID.iterate( nextJob ) ) {
            myJobs.Append( nextJob );
        }

        returnStatus.rewind();
        ASSERT( returnStatus.number() % 4 == 0 );
        for( int i = 0; i < returnStatus.number(); i += 4 ) {
            std::string instanceID = returnStatus.next();
            std::string status = returnStatus.next();
            /* std::string amiID = */ returnStatus.next();
            /* std::string clientToken = */ returnStatus.next();

            // We can't use BaseJob::JobsByRemoteId because OpenStack doesn't
            // include the client token in its status responses, and therefore
            // we can't always fully reconstruct the remoteJobID used as the key.
            EC2Job * job = NULL;
            rc = jobsByInstanceID.lookup( HashKey( instanceID.c_str() ), job );
            if( rc != 0 ) {
                dprintf( D_FULLDEBUG, "Found unknown instance '%s'; skipping.\n", instanceID.c_str() );
                continue;
            }
            ASSERT( job );

            dprintf( D_FULLDEBUG, "Found job object for '%s', updating status ('%s').\n", instanceID.c_str(), status.c_str() );
            job->StatusUpdate( status.c_str() );
            myJobs.Delete( job );
        }

        myJobs.Rewind();
        while( ( nextJob = myJobs.Next() ) ) {
            dprintf( D_FULLDEBUG, "Informing job %p it got no status.\n", nextJob );
            nextJob->StatusUpdate( NULL );
        }

        // Don't ask for spot results unless we know about a spot job.  This
        // should prevent us from breaking OpenStack.
        if( spotJobsByRequestID.getNumElements() == 0 ) {
            m_checkSpotNext = false;
            return BSR_DONE;
        } else {
            m_checkSpotNext = true;
        }
    }

    if( m_checkSpotNext ) {
        StringList spotReturnStatus;
        char * spotErrorCode = NULL;
        int spotRC = status_gahp->ec2_spot_status_all( resourceName,
                        m_public_key_file, m_private_key_file,
                        spotReturnStatus, spotErrorCode );

        if( spotRC == GAHPCLIENT_COMMAND_PENDING ) { return BSR_PENDING; }

        if( spotRC != 0 ) {
            std::string errorString = status_gahp->getErrorString();
            dprintf( D_ALWAYS, "Error doing batched EC2 spot status query: %s: %s.\n",
                    spotErrorCode, errorString.c_str() );
            return BSR_ERROR;
        }

        List<EC2Job> mySpotJobs;
        EC2Job * nextSpotJob = NULL;
        spotJobsByRequestID.startIterations();
        while( spotJobsByRequestID.iterate( nextSpotJob ) ) {
            mySpotJobs.Append( nextSpotJob );
        }

        spotReturnStatus.rewind();
        ASSERT( spotReturnStatus.number() % 5 == 0 );
        for( int i = 0; i < spotReturnStatus.number(); i += 5 ) {
            // Note that we called
            std::string requestID = spotReturnStatus.next();
            std::string state = spotReturnStatus.next();
            /* std::string launchGroup = */ spotReturnStatus.next();
            /* std::string instanceID = */ spotReturnStatus.next();
            std::string statusCode = spotReturnStatus.next();

            EC2Job * spotJob = NULL;
            spotRC = spotJobsByRequestID.lookup( HashKey( requestID.c_str() ), spotJob );
            if( spotRC != 0 ) {
                dprintf( D_FULLDEBUG, "Found unknown spot request '%s'; skipping.\n", requestID.c_str() );
                continue;
            }
            ASSERT( spotJob );

            if( ! statusCode.empty() ) { state = statusCode; }

            dprintf( D_FULLDEBUG, "Found spot job object for '%s', updating status ('%s').\n", requestID.c_str(), state.c_str() );
            spotJob->StatusUpdate( state.c_str() );
            mySpotJobs.Delete( spotJob );
        }

        mySpotJobs.Rewind();
        while( ( nextSpotJob = mySpotJobs.Next() ) ) {
            dprintf( D_FULLDEBUG, "Informing spot job %p it got no status.\n", nextSpotJob );
            nextSpotJob->StatusUpdate( NULL );
        }

        m_checkSpotNext = false;
        return BSR_DONE;
    }

    // This should never happen (but the compiler hates you).
    return BSR_ERROR;
}

EC2Resource::BatchStatusResult EC2Resource::FinishBatchStatus() {
    return StartBatchStatus();
}
<commit_msg>Gridmanager can crash on certain replies from the EC2 GAHP #3701<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "condor_common.h"
#include "condor_config.h"
#include "string_list.h"

#include "ec2resource.h"
#include "gridmanager.h"

#define HASH_TABLE_SIZE	500

HashTable <HashKey, EC2Resource *>
	EC2Resource::ResourcesByName( HASH_TABLE_SIZE, hashFunction );

const char * EC2Resource::HashName( const char * resource_name,
		const char * public_key_file, const char * private_key_file )
{
	static std::string hash_name;
	formatstr( hash_name, "ec2 %s#%s#%s", resource_name, public_key_file, private_key_file );
	return hash_name.c_str();
}

EC2Resource* EC2Resource::FindOrCreateResource(const char * resource_name,
	const char * public_key_file, const char * private_key_file )
{
	int rc;
	EC2Resource *resource = NULL;

	rc = ResourcesByName.lookup( HashKey( HashName( resource_name, public_key_file, private_key_file ) ), resource );
	if ( rc != 0 ) {
		resource = new EC2Resource( resource_name, public_key_file, private_key_file );
		ASSERT(resource);
		resource->Reconfig();
		ResourcesByName.insert( HashKey( HashName( resource_name, public_key_file, private_key_file ) ), resource );
	} else {
		ASSERT(resource);
	}

	return resource;
}

EC2Resource::EC2Resource( const char *resource_name,
	const char * public_key_file, const char * private_key_file ) :
		BaseResource( resource_name ),
		jobsByInstanceID( hashFunction ),
		spotJobsByRequestID( hashFunction ),
		m_hadAuthFailure( false ),
		m_checkSpotNext( false )
{
	// although no one will use resource_name, we still keep it for base class constructor

	m_public_key_file = strdup(public_key_file);
	m_private_key_file = strdup(private_key_file);

	gahp = NULL;

	char * gahp_path = param( "EC2_GAHP" );
	if ( gahp_path == NULL ) {
		dprintf(D_ALWAYS, "EC2_GAHP not defined! \n");
		return;
	}

	ArgList args;
	args.AppendArg("-f");

	gahp = new GahpClient( EC2_RESOURCE_NAME, gahp_path, &args );
	gahp->setNotificationTimerId( pingTimerId );
	gahp->setMode( GahpClient::normal );
	gahp->setTimeout( EC2Job::gahpCallTimeout );

	status_gahp = new GahpClient( EC2_RESOURCE_NAME, gahp_path, &args );

	StartBatchStatusTimer();

    status_gahp->setNotificationTimerId( BatchPollTid() );
	status_gahp->setMode( GahpClient::normal );
	status_gahp->setTimeout( EC2Job::gahpCallTimeout );

	free(gahp_path);
}

EC2Resource::~EC2Resource()
{
	ResourcesByName.remove( HashKey( HashName( resourceName, m_public_key_file,
											   m_private_key_file ) ) );
	if ( gahp ) delete gahp;
	if (m_public_key_file) free(m_public_key_file);
	if (m_private_key_file) free(m_private_key_file);
}

void EC2Resource::Reconfig()
{
	BaseResource::Reconfig();
	gahp->setTimeout( EC2Job::gahpCallTimeout );
}

const char *EC2Resource::ResourceType()
{
	return "ec2";
}

const char *EC2Resource::GetHashName()
{
	return HashName( resourceName, m_public_key_file, m_private_key_file );
}

void EC2Resource::PublishResourceAd( ClassAd *resource_ad )
{
	BaseResource::PublishResourceAd( resource_ad );

	resource_ad->Assign( ATTR_EC2_ACCESS_KEY_ID, m_public_key_file );
	resource_ad->Assign( ATTR_EC2_SECRET_ACCESS_KEY, m_private_key_file );
}

// we will use ec2 command "status_all" to do the Ping work
void EC2Resource::DoPing( time_t& ping_delay, bool& ping_complete, bool& ping_succeeded )
{
	// Since EC2 doesn't use proxy, we should use Startup() to replace isInitialized()
	if ( gahp->isStarted() == false ) {
		dprintf( D_ALWAYS,"gahp server not up yet, delaying ping\n" );
		ping_delay = 5;
		return;
	}

	ping_delay = 0;

	char * error_code = NULL;
	int rc = gahp->ec2_ping( resourceName, m_public_key_file, m_private_key_file, error_code );

	if ( rc == GAHPCLIENT_COMMAND_PENDING ) {
		ping_complete = false;
	}
	else if ( rc != 0 ) {
		ping_complete = true;

		// If the service returns an authorization failure, that means
		// the service is up, so return true.  Individual jobs with
		// invalid authentication tokens will then go on hold, which is
		// what we want (rather than saying idle).
		if( error_code != NULL ) {
			if( strstr( error_code, "(401)" ) != NULL ) {
				ping_succeeded = true;
				m_hadAuthFailure = true;
				formatstr( authFailureMessage, "(%s): '%s'", error_code, gahp->getErrorString() );
			}
			free( error_code );
		} else {
		    ping_succeeded = false;
		}
	}
	else {
		ping_complete = true;
		ping_succeeded = true;
	}

	return;
}

EC2Resource::BatchStatusResult EC2Resource::StartBatchStatus() {
    ASSERT( status_gahp );

    // m_checkSpotNext starts out false
    if( ! m_checkSpotNext ) {
        StringList returnStatus;
        char * errorCode = NULL;
        int rc = status_gahp->ec2_vm_status_all( resourceName,
                    m_public_key_file, m_private_key_file,
                    returnStatus, errorCode );

        if( rc == GAHPCLIENT_COMMAND_PENDING ) { return BSR_PENDING; }

        if( rc != 0 ) {
            std::string errorString = status_gahp->getErrorString();
            dprintf( D_ALWAYS, "Error doing batched EC2 status query: %s: %s.\n",
                     errorCode ? errorCode : "", errorString.c_str() );
            free( errorCode );
            return BSR_ERROR;
        }

        //
        // We have to let a job know if we can't find a status report for it.
        //
        List<EC2Job> myJobs;
        EC2Job * nextJob = NULL;
        jobsByInstanceID.startIterations();
        while( jobsByInstanceID.iterate( nextJob ) ) {
            myJobs.Append( nextJob );
        }

        returnStatus.rewind();
        ASSERT( returnStatus.number() % 4 == 0 );
        for( int i = 0; i < returnStatus.number(); i += 4 ) {
            std::string instanceID = returnStatus.next();
            std::string status = returnStatus.next();
            /* std::string amiID = */ returnStatus.next();
            /* std::string clientToken = */ returnStatus.next();

            // We can't use BaseJob::JobsByRemoteId because OpenStack doesn't
            // include the client token in its status responses, and therefore
            // we can't always fully reconstruct the remoteJobID used as the key.
            EC2Job * job = NULL;
            rc = jobsByInstanceID.lookup( HashKey( instanceID.c_str() ), job );
            if( rc != 0 ) {
                dprintf( D_FULLDEBUG, "Found unknown instance '%s'; skipping.\n", instanceID.c_str() );
                continue;
            }
            ASSERT( job );

            dprintf( D_FULLDEBUG, "Found job object for '%s', updating status ('%s').\n", instanceID.c_str(), status.c_str() );
            job->StatusUpdate( status.c_str() );
            myJobs.Delete( job );
        }

        myJobs.Rewind();
        while( ( nextJob = myJobs.Next() ) ) {
            dprintf( D_FULLDEBUG, "Informing job %p it got no status.\n", nextJob );
            nextJob->StatusUpdate( NULL );
        }

        // Don't ask for spot results unless we know about a spot job.  This
        // should prevent us from breaking OpenStack.
        if( spotJobsByRequestID.getNumElements() == 0 ) {
            m_checkSpotNext = false;
            return BSR_DONE;
        } else {
            m_checkSpotNext = true;
        }
    }

    if( m_checkSpotNext ) {
        StringList spotReturnStatus;
        char * spotErrorCode = NULL;
        int spotRC = status_gahp->ec2_spot_status_all( resourceName,
                        m_public_key_file, m_private_key_file,
                        spotReturnStatus, spotErrorCode );

        if( spotRC == GAHPCLIENT_COMMAND_PENDING ) { return BSR_PENDING; }

        if( spotRC != 0 ) {
            std::string errorString = status_gahp->getErrorString();
            dprintf( D_ALWAYS, "Error doing batched EC2 spot status query: %s: %s.\n",
                     spotErrorCode ? spotErrorCode : "", errorString.c_str() );
            free( spotErrorCode );
            return BSR_ERROR;
        }

        List<EC2Job> mySpotJobs;
        EC2Job * nextSpotJob = NULL;
        spotJobsByRequestID.startIterations();
        while( spotJobsByRequestID.iterate( nextSpotJob ) ) {
            mySpotJobs.Append( nextSpotJob );
        }

        spotReturnStatus.rewind();
        ASSERT( spotReturnStatus.number() % 5 == 0 );
        for( int i = 0; i < spotReturnStatus.number(); i += 5 ) {
            // Note that we called
            std::string requestID = spotReturnStatus.next();
            std::string state = spotReturnStatus.next();
            /* std::string launchGroup = */ spotReturnStatus.next();
            /* std::string instanceID = */ spotReturnStatus.next();
            std::string statusCode = spotReturnStatus.next();

            EC2Job * spotJob = NULL;
            spotRC = spotJobsByRequestID.lookup( HashKey( requestID.c_str() ), spotJob );
            if( spotRC != 0 ) {
                dprintf( D_FULLDEBUG, "Found unknown spot request '%s'; skipping.\n", requestID.c_str() );
                continue;
            }
            ASSERT( spotJob );

            if( ! statusCode.empty() ) { state = statusCode; }

            dprintf( D_FULLDEBUG, "Found spot job object for '%s', updating status ('%s').\n", requestID.c_str(), state.c_str() );
            spotJob->StatusUpdate( state.c_str() );
            mySpotJobs.Delete( spotJob );
        }

        mySpotJobs.Rewind();
        while( ( nextSpotJob = mySpotJobs.Next() ) ) {
            dprintf( D_FULLDEBUG, "Informing spot job %p it got no status.\n", nextSpotJob );
            nextSpotJob->StatusUpdate( NULL );
        }

        m_checkSpotNext = false;
        return BSR_DONE;
    }

    // This should never happen (but the compiler hates you).
    return BSR_ERROR;
}

EC2Resource::BatchStatusResult EC2Resource::FinishBatchStatus() {
    return StartBatchStatus();
}
<|endoftext|>