<commit_before>#include "libyaml_utils.h"
#include "logger.h"

// ---------------------------------------------------------------------------------
// ------------------------------- libyaml test code -------------------------------
// ---------------------------------------------------------------------------------
namespace
{

bool PositionAnalysis(mode_type* add_to_me, const mode_type reference_character, const bool map_mode)
{
    if (add_to_me != nullptr)
    {
        if (reference_character ==  mode_type::MAP_TYPE)
        {
            if (map_mode)
            {
                *add_to_me =  mode_type::KEY_TYPE;
            }
            else
            {
                *add_to_me =  mode_type::VALUE_TYPE;
            }
            return !map_mode;
        }
        else if (reference_character ==  mode_type::SEQUENCE_TYPE)
        {
            *add_to_me =  mode_type::SEQUENCE_TYPE;
        }
        else
        {
            *add_to_me =  mode_type::UNKNOWN_TYPE;
        }
    }
    return map_mode;
}

void AddTag(YAML::Node* current_node, yaml_char_t* tag)
{
    if (current_node != nullptr)
    {
        if (tag)
        {
            std::string temp_tag_translator = ((char*) tag);

            current_node->SetTag(temp_tag_translator);
        }
        else if(current_node->Tag().empty())
        {
            current_node->SetTag("?");
        }
    }
}

void AddToNode
    (YAML::Node* addToMe, YAML::Node* add_me, std::stack<YAML::Node>* key_stack,
    const mode_type* tracking_current_type, yaml_char_t* tag)
{
    AddTag(add_me, tag);

    if (tracking_current_type != nullptr && addToMe != nullptr)
    {
        if (*tracking_current_type ==  mode_type::SEQUENCE_TYPE && addToMe->IsSequence())
        {
            TEST_PPRINT("squ type\n")
            addToMe->push_back(*add_me);
        }
        else if (*tracking_current_type ==  mode_type::KEY_TYPE && addToMe->IsMap())
        {
            TEST_PPRINT("key type\n")
            key_stack->push(*add_me);
            (*addToMe)[*add_me];
        }
        else if (*tracking_current_type ==  mode_type::VALUE_TYPE && addToMe->IsMap())
        {
            TEST_PPRINT("map type\n")
            if (!key_stack->empty())
            {
                (*addToMe)[key_stack->top()] = *add_me;
                key_stack->pop();
            }
        }
        else
        {
            TEST_PPRINT("? type\n")
        }
    }
}

bool EndEventAddition
    (std::vector<YAML::Node>* libyaml_local_output, std::stack<mode_type>* mode_stack,
    std::stack<bool>* map_mode_stack, bool map_mode, std::stack<YAML::Node>* key_stack)
{
    if (libyaml_local_output->size() > 1 && mode_stack->size() > 1)
    {
        mode_stack->pop();

        if (mode_stack->top() == mode_type::MAP_TYPE && !map_mode_stack->empty())
        {
            map_mode = map_mode_stack->top();
            map_mode_stack->pop();
        }
        mode_type temp_position_info;

        if (mode_stack->empty())
        {
            return map_mode;
        }
        PositionAnalysis(&temp_position_info, (mode_stack->top()), !map_mode);

        YAML::Node temp_node = libyaml_local_output->back();

        libyaml_local_output->pop_back();

        AddToNode(&libyaml_local_output->back(), &temp_node, key_stack, &temp_position_info, nullptr);
    }
    return map_mode;
}

void RestartVariables (std::stack<YAML::Node>* key_stack,
    std::stack<mode_type>* mode_stack, std::stack<bool>* map_mode_stack,
    std::vector<YAML::Node>* libyaml_local_output, std::vector<YAML::Node>* libyaml_final_output,
    bool* map_mode, std::map<std::string, YAML::Node>* anchor_map)
{
    while (!key_stack->empty())
    {
        key_stack->pop();
    }

    while (!mode_stack->empty())
    {
        mode_stack->pop();
    }
    mode_stack->push(mode_type::UNKNOWN_TYPE);

    while (!map_mode_stack->empty())
    {
        map_mode_stack->pop();
    }

    if (!libyaml_local_output->empty())
    {
        libyaml_final_output->push_back(libyaml_local_output->back());
    }

    libyaml_local_output->clear();

    *map_mode = true;

    anchor_map->clear();
}

std::unique_ptr<std::vector<yaml_event_t>> GetEvents
    (const uint8_t* input, size_t input_size, std::string* error_message_container)
{
    std::unique_ptr<std::vector<yaml_event_t>> event_list(new std::vector<yaml_event_t>);

    yaml_parser_t parser;
    yaml_event_t event;

    if (!yaml_parser_initialize(&parser))
    {
        TEST_PPRINT("ERROR: Failed to initialize\n");

        *error_message_container = std::string("ERROR");

        return std::move(event_list);
    }

    yaml_parser_set_input_string(&parser, input, input_size);

    int done = 0;

    while (true)
    {
        yaml_event_type_t type;

        type = event.type;

        if (!yaml_parser_parse(&parser, &event))
        {
            yaml_event_delete(&event);

            TEST_PPRINT("ERROR: Bad parsing\n");

            *error_message_container = std::string("ERROR");

            WipeEventList(event_list.get());

            break;
        }

        event_list.get()->push_back(event);

        if (type == YAML_STREAM_END_EVENT)
        {
            break;
        }
    }

    yaml_parser_delete(&parser);

    fflush(stdout);

    return std::move(event_list);
}

void WipeEventList(std::vector<yaml_event_t>* event_list)
{
    for (std::vector<yaml_event_t>::iterator it = event_list->begin();
        it != event_list->end(); ++ it)
    {
        yaml_event_delete(&(*it));
    }
    event_list->clear();
}

bool RelevantTag(yaml_char_t* check_tag)
{
    if (check_tag != nullptr)
    {
        std::string temp_tag_translator = ((char*) check_tag);

        return temp_tag_translator != "!" && temp_tag_translator != "?";
    }
    return false;
}

}

std::vector<YAML::Node>* libyaml_parsing::ParseLibyaml
    (const uint8_t* input, size_t input_size, std::string* error_message_container)
{
    std::vector<YAML::Node> libyaml_local_output;

    std::vector<YAML::Node>* libyaml_final_output = new std::vector<YAML::Node>;

    std::stack<YAML::Node> key_stack;

    std::stack<mode_type> mode_stack;

    mode_stack.push(mode_type::UNKNOWN_TYPE);

    std::stack<bool> map_mode_stack;

    bool map_mode = true;

    std::map<std::string, YAML::Node> anchor_map;

    std::unique_ptr<std::vector<yaml_event_t>> event_list =
        std::move(GetEvents(input, input_size, error_message_container));

    for (std::vector<yaml_event_t>::iterator event = event_list.get()->begin();
        event != event_list.get()->end(); ++ event)
    {
        mode_type tracking_current_type;

        switch (event->type)
        {
            case YAML_STREAM_END_EVENT:
                TEST_PPRINT("STR-\n");

                break;
            case YAML_DOCUMENT_END_EVENT:
                TEST_PPRINT("DOC-\n");

                RestartVariables(&key_stack, &mode_stack, &map_mode_stack, &libyaml_local_output,
                    libyaml_final_output, &map_mode, &anchor_map);

                break;
            case YAML_DOCUMENT_START_EVENT:
                TEST_PPRINT("DOC+\n");

                RestartVariables(&key_stack, &mode_stack, &map_mode_stack, &libyaml_local_output,
                    libyaml_final_output, &map_mode, &anchor_map);

                break;

            case YAML_MAPPING_END_EVENT:
                TEST_PPRINT("MAP-\n");

                map_mode = EndEventAddition(&libyaml_local_output, &mode_stack, &map_mode_stack, map_mode, &key_stack);

                break;
            case YAML_SEQUENCE_END_EVENT:
                TEST_PPRINT("SQU-\n");

                map_mode = EndEventAddition(&libyaml_local_output, &mode_stack, &map_mode_stack, map_mode, &key_stack);

                break;
            case YAML_MAPPING_START_EVENT:
                TEST_PPRINT("MAP+\n");

                libyaml_local_output.push_back(YAML::Node(YAML::NodeType::Map));
                AddTag(&libyaml_local_output.back(), event->data.sequence_start.tag);

                if (!mode_stack.empty())
                {
                    PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                    if (mode_stack.top() ==  mode_type::MAP_TYPE)
                    {
                        map_mode_stack.push(!map_mode);
                    }
                }

                mode_stack.push(mode_type::MAP_TYPE);
                map_mode = true;

                if (event->data.mapping_start.anchor)
                {
                    TEST_PPRINT("ANCH-map+\n");
                    anchor_map[std::string((char*)event->data.mapping_start.anchor)] = libyaml_local_output.back();
                }

                break;
            case YAML_SEQUENCE_START_EVENT:
                TEST_PPRINT("SQU+\n");
                libyaml_local_output.push_back(YAML::Node(YAML::NodeType::Sequence));
                AddTag(&libyaml_local_output.back(), event->data.sequence_start.tag);

                if (!mode_stack.empty())
                {
                    if (mode_stack.top() ==  mode_type::MAP_TYPE)
                    {
                        map_mode_stack.push(!map_mode);
                    }
                }

                mode_stack.push(mode_type::SEQUENCE_TYPE);

                if (event->data.sequence_start.anchor)
                {
                    TEST_PPRINT("ANCH-squ+\n");

                    anchor_map[std::string((char*)event->data.sequence_start.anchor)] = libyaml_local_output.back();
                }

                break;

            case YAML_SCALAR_EVENT:
            {
                TEST_PPRINT("SCL\n");

                YAML::Node add_me(std::string((char*)event->data.scalar.value, event->data.scalar.length));
                AddTag(&add_me, event->data.scalar.tag);

                if (event->data.scalar.anchor)
                {
                    TEST_PPRINT("ANCH-scl\n");
                    std::string temp_translator = ((char*)event->data.scalar.anchor);
                    if (mode_stack.empty())
                    {
                        break;
                    }

                    if (event->data.scalar.value)
                    {
                        TEST_PPRINT("value\n");

                        if(event->data.scalar.length != 0)
                        {
                            anchor_map[temp_translator] = add_me;

                            map_mode = PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                            if (libyaml_local_output.empty())
                            {
                                libyaml_local_output.push_back(add_me);

                            }
                            else
                            {
                                AddToNode(&libyaml_local_output.back(), &add_me, &key_stack, &tracking_current_type,
                                    event->data.scalar.tag);
                            }
                        }
                        else
                        {
                            TEST_PPRINT("empty\n");

                            if (mode_stack.top() ==  mode_type::SEQUENCE_TYPE)
                            {
                                TEST_PPRINT("sequence\n");
                                add_me = YAML::Node(YAML::NodeType::Null);

                                if (!libyaml_local_output.empty())
                                {
                                    if (libyaml_local_output.back().IsSequence())
                                    {
                                        TEST_PPRINT("insert\n");
                                        libyaml_local_output.back().push_back(add_me);
                                    }
                                }

                                break;
                            }

                            if (mode_stack.top() ==  mode_type::MAP_TYPE && !map_mode)
                            {
                                map_mode = PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                                add_me = YAML::Node(YAML::NodeType::Null);

                                AddToNode(&libyaml_local_output.back(), &add_me, &key_stack, &tracking_current_type,
                                    event->data.scalar.tag);

                                break;
                            }
                            mode_stack.pop();

                            if (!mode_stack.empty())
                            {
                                if (mode_stack.top() ==  mode_type::MAP_TYPE)
                                {
                                    TEST_PPRINT("map\n");
                                    map_mode = map_mode_stack.top();
                                    map_mode_stack.pop();
                                }

                                if (!libyaml_local_output.empty())
                                {
                                    TEST_PPRINT("pop\n");
                                    libyaml_local_output.pop_back();
                                }
                            }
                            else
                            {
                                TEST_PPRINT("insert\n");
                                libyaml_local_output.push_back(YAML::Node());
                            }
                        }
                    }
                }
                else
                {
                    TEST_PPRINT("normal\n");
                    if (mode_stack.empty())
                    {
                        break;
                    }
                    map_mode = PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                    if (event->data.scalar.length <= 0 && !RelevantTag(event->data.scalar.tag) &&
                            event->data.scalar.style == YAML_PLAIN_SCALAR_STYLE)
                    {
                        TEST_PPRINT("Begin from nothing\n");
                        add_me = YAML::Node();
                    }

                    if (libyaml_local_output.empty())
                    {
                        libyaml_local_output.push_back(add_me);
                    }
                    else
                    {
                        TEST_PPRINT("Add to node\n");
                        AddToNode(&libyaml_local_output.back(), &add_me, &key_stack, &tracking_current_type,
                            event->data.scalar.tag);
                    }
                }

                break;
            }
            case YAML_ALIAS_EVENT:
            {
                TEST_PPRINT("ALI\n");

                std::string temp_translator = ((char*) event->data.alias.anchor);

                if(anchor_map.find(temp_translator) != anchor_map.end())
                {
                    if (mode_stack.empty())
                    {
                        break;
                    }

                    map_mode = PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                    AddToNode(&libyaml_local_output.back(), &anchor_map[temp_translator],
                        &key_stack, &tracking_current_type, nullptr);
                }
                else
                {
                    WipeEventList(event_list.get());

                    TEST_PPRINT("ERROR: Missing anchor\n");

                    *error_message_container = std::string("ERROR");

                    return libyaml_final_output;
                }
                break;
            }
            default:
                break;
        }
        // if (!libyaml_local_output.empty())
        // {
        //     std::cout << "------------------" << std::endl;
        //     std::cout << libyaml_local_output.back() << std::endl;
        //     std::cout << "------------------" << std::endl;
        // }
        TEST_PPRINT("---------\n");
    }

    WipeEventList(event_list.get());

    fflush(stdout);

    return libyaml_final_output;
}<commit_msg>Minor modifications to libyaml normalization<commit_after>#include "libyaml_utils.h"
#include "logger.h"

// ---------------------------------------------------------------------------------
// ------------------------------- libyaml test code -------------------------------
// ---------------------------------------------------------------------------------
namespace
{

bool PositionAnalysis(mode_type* add_to_me, const mode_type reference_character, const bool map_mode)
{
    if (add_to_me != nullptr)
    {
        if (reference_character ==  mode_type::MAP_TYPE)
        {
            if (map_mode)
            {
                *add_to_me =  mode_type::KEY_TYPE;
            }
            else
            {
                *add_to_me =  mode_type::VALUE_TYPE;
            }
            return !map_mode;
        }
        else if (reference_character ==  mode_type::SEQUENCE_TYPE)
        {
            *add_to_me =  mode_type::SEQUENCE_TYPE;
        }
        else
        {
            *add_to_me =  mode_type::UNKNOWN_TYPE;
        }
    }
    return map_mode;
}

void AddTag(YAML::Node* current_node, yaml_char_t* tag)
{
    if (current_node != nullptr)
    {
        if (tag)
        {
            std::string temp_tag_translator = ((char*) tag);

            current_node->SetTag(temp_tag_translator);
        }
        else if(current_node->Tag().empty())
        {
            current_node->SetTag("?");
        }
    }
}

void AddToNode
    (YAML::Node* addToMe, YAML::Node* add_me, std::stack<YAML::Node>* key_stack,
    const mode_type* tracking_current_type, yaml_char_t* tag)
{
    AddTag(add_me, tag);

    if (tracking_current_type != nullptr && addToMe != nullptr)
    {
        if (*tracking_current_type ==  mode_type::SEQUENCE_TYPE && addToMe->IsSequence())
        {
            TEST_PPRINT("squ type\n")
            addToMe->push_back(*add_me);
        }
        else if (*tracking_current_type ==  mode_type::KEY_TYPE && addToMe->IsMap())
        {
            TEST_PPRINT("key type\n")
            key_stack->push(*add_me);
            (*addToMe)[*add_me];
        }
        else if (*tracking_current_type ==  mode_type::VALUE_TYPE && addToMe->IsMap())
        {
            TEST_PPRINT("map type\n")
            if (!key_stack->empty())
            {
                (*addToMe)[key_stack->top()] = *add_me;
                key_stack->pop();
            }
        }
        else
        {
            TEST_PPRINT("? type\n")
        }
    }
}

bool EndEventAddition
    (std::vector<YAML::Node>* libyaml_local_output, std::stack<mode_type>* mode_stack,
    std::stack<bool>* map_mode_stack, bool map_mode, std::stack<YAML::Node>* key_stack)
{
    if (libyaml_local_output->size() > 1 && mode_stack->size() > 1)
    {
        mode_stack->pop();

        if (mode_stack->top() == mode_type::MAP_TYPE && !map_mode_stack->empty())
        {
            map_mode = map_mode_stack->top();
            map_mode_stack->pop();
        }
        mode_type temp_position_info;

        if (mode_stack->empty())
        {
            return map_mode;
        }
        PositionAnalysis(&temp_position_info, (mode_stack->top()), !map_mode);

        YAML::Node temp_node = libyaml_local_output->back();

        libyaml_local_output->pop_back();

        AddToNode(&libyaml_local_output->back(), &temp_node, key_stack, &temp_position_info, nullptr);
    }
    return map_mode;
}

void RestartVariables (std::stack<YAML::Node>* key_stack,
    std::stack<mode_type>* mode_stack, std::stack<bool>* map_mode_stack,
    std::vector<YAML::Node>* libyaml_local_output, std::vector<YAML::Node>* libyaml_final_output,
    bool* map_mode, std::map<std::string, YAML::Node>* anchor_map)
{
    while (!key_stack->empty())
    {
        key_stack->pop();
    }

    while (!mode_stack->empty())
    {
        mode_stack->pop();
    }
    mode_stack->push(mode_type::UNKNOWN_TYPE);

    while (!map_mode_stack->empty())
    {
        map_mode_stack->pop();
    }

    if (!libyaml_local_output->empty())
    {
        libyaml_final_output->push_back(libyaml_local_output->back());
    }

    libyaml_local_output->clear();

    *map_mode = true;

    anchor_map->clear();
}

std::unique_ptr<std::vector<yaml_event_t>> GetEvents
    (const uint8_t* input, size_t input_size, std::string* error_message_container)
{
    std::unique_ptr<std::vector<yaml_event_t>> event_list(new std::vector<yaml_event_t>);

    yaml_parser_t parser;
    yaml_event_t event;

    if (!yaml_parser_initialize(&parser))
    {
        TEST_PPRINT("ERROR: Failed to initialize\n");

        *error_message_container = std::string("ERROR");

        return std::move(event_list);
    }

    yaml_parser_set_input_string(&parser, input, input_size);

    int done = 0;

    while (true)
    {
        yaml_event_type_t type;

        type = event.type;

        if (!yaml_parser_parse(&parser, &event))
        {
            yaml_event_delete(&event);

            TEST_PPRINT("ERROR: Bad parsing\n");

            *error_message_container = std::string("ERROR");

            WipeEventList(event_list.get());

            break;
        }

        event_list.get()->push_back(event);

        if (type == YAML_STREAM_END_EVENT)
        {
            break;
        }
    }

    yaml_parser_delete(&parser);

    fflush(stdout);

    return std::move(event_list);
}

void WipeEventList(std::vector<yaml_event_t>* event_list)
{
    for (std::vector<yaml_event_t>::iterator it = event_list->begin();
        it != event_list->end(); ++ it)
    {
        yaml_event_delete(&(*it));
    }
    event_list->clear();
}

bool RelevantTag(yaml_char_t* check_tag)
{
    if (check_tag != nullptr)
    {
        std::string temp_tag_translator = ((char*) check_tag);

        return temp_tag_translator != "!" && temp_tag_translator != "?";
    }
    return false;
}

}

std::vector<YAML::Node>* libyaml_parsing::ParseLibyaml
    (const uint8_t* input, size_t input_size, std::string* error_message_container)
{
    std::vector<YAML::Node> libyaml_local_output;

    std::vector<YAML::Node>* libyaml_final_output = new std::vector<YAML::Node>;

    std::stack<YAML::Node> key_stack;

    std::stack<mode_type> mode_stack;

    mode_stack.push(mode_type::UNKNOWN_TYPE);

    std::stack<bool> map_mode_stack;

    bool map_mode = true;

    std::map<std::string, YAML::Node> anchor_map;

    std::unique_ptr<std::vector<yaml_event_t>> event_list =
        std::move(GetEvents(input, input_size, error_message_container));

    for (std::vector<yaml_event_t>::iterator event = event_list.get()->begin();
        event != event_list.get()->end(); ++ event)
    {
        mode_type tracking_current_type;

        switch (event->type)
        {
            case YAML_STREAM_END_EVENT:
                TEST_PPRINT("STR-\n");

                break;
            case YAML_DOCUMENT_END_EVENT:
                TEST_PPRINT("DOC-\n");

                RestartVariables(&key_stack, &mode_stack, &map_mode_stack, &libyaml_local_output,
                    libyaml_final_output, &map_mode, &anchor_map);

                break;
            case YAML_DOCUMENT_START_EVENT:
                TEST_PPRINT("DOC+\n");

                RestartVariables(&key_stack, &mode_stack, &map_mode_stack, &libyaml_local_output,
                    libyaml_final_output, &map_mode, &anchor_map);

                break;

            case YAML_MAPPING_END_EVENT:
                TEST_PPRINT("MAP-\n");

                map_mode = EndEventAddition(&libyaml_local_output, &mode_stack, &map_mode_stack, map_mode, &key_stack);

                break;
            case YAML_SEQUENCE_END_EVENT:
                TEST_PPRINT("SQU-\n");

                map_mode = EndEventAddition(&libyaml_local_output, &mode_stack, &map_mode_stack, map_mode, &key_stack);

                break;
            case YAML_MAPPING_START_EVENT:
                TEST_PPRINT("MAP+\n");

                libyaml_local_output.push_back(YAML::Node(YAML::NodeType::Map));
                AddTag(&libyaml_local_output.back(), event->data.sequence_start.tag);

                if (!mode_stack.empty())
                {
                    PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                    if (mode_stack.top() ==  mode_type::MAP_TYPE)
                    {
                        map_mode_stack.push(!map_mode);
                    }
                }

                mode_stack.push(mode_type::MAP_TYPE);
                map_mode = true;

                if (event->data.mapping_start.anchor)
                {
                    TEST_PPRINT("ANCH-map+\n");
                    anchor_map[std::string((char*)event->data.mapping_start.anchor)] = libyaml_local_output.back();
                }

                break;
            case YAML_SEQUENCE_START_EVENT:
                TEST_PPRINT("SQU+\n");
                libyaml_local_output.push_back(YAML::Node(YAML::NodeType::Sequence));
                AddTag(&libyaml_local_output.back(), event->data.sequence_start.tag);

                if (!mode_stack.empty())
                {
                    if (mode_stack.top() ==  mode_type::MAP_TYPE)
                    {
                        map_mode_stack.push(!map_mode);
                    }
                }

                mode_stack.push(mode_type::SEQUENCE_TYPE);

                if (event->data.sequence_start.anchor)
                {
                    TEST_PPRINT("ANCH-squ+\n");

                    anchor_map[std::string((char*)event->data.sequence_start.anchor)] = libyaml_local_output.back();
                }

                break;

            case YAML_SCALAR_EVENT:
            {
                TEST_PPRINT("SCL\n");

                YAML::Node add_me(std::string((char*)event->data.scalar.value, event->data.scalar.length));
                AddTag(&add_me, event->data.scalar.tag);

                if (event->data.scalar.anchor)
                {
                    TEST_PPRINT("ANCH-scl\n");
                    std::string temp_translator = ((char*)event->data.scalar.anchor);
                    if (mode_stack.empty())
                    {
                        break;
                    }

                    if (event->data.scalar.value)
                    {
                        TEST_PPRINT("value\n");

                        if(event->data.scalar.length != 0)
                        {
                            anchor_map[temp_translator] = add_me;

                            map_mode = PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                            if (libyaml_local_output.empty())
                            {
                                libyaml_local_output.push_back(add_me);

                            }
                            else
                            {
                                AddToNode(&libyaml_local_output.back(), &add_me, &key_stack, &tracking_current_type,
                                    event->data.scalar.tag);
                            }
                        }
                        else
                        {
                            TEST_PPRINT("empty\n");

                            if (mode_stack.top() ==  mode_type::SEQUENCE_TYPE)
                            {
                                TEST_PPRINT("sequence\n");
                                add_me = YAML::Node(YAML::NodeType::Null);

                                if (!libyaml_local_output.empty())
                                {
                                    if (libyaml_local_output.back().IsSequence())
                                    {
                                        TEST_PPRINT("insert\n");
                                        libyaml_local_output.back().push_back(add_me);
                                    }
                                }

                                break;
                            }

                            if (mode_stack.top() ==  mode_type::MAP_TYPE && !map_mode)
                            {
                                map_mode = PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                                add_me = YAML::Node(YAML::NodeType::Null);

                                AddToNode(&libyaml_local_output.back(), &add_me, &key_stack, &tracking_current_type,
                                    event->data.scalar.tag);

                                break;
                            }
                            mode_stack.pop();

                            if (!mode_stack.empty())
                            {
                                if (mode_stack.top() ==  mode_type::MAP_TYPE)
                                {
                                    TEST_PPRINT("map\n");
                                    map_mode = map_mode_stack.top();
                                    map_mode_stack.pop();
                                }

                                if (!libyaml_local_output.empty())
                                {
                                    TEST_PPRINT("pop\n");
                                    libyaml_local_output.pop_back();
                                }
                            }
                            else
                            {
                                TEST_PPRINT("insert\n");
                                libyaml_local_output.push_back(YAML::Node());
                            }
                        }
                    }
                }
                else
                {
                    TEST_PPRINT("normal\n");
                    if (mode_stack.empty())
                    {
                        break;
                    }
                    map_mode = PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                    if (event->data.scalar.length <= 0 && !RelevantTag(event->data.scalar.tag) &&
                            event->data.scalar.style == YAML_PLAIN_SCALAR_STYLE)
                    {
                        TEST_PPRINT("Begin from nothing\n");
                        add_me = YAML::Node();
                    }

                    if (libyaml_local_output.empty())
                    {
                        libyaml_local_output.push_back(add_me);
                    }
                    else
                    {
                        TEST_PPRINT("Add to node\n");
                        AddToNode(&libyaml_local_output.back(), &add_me, &key_stack, &tracking_current_type,
                            event->data.scalar.tag);
                    }
                }

                break;
            }
            case YAML_ALIAS_EVENT:
            {
                TEST_PPRINT("ALI\n");

                std::string temp_translator = ((char*) event->data.alias.anchor);

                if(anchor_map.find(temp_translator) != anchor_map.end())
                {
                    if (mode_stack.empty())
                    {
                        break;
                    }

                    map_mode = PositionAnalysis(&tracking_current_type, mode_stack.top(), map_mode);

                    AddToNode(&libyaml_local_output.back(), &anchor_map[temp_translator],
                        &key_stack, &tracking_current_type, nullptr);
                }
                else
                {
                    WipeEventList(event_list.get());

                    TEST_PPRINT("ERROR: Missing anchor\n");

                    *error_message_container = std::string("ERROR");

                    return libyaml_final_output;
                }
                break;
            }
            default:
                break;
        }
        // if (!libyaml_local_output.empty())
        // {
        //     std::cout << "------------------" << std::endl;
        //     std::cout << libyaml_local_output.back() << std::endl;
        //     std::cout << "------------------" << std::endl;
        // }
        TEST_PPRINT("---------\n");
    }

    WipeEventList(event_list.get());

    fflush(stdout);

    return libyaml_final_output;
}<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#include <osgUtil/Statistics>

#include <osg/PrimitiveSet>
#include <osg/Drawable>
#include <osg/NodeVisitor>
#include <osg/Geode>
#include <osg/LOD>
#include <osg/Switch>
#include <osg/Geometry>
#include <osg/Transform>

#include <map>
#include <set>
#include <ostream>
#include <iomanip>

using namespace osgUtil;

Statistics::Statistics()
{
    reset();
}

void Statistics::reset()
{
    numDrawables=0;
    numFastDrawables=0;
    nummat=0;
    depth=0;
    stattype=STAT_NONE;
    nlights=0;
    nbins=0;
    nimpostor=0;
    numStateGraphs=0;
    numOrderedLeaves=0;

    _binNo = 0;

    _vertexCount=0;
    _primitiveCount.clear();

    _currentPrimitiveFunctorMode=0;

    _primitives_count.clear();
    _total_primitives_count=0;
    _number_of_vertexes=0;
}

void Statistics::drawArrays(GLenum mode,GLint,GLsizei count)
{
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    prim.second+=count;
    _primitives_count[mode] += _calculate_primitives_number_by_mode(mode, count);
}

void Statistics::drawElements(GLenum mode,GLsizei count,const GLubyte*)
{
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    prim.second+=count;
    _primitives_count[mode] += _calculate_primitives_number_by_mode(mode, count);
}

void Statistics::drawElements(GLenum mode,GLsizei count,const GLushort*)
{
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    prim.second+=count;
    _primitives_count[mode] += _calculate_primitives_number_by_mode(mode, count);
}

void Statistics::drawElements(GLenum mode,GLsizei count,const GLuint*)
{
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    prim.second+=count;
    _primitives_count[mode] += _calculate_primitives_number_by_mode(mode, count);
}

void Statistics::begin(GLenum mode)
{
    _currentPrimitiveFunctorMode=mode;
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    _number_of_vertexes = 0;
}

void Statistics::end()
{
    _primitives_count[_currentPrimitiveFunctorMode] +=
        _calculate_primitives_number_by_mode(_currentPrimitiveFunctorMode, _number_of_vertexes);

    _vertexCount += _number_of_vertexes;
}

void Statistics::add(const Statistics& stats)
{
    numDrawables += stats.numDrawables;
    numFastDrawables += stats.numFastDrawables;
    nummat += stats.nummat;
    depth += stats.depth;
    nlights += stats.nlights;
    nbins += stats.nbins;
    nimpostor += stats.nimpostor;
    numStateGraphs += stats.numStateGraphs;
    numOrderedLeaves += stats.numOrderedLeaves;

    _vertexCount += stats._vertexCount;
    for(PrimitiveValueMap::const_iterator pitr = stats._primitiveCount.begin();
        pitr != stats._primitiveCount.end();
        ++pitr)
    {
        _primitiveCount[pitr->first].first += pitr->second.first;
        _primitiveCount[pitr->first].second += pitr->second.second;
    }

    _currentPrimitiveFunctorMode += stats._currentPrimitiveFunctorMode;

    for(PrimitiveCountMap::const_iterator citr = stats._primitives_count.begin();
        citr != stats._primitives_count.end();
        ++citr)
    {
        _primitives_count[citr->first] += citr->second;
    }

    _total_primitives_count += stats._total_primitives_count;
    _number_of_vertexes += stats._number_of_vertexes;
}

StatsVisitor::StatsVisitor():
    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
    _numInstancedGroup(0),
    _numInstancedSwitch(0),
    _numInstancedLOD(0),
    _numInstancedTransform(0),
    _numInstancedGeode(0),
    _numInstancedDrawable(0),
    _numInstancedGeometry(0),
    _numInstancedFastGeometry(0),
    _numInstancedStateSet(0)
{}

void StatsVisitor::reset()
{
    _numInstancedGroup = 0;
    _numInstancedSwitch = 0;
    _numInstancedLOD = 0;
    _numInstancedTransform = 0;
    _numInstancedGeode = 0;
    _numInstancedDrawable = 0;
    _numInstancedGeometry = 0;
    _numInstancedFastGeometry = 0;
    _numInstancedStateSet = 0;

    _groupSet.clear();
    _transformSet.clear();
    _lodSet.clear();
    _switchSet.clear();
    _geodeSet.clear();
    _drawableSet.clear();
    _geometrySet.clear();
    _fastGeometrySet.clear();
    _statesetSet.clear();

    _uniqueStats.reset();
    _instancedStats.reset();
}

void StatsVisitor::apply(osg::Node& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    traverse(node);
}

void StatsVisitor::apply(osg::Group& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedGroup;
    _groupSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::Transform& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedTransform;
    _transformSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::LOD& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedLOD;
    _lodSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::Switch& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedSwitch;
    _switchSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::Geode& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedGeode;
    _geodeSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::Drawable& drawable)
{
    if (drawable.getStateSet())
    {
        apply(*drawable.getStateSet());
    }

    ++_numInstancedDrawable;

    drawable.accept(_instancedStats);

    _drawableSet.insert(&drawable);

    osg::Geometry* geometry = drawable.asGeometry();
    if (geometry)
    {
        ++_numInstancedGeometry;
        _geometrySet.insert(geometry);

        ++_numInstancedFastGeometry;
        _fastGeometrySet.insert(geometry);
    }
}

void StatsVisitor::apply(osg::StateSet& stateSet)
{
   ++_numInstancedStateSet;
   _statesetSet.insert(&stateSet);
}

void StatsVisitor::totalUpStats()
{
    _uniqueStats.reset();

    for(DrawableSet::iterator itr = _drawableSet.begin();
        itr != _drawableSet.end();
        ++itr)
    {
        (*itr)->accept(_uniqueStats);
    }
}

void StatsVisitor::print(std::ostream& out)
{
    unsigned int unique_primitives = 0;
    osgUtil::Statistics::PrimitiveCountMap::iterator pcmitr;
    for(pcmitr = _uniqueStats.GetPrimitivesBegin();
        pcmitr != _uniqueStats.GetPrimitivesEnd();
        ++pcmitr)
    {
        unique_primitives += pcmitr->second;
    }

    unsigned int instanced_primitives = 0;
    for(pcmitr = _instancedStats.GetPrimitivesBegin();
        pcmitr != _instancedStats.GetPrimitivesEnd();
        ++pcmitr)
    {
        instanced_primitives += pcmitr->second;
    }

    out << std::setw(12) << "Object Type" << std::setw(10) << "Unique"                  << std::setw(10) << "Instanced" << std::endl;
    out << std::setw(12) << "-----------" << std::setw(10) << "------"                  << std::setw(10) << "---------" << std::endl;
    out << std::setw(12) << "StateSet   " << std::setw(10) << _statesetSet.size()       << std::setw(10) << _numInstancedStateSet << std::endl;
    out << std::setw(12) << "Group      " << std::setw(10) << _groupSet.size()          << std::setw(10) << _numInstancedGroup << std::endl;
    out << std::setw(12) << "Transform  " << std::setw(10) << _transformSet.size()      << std::setw(10) << _numInstancedTransform << std::endl;
    out << std::setw(12) << "LOD        " << std::setw(10) << _lodSet.size()            << std::setw(10) << _numInstancedLOD << std::endl;
    out << std::setw(12) << "Switch     " << std::setw(10) << _switchSet.size()         << std::setw(10) << _numInstancedSwitch << std::endl;
    out << std::setw(12) << "Geode      " << std::setw(10) << _geodeSet.size()          << std::setw(10) << _numInstancedGeode << std::endl;
    out << std::setw(12) << "Drawable   " << std::setw(10) << _drawableSet.size()       << std::setw(10) << _numInstancedDrawable << std::endl;
    out << std::setw(12) << "Geometry   " << std::setw(10) << _geometrySet.size()       << std::setw(10) << _numInstancedGeometry << std::endl;
    out << std::setw(12) << "Fast geom. " << std::setw(10) << _fastGeometrySet.size()   << std::setw(10) << _numInstancedFastGeometry << std::endl;
    out << std::setw(12) << "Vertices   " << std::setw(10) << _uniqueStats._vertexCount << std::setw(10) << _instancedStats._vertexCount << std::endl;
    out << std::setw(12) << "Primitives " << std::setw(10) << unique_primitives         << std::setw(10) << instanced_primitives << std::endl;
}

<commit_msg>added some info about dynamic drawables and transparent bin to Statistics<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#include <osgUtil/Statistics>

#include <osg/PrimitiveSet>
#include <osg/Drawable>
#include <osg/NodeVisitor>
#include <osg/Geode>
#include <osg/LOD>
#include <osg/Switch>
#include <osg/Geometry>
#include <osg/Transform>

#include <map>
#include <set>
#include <ostream>
#include <iomanip>

using namespace osgUtil;

Statistics::Statistics()
{
    reset();
}

void Statistics::reset()
{
    numDrawables=0;
    numFastDrawables=0;
    nummat=0;
    depth=0;
    stattype=STAT_NONE;
    nlights=0;
    nbins=0;
    nimpostor=0;
    numStateGraphs=0;
    numOrderedLeaves=0;

    _binNo = 0;

    _vertexCount=0;
    _primitiveCount.clear();

    _currentPrimitiveFunctorMode=0;

    _primitives_count.clear();
    _total_primitives_count=0;
    _number_of_vertexes=0;
}

void Statistics::drawArrays(GLenum mode,GLint,GLsizei count)
{
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    prim.second+=count;
    _primitives_count[mode] += _calculate_primitives_number_by_mode(mode, count);
}

void Statistics::drawElements(GLenum mode,GLsizei count,const GLubyte*)
{
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    prim.second+=count;
    _primitives_count[mode] += _calculate_primitives_number_by_mode(mode, count);
}

void Statistics::drawElements(GLenum mode,GLsizei count,const GLushort*)
{
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    prim.second+=count;
    _primitives_count[mode] += _calculate_primitives_number_by_mode(mode, count);
}

void Statistics::drawElements(GLenum mode,GLsizei count,const GLuint*)
{
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    prim.second+=count;
    _primitives_count[mode] += _calculate_primitives_number_by_mode(mode, count);
}

void Statistics::begin(GLenum mode)
{
    _currentPrimitiveFunctorMode=mode;
    PrimitivePair& prim = _primitiveCount[mode];
    ++prim.first;
    _number_of_vertexes = 0;
}

void Statistics::end()
{
    _primitives_count[_currentPrimitiveFunctorMode] +=
        _calculate_primitives_number_by_mode(_currentPrimitiveFunctorMode, _number_of_vertexes);

    _vertexCount += _number_of_vertexes;
}

void Statistics::add(const Statistics& stats)
{
    numDrawables += stats.numDrawables;
    numFastDrawables += stats.numFastDrawables;
    nummat += stats.nummat;
    depth += stats.depth;
    nlights += stats.nlights;
    nbins += stats.nbins;
    nimpostor += stats.nimpostor;
    numStateGraphs += stats.numStateGraphs;
    numOrderedLeaves += stats.numOrderedLeaves;

    _vertexCount += stats._vertexCount;
    for(PrimitiveValueMap::const_iterator pitr = stats._primitiveCount.begin();
        pitr != stats._primitiveCount.end();
        ++pitr)
    {
        _primitiveCount[pitr->first].first += pitr->second.first;
        _primitiveCount[pitr->first].second += pitr->second.second;
    }

    _currentPrimitiveFunctorMode += stats._currentPrimitiveFunctorMode;

    for(PrimitiveCountMap::const_iterator citr = stats._primitives_count.begin();
        citr != stats._primitives_count.end();
        ++citr)
    {
        _primitives_count[citr->first] += citr->second;
    }

    _total_primitives_count += stats._total_primitives_count;
    _number_of_vertexes += stats._number_of_vertexes;
}

StatsVisitor::StatsVisitor():
    osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
    _numInstancedGroup(0),
    _numInstancedSwitch(0),
    _numInstancedLOD(0),
    _numInstancedTransform(0),
    _numInstancedGeode(0),
    _numInstancedDrawable(0),
    _numInstancedGeometry(0),
    _numInstancedFastGeometry(0),
    _numInstancedStateSet(0)
{}

void StatsVisitor::reset()
{
    _numInstancedGroup = 0;
    _numInstancedSwitch = 0;
    _numInstancedLOD = 0;
    _numInstancedTransform = 0;
    _numInstancedGeode = 0;
    _numInstancedDrawable = 0;
    _numInstancedGeometry = 0;
    _numInstancedFastGeometry = 0;
    _numInstancedStateSet = 0;

    _groupSet.clear();
    _transformSet.clear();
    _lodSet.clear();
    _switchSet.clear();
    _geodeSet.clear();
    _drawableSet.clear();
    _geometrySet.clear();
    _fastGeometrySet.clear();
    _statesetSet.clear();

    _uniqueStats.reset();
    _instancedStats.reset();
}

void StatsVisitor::apply(osg::Node& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    traverse(node);
}

void StatsVisitor::apply(osg::Group& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedGroup;
    _groupSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::Transform& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedTransform;
    _transformSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::LOD& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedLOD;
    _lodSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::Switch& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedSwitch;
    _switchSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::Geode& node)
{
    if (node.getStateSet())
    {
        apply(*node.getStateSet());
    }

    ++_numInstancedGeode;
    _geodeSet.insert(&node);

    traverse(node);
}

void StatsVisitor::apply(osg::Drawable& drawable)
{
    if (drawable.getStateSet())
    {
        apply(*drawable.getStateSet());
    }

    ++_numInstancedDrawable;

    drawable.accept(_instancedStats);

    _drawableSet.insert(&drawable);

    osg::Geometry* geometry = drawable.asGeometry();
    if (geometry)
    {
        ++_numInstancedGeometry;
        _geometrySet.insert(geometry);

        ++_numInstancedFastGeometry;
        _fastGeometrySet.insert(geometry);
    }
}

void StatsVisitor::apply(osg::StateSet& stateSet)
{
   ++_numInstancedStateSet;
   _statesetSet.insert(&stateSet);
}

void StatsVisitor::totalUpStats()
{
    _uniqueStats.reset();

    for(DrawableSet::iterator itr = _drawableSet.begin();
        itr != _drawableSet.end();
        ++itr)
    {
        (*itr)->accept(_uniqueStats);
    }
}

void StatsVisitor::print(std::ostream& out)
{
    unsigned int unique_primitives = 0;
    osgUtil::Statistics::PrimitiveCountMap::iterator pcmitr;
    for(pcmitr = _uniqueStats.GetPrimitivesBegin();
        pcmitr != _uniqueStats.GetPrimitivesEnd();
        ++pcmitr)
    {
        unique_primitives += pcmitr->second;
    }

    unsigned int instanced_primitives = 0;
    for(pcmitr = _instancedStats.GetPrimitivesBegin();
        pcmitr != _instancedStats.GetPrimitivesEnd();
        ++pcmitr)
    {
        instanced_primitives += pcmitr->second;
    }

    unsigned int dynamic_statesets = 0;
    unsigned int non_default_renderbin_statesets = 0;
    unsigned int bin10_statesets = 0;
    for (StateSetSet::iterator statsetitr = _statesetSet.begin();
        statsetitr != _statesetSet.end();
        ++statsetitr)
    {
        if ((*statsetitr)->getDataVariance() == osg::Object::DYNAMIC) ++dynamic_statesets;
        if ((*statsetitr)->getRenderBinMode() != osg::StateSet::INHERIT_RENDERBIN_DETAILS) {
            ++non_default_renderbin_statesets;
            if ((*statsetitr)->getBinNumber() == 10) ++bin10_statesets;
        }
    }
    unsigned int dynamic_drawables = 0;
    for (DrawableSet::iterator drawableitr = _drawableSet.begin();
        drawableitr != _drawableSet.end();
        ++drawableitr)
    {
        if ((*drawableitr)->getDataVariance() == osg::Object::DYNAMIC) ++dynamic_drawables;
    }
    if (dynamic_drawables != 0 || dynamic_statesets != 0) {
        out << std::setw(12) << "Object Type" << std::setw(10) << "Unique"                  << std::setw(10) << "Instanced" << std::setw(10) << "Dynamic" << std::endl;
        out << std::setw(12) << "-----------" << std::setw(10) << "------"                  << std::setw(10) << "---------" << std::setw(10) << "---------" << std::endl;
    } else {
        out << std::setw(12) << "Object Type" << std::setw(10) << "Unique" << std::setw(10) << "Instanced" << std::endl;
        out << std::setw(12) << "-----------" << std::setw(10) << "------" << std::setw(10) << "---------" << std::endl;
    }
    out << std::setw(12) << "StateSet   " << std::setw(10) << _statesetSet.size() << std::setw(10) << _numInstancedStateSet << std::setw(10) << dynamic_statesets << std::endl;
    if (bin10_statesets != 0) out << std::setw(12) << "  bin 10   " << std::setw(10) << bin10_statesets << std::endl;
    if (non_default_renderbin_statesets != 0) out << std::setw(12) << "  other bin" << std::setw(10) << non_default_renderbin_statesets - bin10_statesets << std::endl;
    out << std::setw(12) << "Group      " << std::setw(10) << _groupSet.size() << std::setw(10) << _numInstancedGroup << std::endl;
    out << std::setw(12) << "Transform  " << std::setw(10) << _transformSet.size()      << std::setw(10) << _numInstancedTransform << std::endl;
    out << std::setw(12) << "LOD        " << std::setw(10) << _lodSet.size()            << std::setw(10) << _numInstancedLOD << std::endl;
    out << std::setw(12) << "Switch     " << std::setw(10) << _switchSet.size()         << std::setw(10) << _numInstancedSwitch << std::endl;
    out << std::setw(12) << "Geode      " << std::setw(10) << _geodeSet.size()          << std::setw(10) << _numInstancedGeode << std::endl;
    out << std::setw(12) << "Drawable   " << std::setw(10) << _drawableSet.size()       << std::setw(10) << _numInstancedDrawable << std::setw(10) << dynamic_drawables << std::endl;
    out << std::setw(12) << "Geometry   " << std::setw(10) << _geometrySet.size()       << std::setw(10) << _numInstancedGeometry << std::endl;
    out << std::setw(12) << "Fast geom. " << std::setw(10) << _fastGeometrySet.size()   << std::setw(10) << _numInstancedFastGeometry << std::endl;
    out << std::setw(12) << "Vertices   " << std::setw(10) << _uniqueStats._vertexCount << std::setw(10) << _instancedStats._vertexCount << std::endl;
    out << std::setw(12) << "Primitives " << std::setw(10) << unique_primitives         << std::setw(10) << instanced_primitives << std::endl;
}

<|endoftext|>


<commit_before>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2011 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or( at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygencairocontext.h"
#include "oxygencairoutils.h"
#include "config.h"
#include "oxygengtkutils.h"
#include "oxygenmetrics.h"
#include "oxygenrgba.h"
#include "oxygenshadowhelper.h"

#include "config.h"

#include <iostream>
#include <cairo/cairo.h>

#ifdef GDK_WINDOWING_X11
#include <cairo/cairo-xlib.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#endif

namespace Oxygen
{

    //______________________________________________
    ShadowHelper::ShadowHelper( void ):
        _size(0),
        _hooksInitialized( false )
    {

        #ifdef GDK_WINDOWING_X11
        _atom = None;
        #endif

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::ShadowHelper" << std::endl;
        #endif
    }

    //______________________________________________
    ShadowHelper::~ShadowHelper( void )
    {
        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::~ShadowHelper" << std::endl;
        #endif

        reset();
        _realizeHook.disconnect();

    }

    //______________________________________________
    void ShadowHelper::reset( void )
    {

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::reset" << std::endl;
        #endif

        #ifdef GDK_WINDOWING_X11
        GdkScreen* screen = gdk_screen_get_default();
        if( !screen ) return;

        Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) );

        // round pixmaps
        for( PixmapList::const_iterator iter = _roundPixmaps.begin(); iter != _roundPixmaps.end(); ++iter )
        { XFreePixmap(display, *iter); }

        // square pixmaps
        for( PixmapList::const_iterator iter = _squarePixmaps.begin(); iter != _squarePixmaps.end(); ++iter )
        { XFreePixmap(display, *iter); }
        #endif

        // clear arrays
        _roundPixmaps.clear();
        _squarePixmaps.clear();

        // reset size
        _size = 0;

    }

    //______________________________________________
    void ShadowHelper::initializeHooks( void )
    {
       if( _hooksInitialized ) return;

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::initializeHooks" << std::endl;
        #endif

        // install hooks
        _realizeHook.connect( "realize", (GSignalEmissionHook)realizeHook, this );
        _hooksInitialized = true;

    }

    //______________________________________________
    void ShadowHelper::initialize( const ColorUtils::Rgba& color, const WindowShadow& shadow )
    {

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::initialize" << std::endl;
        #endif

        reset();
        _size = int(shadow.shadowSize()) - WindowShadow::Overlap;

        // round tiles
        WindowShadowKey key;
        key.hasTopBorder = true;
        key.hasBottomBorder = true;
        _roundTiles = shadow.tileSet( color, key );

        // square tiles
        key.hasTopBorder = false;
        key.hasBottomBorder = false;
        _squareTiles = shadow.tileSet( color, key );

        // re-install shadows for all windowId
        for( WidgetMap::const_iterator iter = _widgets.begin(); iter != _widgets.end(); ++iter )
        { installX11Shadows( iter->first ); }

    }

    //______________________________________________
    bool ShadowHelper::registerWidget( GtkWidget* widget )
    {

        // check widget
        if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false;

        // make sure that widget is not already registered
        if( _widgets.find( widget ) != _widgets.end() ) return false;

        // check if window is accepted
        if( !acceptWidget( widget ) ) return false;

        // try install shadows
        installX11Shadows( widget );

        // register in map and returns success
        WidgetData data;
        data._destroyId.connect( G_OBJECT( widget ), "destroy", G_CALLBACK( destroyNotifyEvent ), this );
        _widgets.insert( std::make_pair( widget, data ) );

        return true;

    }

    //______________________________________________
    void ShadowHelper::unregisterWidget( GtkWidget* widget )
    {
        // find matching data in map
        WidgetMap::iterator iter( _widgets.find( widget ) );
        if( iter == _widgets.end() ) return;

        // disconnect
        iter->second._destroyId.disconnect();

        // remove from map
        _widgets.erase( iter );
    }

    //______________________________________________
    bool ShadowHelper::isMenu( GtkWidget* widget ) const
    {
        if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false;
        const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) );
        return
            hint == GDK_WINDOW_TYPE_HINT_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU;
    }

    //______________________________________________
    bool ShadowHelper::isToolTip( GtkWidget* widget ) const
    {
        if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false;
        const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) );
        return hint == GDK_WINDOW_TYPE_HINT_TOOLTIP;
    }

    //______________________________________________
    bool ShadowHelper::acceptWidget( GtkWidget* widget ) const
    {

        // check widget and type
        if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false;

        // for openoffice, accept all non decorated windows
        if( _applicationName.isOpenOffice() ) return true;

        // otherwise check window hint
        const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) );
        return
            hint == GDK_WINDOW_TYPE_HINT_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_COMBO ||
            hint == GDK_WINDOW_TYPE_HINT_TOOLTIP;
    }

    //______________________________________________
    void ShadowHelper::createPixmapHandles( void )
    {

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::createPixmapHandles" << std::endl;
        #endif

        #ifdef GDK_WINDOWING_X11
        // create atom
        if( !_atom )
        {

            // get screen and check
            GdkScreen* screen = gdk_screen_get_default();
            if( !screen )
            {

                #if OXYGEN_DEBUG
                std::cerr << "ShadowHelper::createPixmapHandles - screen is NULL" << std::endl;
                #endif

                return;
            }

            // get display and check
            Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) );
            if( !display )
            {

                #if OXYGEN_DEBUG
                std::cerr << "ShadowHelper::createPixmapHandles - display is NULL" << std::endl;
                #endif

                return;
            }

           _atom = XInternAtom( display, "_KDE_NET_WM_SHADOW", False);
        }

        // make sure size is valid
        if( _size <= 0 ) return;

        // opacity
        const int shadowOpacity = 150;

        if( _roundPixmaps.empty() || _squarePixmaps.empty() )
        {
            // get screen, display, visual and check
            // no need to check screen and display, since was already done for ATOM
            GdkScreen* screen = gdk_screen_get_default();
            if( !gdk_screen_get_rgba_visual( screen ) )
            {

                #if OXYGEN_DEBUG
                std::cerr << "ShadowHelper::createPixmapHandles - no valid RGBA visual found." << std::endl;
                #endif

                return;

            }
        }

        // make sure pixmaps are not already initialized
        if( _roundPixmaps.empty() )
        {

            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 1 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 2 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 5 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 8 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 7 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 6 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 3 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 0 ), shadowOpacity ) );

        }

        if( _squarePixmaps.empty() )
        {

            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 1 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 2 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 5 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 8 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 7 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 6 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 3 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 0 ), shadowOpacity ) );

        }

        #endif

    }

    //______________________________________________
    #ifdef GDK_WINDOWING_X11
    Pixmap ShadowHelper::createPixmap( const Cairo::Surface& surface, int opacity ) const
    {
        assert( surface.isValid() );
        int width(0);
        int height(0);
        cairo_surface_get_size( surface, width, height );

        GdkScreen* screen = gdk_screen_get_default();
        Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) );
        Window root( GDK_WINDOW_XID( gdk_screen_get_root_window( screen ) ) );
        Pixmap pixmap = XCreatePixmap( display, root, width, height, 32 );

        // create surface for pixmap
        {
            Cairo::Surface dest( cairo_xlib_surface_create( display, pixmap, GDK_VISUAL_XVISUAL( gdk_screen_get_rgba_visual( screen ) ), width, height ) );
            Cairo::Context context( dest );
            cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );

            cairo_rectangle( context, 0, 0, width, height );
            cairo_set_source_surface( context, surface, 0, 0 );
            cairo_fill( context );

            if( opacity < 255 )
            {

                cairo_set_operator( context, CAIRO_OPERATOR_DEST_IN );
                cairo_set_source( context, ColorUtils::Rgba( 0, 0, 0, double(opacity)/255 ) );
                cairo_rectangle( context, 0, 0, width, height );
                cairo_fill( context );

            }

        }

        return pixmap;

    }
    #endif

    //______________________________________________
    void ShadowHelper::installX11Shadows( GtkWidget* widget )
    {

        #ifdef GDK_WINDOWING_X11

        #if OXYGEN_DEBUG
        std::cerr
            << "Oxygen::ShadowHelper::installX11Shadows - "
            << " widget: " << widget
            << " wid: " << GDK_WINDOW_XID( gtk_widget_get_window( widget ) )
            << std::endl;
        #endif

        // check widget
        if( !GTK_IS_WIDGET( widget ) ) return;

        // make sure handles and atom are defined
        createPixmapHandles();

        GdkWindow  *window = gtk_widget_get_window( widget );
        GdkDisplay *display = gtk_widget_get_display( widget );

        std::vector<unsigned long> data;
        const bool isMenu( this->isMenu( widget ) );
        const bool isToolTip( this->isToolTip( widget ) );
        if( _applicationName.isOpenOffice() || ( (isMenu||isToolTip) && _applicationName.isXul( widget ) ) )
        {

            data = _squarePixmaps;
            data.push_back( _size );
            data.push_back( _size );
            data.push_back( _size );
            data.push_back( _size );

        } else {

            data = _roundPixmaps;
            if( isMenu )
            {

                /*
                for menus, need to shrink top and bottom shadow size, since body is done likely with respect to real size
                in painting method (Oxygen::Style::renderMenuBackground)
                */
                data.push_back( _size - Menu_VerticalOffset );
                data.push_back( _size );
                data.push_back( _size - Menu_VerticalOffset );
                data.push_back( _size );

            } else {

                // all sides have same sizz
                data.push_back( _size );
                data.push_back( _size );
                data.push_back( _size );
                data.push_back( _size );

            }

        }

        // change property
        XChangeProperty(
            GDK_DISPLAY_XDISPLAY( display ), GDK_WINDOW_XID(window), _atom, XA_CARDINAL, 32, PropModeReplace,
            reinterpret_cast<const unsigned char *>(&data[0]), data.size() );

        #endif

    }

    //_______________________________________________________
    void ShadowHelper::uninstallX11Shadows( GtkWidget* widget ) const
    {

        if( !GTK_IS_WIDGET( widget ) ) return;

        #ifdef GDK_WINDOWING_X11
        GdkWindow  *window = gtk_widget_get_window( widget );
        GdkDisplay *display = gtk_widget_get_display( widget );
        XDeleteProperty( GDK_DISPLAY_XDISPLAY( display ), GDK_WINDOW_XID(window), _atom);
        #endif

    }

    //_______________________________________________________
    gboolean ShadowHelper::realizeHook( GSignalInvocationHint*, guint, const GValue* params, gpointer data )
    {

        // get widget from params
        GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) );

        // check type
        if( !GTK_IS_WIDGET( widget ) ) return FALSE;
        static_cast<ShadowHelper*>(data)->registerWidget( widget );
        return TRUE;
    }

    //____________________________________________________________________________________________
    gboolean ShadowHelper::destroyNotifyEvent( GtkWidget* widget, gpointer data )
    {
        static_cast<ShadowHelper*>(data)->unregisterWidget( widget );
        return FALSE;
    }

}
<commit_msg>make sure all signals are disconnected at destruction<commit_after>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2011 Hugo Pereira Da Costa <hugo.pereira@free.fr>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or( at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygencairocontext.h"
#include "oxygencairoutils.h"
#include "config.h"
#include "oxygengtkutils.h"
#include "oxygenmetrics.h"
#include "oxygenrgba.h"
#include "oxygenshadowhelper.h"

#include "config.h"

#include <iostream>
#include <cairo/cairo.h>

#ifdef GDK_WINDOWING_X11
#include <cairo/cairo-xlib.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#endif

namespace Oxygen
{

    //______________________________________________
    ShadowHelper::ShadowHelper( void ):
        _size(0),
        _hooksInitialized( false )
    {

        #ifdef GDK_WINDOWING_X11
        _atom = None;
        #endif

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::ShadowHelper" << std::endl;
        #endif
    }

    //______________________________________________
    ShadowHelper::~ShadowHelper( void )
    {
        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::~ShadowHelper" << std::endl;
        #endif

        for( WidgetMap::iterator iter = _widgets.begin(); iter != _widgets.end(); ++iter )
        { iter->second._destroyId.disconnect(); }

        reset();
        _realizeHook.disconnect();
    }

    //______________________________________________
    void ShadowHelper::reset( void )
    {

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::reset" << std::endl;
        #endif

        #ifdef GDK_WINDOWING_X11
        GdkScreen* screen = gdk_screen_get_default();
        if( !screen ) return;

        Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) );

        // round pixmaps
        for( PixmapList::const_iterator iter = _roundPixmaps.begin(); iter != _roundPixmaps.end(); ++iter )
        { XFreePixmap(display, *iter); }

        // square pixmaps
        for( PixmapList::const_iterator iter = _squarePixmaps.begin(); iter != _squarePixmaps.end(); ++iter )
        { XFreePixmap(display, *iter); }
        #endif

        // clear arrays
        _roundPixmaps.clear();
        _squarePixmaps.clear();

        // reset size
        _size = 0;

    }

    //______________________________________________
    void ShadowHelper::initializeHooks( void )
    {
       if( _hooksInitialized ) return;

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::initializeHooks" << std::endl;
        #endif

        // install hooks
        _realizeHook.connect( "realize", (GSignalEmissionHook)realizeHook, this );
        _hooksInitialized = true;

    }

    //______________________________________________
    void ShadowHelper::initialize( const ColorUtils::Rgba& color, const WindowShadow& shadow )
    {

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::initialize" << std::endl;
        #endif

        reset();
        _size = int(shadow.shadowSize()) - WindowShadow::Overlap;

        // round tiles
        WindowShadowKey key;
        key.hasTopBorder = true;
        key.hasBottomBorder = true;
        _roundTiles = shadow.tileSet( color, key );

        // square tiles
        key.hasTopBorder = false;
        key.hasBottomBorder = false;
        _squareTiles = shadow.tileSet( color, key );

        // re-install shadows for all windowId
        for( WidgetMap::const_iterator iter = _widgets.begin(); iter != _widgets.end(); ++iter )
        { installX11Shadows( iter->first ); }

    }

    //______________________________________________
    bool ShadowHelper::registerWidget( GtkWidget* widget )
    {

        // check widget
        if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false;

        // make sure that widget is not already registered
        if( _widgets.find( widget ) != _widgets.end() ) return false;

        // check if window is accepted
        if( !acceptWidget( widget ) ) return false;

        // try install shadows
        installX11Shadows( widget );

        // register in map and returns success
        WidgetData data;
        data._destroyId.connect( G_OBJECT( widget ), "destroy", G_CALLBACK( destroyNotifyEvent ), this );
        _widgets.insert( std::make_pair( widget, data ) );

        return true;

    }

    //______________________________________________
    void ShadowHelper::unregisterWidget( GtkWidget* widget )
    {
        // find matching data in map
        WidgetMap::iterator iter( _widgets.find( widget ) );
        if( iter == _widgets.end() ) return;

        // disconnect
        iter->second._destroyId.disconnect();

        // remove from map
        _widgets.erase( iter );
    }

    //______________________________________________
    bool ShadowHelper::isMenu( GtkWidget* widget ) const
    {
        if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false;
        const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) );
        return
            hint == GDK_WINDOW_TYPE_HINT_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU;
    }

    //______________________________________________
    bool ShadowHelper::isToolTip( GtkWidget* widget ) const
    {
        if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false;
        const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) );
        return hint == GDK_WINDOW_TYPE_HINT_TOOLTIP;
    }

    //______________________________________________
    bool ShadowHelper::acceptWidget( GtkWidget* widget ) const
    {

        // check widget and type
        if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false;

        // for openoffice, accept all non decorated windows
        if( _applicationName.isOpenOffice() ) return true;

        // otherwise check window hint
        const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) );
        return
            hint == GDK_WINDOW_TYPE_HINT_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU ||
            hint == GDK_WINDOW_TYPE_HINT_COMBO ||
            hint == GDK_WINDOW_TYPE_HINT_TOOLTIP;
    }

    //______________________________________________
    void ShadowHelper::createPixmapHandles( void )
    {

        #if OXYGEN_DEBUG
        std::cerr << "Oxygen::ShadowHelper::createPixmapHandles" << std::endl;
        #endif

        #ifdef GDK_WINDOWING_X11
        // create atom
        if( !_atom )
        {

            // get screen and check
            GdkScreen* screen = gdk_screen_get_default();
            if( !screen )
            {

                #if OXYGEN_DEBUG
                std::cerr << "ShadowHelper::createPixmapHandles - screen is NULL" << std::endl;
                #endif

                return;
            }

            // get display and check
            Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) );
            if( !display )
            {

                #if OXYGEN_DEBUG
                std::cerr << "ShadowHelper::createPixmapHandles - display is NULL" << std::endl;
                #endif

                return;
            }

           _atom = XInternAtom( display, "_KDE_NET_WM_SHADOW", False);
        }

        // make sure size is valid
        if( _size <= 0 ) return;

        // opacity
        const int shadowOpacity = 150;

        if( _roundPixmaps.empty() || _squarePixmaps.empty() )
        {
            // get screen, display, visual and check
            // no need to check screen and display, since was already done for ATOM
            GdkScreen* screen = gdk_screen_get_default();
            if( !gdk_screen_get_rgba_visual( screen ) )
            {

                #if OXYGEN_DEBUG
                std::cerr << "ShadowHelper::createPixmapHandles - no valid RGBA visual found." << std::endl;
                #endif

                return;

            }
        }

        // make sure pixmaps are not already initialized
        if( _roundPixmaps.empty() )
        {

            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 1 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 2 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 5 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 8 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 7 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 6 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 3 ), shadowOpacity ) );
            _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 0 ), shadowOpacity ) );

        }

        if( _squarePixmaps.empty() )
        {

            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 1 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 2 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 5 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 8 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 7 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 6 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 3 ), shadowOpacity ) );
            _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 0 ), shadowOpacity ) );

        }

        #endif

    }

    //______________________________________________
    #ifdef GDK_WINDOWING_X11
    Pixmap ShadowHelper::createPixmap( const Cairo::Surface& surface, int opacity ) const
    {
        assert( surface.isValid() );
        int width(0);
        int height(0);
        cairo_surface_get_size( surface, width, height );

        GdkScreen* screen = gdk_screen_get_default();
        Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) );
        Window root( GDK_WINDOW_XID( gdk_screen_get_root_window( screen ) ) );
        Pixmap pixmap = XCreatePixmap( display, root, width, height, 32 );

        // create surface for pixmap
        {
            Cairo::Surface dest( cairo_xlib_surface_create( display, pixmap, GDK_VISUAL_XVISUAL( gdk_screen_get_rgba_visual( screen ) ), width, height ) );
            Cairo::Context context( dest );
            cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );

            cairo_rectangle( context, 0, 0, width, height );
            cairo_set_source_surface( context, surface, 0, 0 );
            cairo_fill( context );

            if( opacity < 255 )
            {

                cairo_set_operator( context, CAIRO_OPERATOR_DEST_IN );
                cairo_set_source( context, ColorUtils::Rgba( 0, 0, 0, double(opacity)/255 ) );
                cairo_rectangle( context, 0, 0, width, height );
                cairo_fill( context );

            }

        }

        return pixmap;

    }
    #endif

    //______________________________________________
    void ShadowHelper::installX11Shadows( GtkWidget* widget )
    {

        #ifdef GDK_WINDOWING_X11

        #if OXYGEN_DEBUG
        std::cerr
            << "Oxygen::ShadowHelper::installX11Shadows - "
            << " widget: " << widget
            << " wid: " << GDK_WINDOW_XID( gtk_widget_get_window( widget ) )
            << std::endl;
        #endif

        // check widget
        if( !GTK_IS_WIDGET( widget ) ) return;

        // make sure handles and atom are defined
        createPixmapHandles();

        GdkWindow  *window = gtk_widget_get_window( widget );
        GdkDisplay *display = gtk_widget_get_display( widget );

        std::vector<unsigned long> data;
        const bool isMenu( this->isMenu( widget ) );
        const bool isToolTip( this->isToolTip( widget ) );
        if( _applicationName.isOpenOffice() || ( (isMenu||isToolTip) && _applicationName.isXul( widget ) ) )
        {

            data = _squarePixmaps;
            data.push_back( _size );
            data.push_back( _size );
            data.push_back( _size );
            data.push_back( _size );

        } else {

            data = _roundPixmaps;
            if( isMenu )
            {

                /*
                for menus, need to shrink top and bottom shadow size, since body is done likely with respect to real size
                in painting method (Oxygen::Style::renderMenuBackground)
                */
                data.push_back( _size - Menu_VerticalOffset );
                data.push_back( _size );
                data.push_back( _size - Menu_VerticalOffset );
                data.push_back( _size );

            } else {

                // all sides have same sizz
                data.push_back( _size );
                data.push_back( _size );
                data.push_back( _size );
                data.push_back( _size );

            }

        }

        // change property
        XChangeProperty(
            GDK_DISPLAY_XDISPLAY( display ), GDK_WINDOW_XID(window), _atom, XA_CARDINAL, 32, PropModeReplace,
            reinterpret_cast<const unsigned char *>(&data[0]), data.size() );

        #endif

    }

    //_______________________________________________________
    void ShadowHelper::uninstallX11Shadows( GtkWidget* widget ) const
    {

        if( !GTK_IS_WIDGET( widget ) ) return;

        #ifdef GDK_WINDOWING_X11
        GdkWindow  *window = gtk_widget_get_window( widget );
        GdkDisplay *display = gtk_widget_get_display( widget );
        XDeleteProperty( GDK_DISPLAY_XDISPLAY( display ), GDK_WINDOW_XID(window), _atom);
        #endif

    }

    //_______________________________________________________
    gboolean ShadowHelper::realizeHook( GSignalInvocationHint*, guint, const GValue* params, gpointer data )
    {

        // get widget from params
        GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) );

        // check type
        if( !GTK_IS_WIDGET( widget ) ) return FALSE;
        static_cast<ShadowHelper*>(data)->registerWidget( widget );
        return TRUE;
    }

    //____________________________________________________________________________________________
    gboolean ShadowHelper::destroyNotifyEvent( GtkWidget* widget, gpointer data )
    {
        static_cast<ShadowHelper*>(data)->unregisterWidget( widget );
        return FALSE;
    }

}
<|endoftext|>


<commit_before>// 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

#ifndef __PROCESS_GRPC_HPP__
#define __PROCESS_GRPC_HPP__

#include <atomic>
#include <chrono>
#include <memory>
#include <thread>
#include <type_traits>

#include <google/protobuf/message.h>

#include <grpc++/grpc++.h>

#include <process/future.hpp>
#include <process/owned.hpp>
#include <process/process.hpp>

#include <stout/duration.hpp>
#include <stout/lambda.hpp>
#include <stout/synchronized.hpp>
#include <stout/try.hpp>

// This file provides libprocess "support" for using gRPC. In
// particular, it defines two wrapper classes: `Channel` (representing a
// connection to a gRPC server) and `client::Runtime`, which integrates
// an event loop waiting for gRPC responses, and provides the `call`
// interface to create an asynchrous gRPC call and return a `Future`.

#define GRPC_RPC(service, rpc) \
  (&service::Stub::Async##rpc)

namespace process {
namespace grpc {

// Forward declarations.
namespace client { class Runtime; }

/**
 * A copyable interface to manage a connection to a gRPC server.
 * All `Channel` copies share the same connection. Note that the
 * connection is established lazily by the gRPC runtime library: the
 * actual connection is delayed till an RPC call is made.
 */
class Channel
{
public:
  Channel(const std::string& uri,
          const std::shared_ptr<::grpc::ChannelCredentials>& credentials =
            ::grpc::InsecureChannelCredentials())
    : channel(::grpc::CreateChannel(uri, credentials)) {}

private:
  std::shared_ptr<::grpc::Channel> channel;

  friend class client::Runtime;
};

/**
 * The response of a RPC call. It includes the gRPC `Status`
 * (https://grpc.io/grpc/cpp/classgrpc_1_1_status.html), and
 * the actual protobuf response body.
 */
template <typename T>
struct RpcResult
{
  RpcResult(const ::grpc::Status& _status, const T& _response)
    : status(_status), response(_response) {}

  ::grpc::Status status;
  T response;
};

namespace client {

/**
 * A copyable interface to manage an internal gRPC runtime instance for
 * asynchronous gRPC calls. A gRPC runtime instance includes a gRPC
 * `CompletionQueue` to manage outstanding requests, a looper thread to
 * wait for any incoming responses from the `CompletionQueue`, and a
 * process to handle the responses. All `Runtime` copies share the same
 * gRPC runtime instance. Usually we only need a single gRPC runtime
 * instance to handle all gRPC calls, but multiple instances can be
 * instantiated for more parallelism or isolation.
 * NOTE: The destruction of the internal gRPC runtime instance is a
 * blocking operation: it waits for the managed process to terminate.
 * The user should ensure that this only happens at shutdown.
 */
class Runtime
{
public:
  Runtime() : data(new Data()) {}

  /**
   * Sends an asynchronous gRPC call.
   *
   * @param channel A connection to a gRPC server.
   * @param rpc The asynchronous gRPC call to make. This can be obtained
   *     by the `GRPC_RPC(Service, RPC)` macro.
   * @param request The request protobuf for the gRPC call.
   * @return a `Future` waiting for a response protobuf.
   */
  template <typename Stub, typename Request, typename Response>
  Future<RpcResult<Response>> call(
      const Channel& channel,
      std::unique_ptr<::grpc::ClientAsyncResponseReader<Response>>(Stub::*rpc)(
          ::grpc::ClientContext*,
          const Request&,
          ::grpc::CompletionQueue*),
      const Request& request)
  {
    static_assert(
        std::is_convertible<Request*, google::protobuf::Message*>::value,
        "Request must be a protobuf message");

    synchronized (data->lock) {
      if (data->terminating) {
        return Failure("Runtime has been terminated.");
      }

      std::shared_ptr<::grpc::ClientContext> context(
          new ::grpc::ClientContext());

      // TODO(chhsiao): Allow the caller to specify a timeout.
      context->set_deadline(
          std::chrono::system_clock::now() + std::chrono::seconds(5));

      // Create a `Promise` and a callback lambda as a tag and invokes
      // an asynchronous gRPC call through the `CompletionQueue`
      // managed by `data`. The `Promise` will be set by the callback
      // upon server response.
      std::shared_ptr<Promise<RpcResult<Response>>> promise(
          new Promise<RpcResult<Response>>);

      promise->future().onDiscard([=] { context->TryCancel(); });

      std::shared_ptr<Response> response(new Response());
      std::shared_ptr<::grpc::Status> status(new ::grpc::Status());

      std::shared_ptr<::grpc::ClientAsyncResponseReader<Response>> reader(
          (Stub(channel.channel).*rpc)(context.get(), request, &data->queue));

      reader->Finish(
          response.get(),
          status.get(),
          new lambda::function<void()>(
              // NOTE: `context` and `reader` need to be held on in
              // order to get updates for the ongoing RPC, and thus
              // are captured here. The lambda itself will later be
              // retrieved and managed in `Data::loop()`.
              [context, reader, response, status, promise]() {
                CHECK(promise->future().isPending());
                if (promise->future().hasDiscard()) {
                  promise->discard();
                  return;
                }

                promise->set(RpcResult<Response>(*status, *response));
              }));

      return promise->future();
    }
  }

  /**
   * Asks the internal gRPC runtime instance to shut down the
   * `CompletionQueue`, which would stop its looper thread, drain and
   * fail all pending gRPC calls in the `CompletionQueue`, then
   * asynchronously join the looper thread.
   */
  void terminate();

  /**
   * @return A `Future` waiting for all pending gRPC calls in the
   *     `CompletionQueue` of the internal gRPC runtime instance to be
   *     drained and the looper thread to be joined.
   */
  Future<Nothing> wait();

private:
  struct Data
  {
    Data();
    ~Data();

    void loop();
    void terminate();

    std::unique_ptr<std::thread> looper;
    ::grpc::CompletionQueue queue;
    ProcessBase process;
    std::atomic_flag lock = ATOMIC_FLAG_INIT;
    bool terminating = false;
    Promise<Nothing> terminated;
  };

  std::shared_ptr<Data> data;
};

} // namespace client {

} // namespace grpc {
} // namespace process {

#endif // __PROCESS_GRPC_HPP__
<commit_msg>Made gRPC calls wait for the channel to be ready.<commit_after>// 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

#ifndef __PROCESS_GRPC_HPP__
#define __PROCESS_GRPC_HPP__

#include <atomic>
#include <chrono>
#include <memory>
#include <thread>
#include <type_traits>

#include <google/protobuf/message.h>

#include <grpc++/grpc++.h>

#include <process/future.hpp>
#include <process/owned.hpp>
#include <process/process.hpp>

#include <stout/duration.hpp>
#include <stout/lambda.hpp>
#include <stout/synchronized.hpp>
#include <stout/try.hpp>

// This file provides libprocess "support" for using gRPC. In
// particular, it defines two wrapper classes: `Channel` (representing a
// connection to a gRPC server) and `client::Runtime`, which integrates
// an event loop waiting for gRPC responses, and provides the `call`
// interface to create an asynchrous gRPC call and return a `Future`.

#define GRPC_RPC(service, rpc) \
  (&service::Stub::Async##rpc)

namespace process {
namespace grpc {

// Forward declarations.
namespace client { class Runtime; }

/**
 * A copyable interface to manage a connection to a gRPC server.
 * All `Channel` copies share the same connection. Note that the
 * connection is established lazily by the gRPC runtime library: the
 * actual connection is delayed till an RPC call is made.
 */
class Channel
{
public:
  Channel(const std::string& uri,
          const std::shared_ptr<::grpc::ChannelCredentials>& credentials =
            ::grpc::InsecureChannelCredentials())
    : channel(::grpc::CreateChannel(uri, credentials)) {}

private:
  std::shared_ptr<::grpc::Channel> channel;

  friend class client::Runtime;
};

/**
 * The response of a RPC call. It includes the gRPC `Status`
 * (https://grpc.io/grpc/cpp/classgrpc_1_1_status.html), and
 * the actual protobuf response body.
 */
template <typename T>
struct RpcResult
{
  RpcResult(const ::grpc::Status& _status, const T& _response)
    : status(_status), response(_response) {}

  ::grpc::Status status;
  T response;
};

namespace client {

/**
 * A copyable interface to manage an internal gRPC runtime instance for
 * asynchronous gRPC calls. A gRPC runtime instance includes a gRPC
 * `CompletionQueue` to manage outstanding requests, a looper thread to
 * wait for any incoming responses from the `CompletionQueue`, and a
 * process to handle the responses. All `Runtime` copies share the same
 * gRPC runtime instance. Usually we only need a single gRPC runtime
 * instance to handle all gRPC calls, but multiple instances can be
 * instantiated for more parallelism or isolation.
 * NOTE: The destruction of the internal gRPC runtime instance is a
 * blocking operation: it waits for the managed process to terminate.
 * The user should ensure that this only happens at shutdown.
 */
class Runtime
{
public:
  Runtime() : data(new Data()) {}

  /**
   * Sends an asynchronous gRPC call.
   *
   * @param channel A connection to a gRPC server.
   * @param rpc The asynchronous gRPC call to make. This can be obtained
   *     by the `GRPC_RPC(Service, RPC)` macro.
   * @param request The request protobuf for the gRPC call.
   * @return a `Future` waiting for a response protobuf.
   */
  template <typename Stub, typename Request, typename Response>
  Future<RpcResult<Response>> call(
      const Channel& channel,
      std::unique_ptr<::grpc::ClientAsyncResponseReader<Response>>(Stub::*rpc)(
          ::grpc::ClientContext*,
          const Request&,
          ::grpc::CompletionQueue*),
      const Request& request)
  {
    static_assert(
        std::is_convertible<Request*, google::protobuf::Message*>::value,
        "Request must be a protobuf message");

    synchronized (data->lock) {
      if (data->terminating) {
        return Failure("Runtime has been terminated.");
      }

      std::shared_ptr<::grpc::ClientContext> context(
          new ::grpc::ClientContext());

      // TODO(chhsiao): Allow the caller to specify a timeout.
      context->set_deadline(
          std::chrono::system_clock::now() + std::chrono::seconds(5));

      // Enable the gRPC wait-for-ready semantics by default. See:
      // https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md
      // TODO(chhsiao): Allow the caller to set the option.
      context->set_wait_for_ready(true);

      // Create a `Promise` and a callback lambda as a tag and invokes
      // an asynchronous gRPC call through the `CompletionQueue`
      // managed by `data`. The `Promise` will be set by the callback
      // upon server response.
      std::shared_ptr<Promise<RpcResult<Response>>> promise(
          new Promise<RpcResult<Response>>);

      promise->future().onDiscard([=] { context->TryCancel(); });

      std::shared_ptr<Response> response(new Response());
      std::shared_ptr<::grpc::Status> status(new ::grpc::Status());

      std::shared_ptr<::grpc::ClientAsyncResponseReader<Response>> reader(
          (Stub(channel.channel).*rpc)(context.get(), request, &data->queue));

      reader->Finish(
          response.get(),
          status.get(),
          new lambda::function<void()>(
              // NOTE: `context` and `reader` need to be held on in
              // order to get updates for the ongoing RPC, and thus
              // are captured here. The lambda itself will later be
              // retrieved and managed in `Data::loop()`.
              [context, reader, response, status, promise]() {
                CHECK(promise->future().isPending());
                if (promise->future().hasDiscard()) {
                  promise->discard();
                  return;
                }

                promise->set(RpcResult<Response>(*status, *response));
              }));

      return promise->future();
    }
  }

  /**
   * Asks the internal gRPC runtime instance to shut down the
   * `CompletionQueue`, which would stop its looper thread, drain and
   * fail all pending gRPC calls in the `CompletionQueue`, then
   * asynchronously join the looper thread.
   */
  void terminate();

  /**
   * @return A `Future` waiting for all pending gRPC calls in the
   *     `CompletionQueue` of the internal gRPC runtime instance to be
   *     drained and the looper thread to be joined.
   */
  Future<Nothing> wait();

private:
  struct Data
  {
    Data();
    ~Data();

    void loop();
    void terminate();

    std::unique_ptr<std::thread> looper;
    ::grpc::CompletionQueue queue;
    ProcessBase process;
    std::atomic_flag lock = ATOMIC_FLAG_INIT;
    bool terminating = false;
    Promise<Nothing> terminated;
  };

  std::shared_ptr<Data> data;
};

} // namespace client {

} // namespace grpc {
} // namespace process {

#endif // __PROCESS_GRPC_HPP__
<|endoftext|>