<commit_before>#include "code/ylikuutio/ontology/symbiosis.hpp"
#include "code/ylikuutio/ontology/symbiont_species.hpp"
#include "code/ylikuutio/ontology/symbiosis_struct.hpp"
#include "code/ylikuutio/command_line/command_line_master.hpp"
#include "code/ylikuutio/string/ylikuutio_string.hpp"

#include "assimp/scene.h"
#include "assimp/Exporter.hpp"
#include "assimp/types.h"

#define SDL_main main

#include "SDL.h"

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <algorithm>     // std::transform
#include <cstddef>       // std::size_t
#include <cstdlib>       // EXIT_FAILURE, EXIT_SUCCESS
#include <iostream>      // std::cout, std::cin, std::cerr
#include <limits>        // std::numeric_limits
#include <string>        // std::string
#include <vector>        // std::vector

// `yli_convert` is a command-line tool to load and export 3D data between different formats.
//
// parameters:
// required:
// `--in=foo`  load 3D data from file `foo`.
// `--in foo`  load 3D data from file `foo`.
// `--out=bar` export 3D data into file `bar`.
// `--out bar` export 3D data into file `bar`.
//
// optional:
// `--informat=baz` assume file format `baz` (if `--informat` is left out, file format is deduced from the filename extension).
// `--informat baz` assume file format `baz` (if `--informat` is left out, file format is deduced from the filename extension).
// `--outformat=qux` export 3D data into file `qux` (if `--outformat` is left out, file format is deduced from the filename extension).
// `--outformat qux` export 3D data into file `qux` (if `--outformat` is left out, file format is deduced from the filename extension).
// `--intexture=quux` use texture data into file `quux` as the material for all meshes (eg. `.obj` input file).
// `--intexture quux` use texture data into file `quux` as the material for all meshes (eg. `.obj` input file).

namespace yli
{
    namespace ontology
    {
        class Universe;
    }
}

int main(const int argc, const char* argv[])
{
    yli::command_line::CommandLineMaster command_line_master(argc, argv);
    command_line_master.print_keys_and_values();

    if (!command_line_master.is_key("in"))
    {
        std::cerr << "ERROR: --in parameter is required!\n\nexample:\n\nyli_convert --in=foo --out=bar\n";
        exit(EXIT_FAILURE);
    }

    if (!command_line_master.is_key("out"))
    {
        std::cerr << "ERROR: --out parameter is required!\n\nexample:\n\nyli_convert --in=foo --out=bar\n";
        exit(EXIT_FAILURE);
    }

    const std::string in_file = command_line_master.get_value("in");
    const std::string out_file = command_line_master.get_value("out");

    std::string in_file_format = ""; // dummy value.

    if (command_line_master.is_key("informat"))
    {
        in_file_format = command_line_master.get_value("informat");
    }
    else
    {
        const char separator = '.';
        yli::string::extract_last_part_of_string(in_file, in_file_format, separator);
    }

    if (in_file_format.empty())
    {
        std::cerr << "ERROR: --informat is empty!\n";
        exit(EXIT_FAILURE);
    }

    std::cout << "in file format is " << in_file_format << "\n";

    std::string out_file_format = ""; // dummy value.

    if (command_line_master.is_key("outformat"))
    {
        out_file_format = command_line_master.get_value("outformat");
    }
    else
    {
        const char separator = '.';
        yli::string::extract_last_part_of_string(out_file, out_file_format, separator);
    }

    if (out_file_format.empty())
    {
        std::cerr << "ERROR: --outformat is empty!\n";
        exit(EXIT_FAILURE);
    }

    std::cout << "out file format is " << out_file_format << "\n";

    // OK, all required input is given in the correct format.

    // convert in file format & out file format into lowercase.
    std::transform(in_file_format.begin(), in_file_format.end(), in_file_format.begin(), ::tolower);
    std::transform(out_file_format.begin(), out_file_format.end(), out_file_format.begin(), ::tolower);

    if (in_file_format != "obj" && in_file_format != "fbx")
    {
        std::cerr << "ERROR: unsupported in file format: " << in_file_format << "\n";
        exit(EXIT_FAILURE);
    }

    // Always load the 3D object into a `yli::ontology::Symbiosis`.
    SymbiosisStruct symbiosis_struct;
    symbiosis_struct.model_filename = in_file;
    symbiosis_struct.model_file_format = in_file_format;
    symbiosis_struct.triangulation_type = "bilinear_interpolation";
    symbiosis_struct.parent = nullptr;           // no parent.
    symbiosis_struct.vram_buffer_in_use = false; // do not use VRAM buffer.
    yli::ontology::Universe* const universe = nullptr;
    const yli::ontology::Symbiosis symbiosis = yli::ontology::Symbiosis(universe, symbiosis_struct);

    // Assimp usage example:
    // https://github.com/assimp/assimp/issues/203

    aiScene ai_scene;
    ai_scene.mRootNode = new aiNode();
    ai_scene.mNumMaterials = symbiosis.get_number_of_symbiont_materials();
    ai_scene.mMaterials = new aiMaterial*[ai_scene.mNumMaterials];
    ai_scene.mNumAnimations = 0;    // yli_convert does not yet support animations.
    ai_scene.mAnimations = nullptr; // yli_convert does not yet support animations.
    ai_scene.mNumTextures = symbiosis.get_number_of_symbiont_materials();
    ai_scene.mTextures = new aiTexture*[ai_scene.mNumMaterials];
    ai_scene.mNumLights = 0;        // yli_convert does not yet support lights.
    ai_scene.mLights = nullptr;     // yli_convert does not yet support lights.
    ai_scene.mNumCameras = 0;       // yli_convert does not yet support cameras.
    ai_scene.mCameras = nullptr;    // yli_convert does not yet support cameras.
    ai_scene.mMetaData = nullptr;   // yli_convert does not yet support metadata.
    ai_scene.mNumMeshes = symbiosis.get_number_of_symbiont_species();
    ai_scene.mMeshes = new aiMesh*[ai_scene.mNumMeshes];
    ai_scene.mRootNode->mNumMeshes = symbiosis.get_number_of_symbiont_species();
    ai_scene.mRootNode->mMeshes = new unsigned int[symbiosis.get_number_of_symbiont_species()];

    std::cout << "Number of `SymbiontMaterial`s: " << symbiosis.get_number_of_symbiont_materials() << "\n";

    for (std::size_t symbiont_material_i = 0; symbiont_material_i < symbiosis.get_number_of_symbiont_materials(); symbiont_material_i++)
    {
        std::cout << "Processing `SymbiontMaterial` " << symbiont_material_i << "\n";
        const yli::ontology::SymbiontMaterial* const symbiont_material = symbiosis.get_symbiont_material(symbiont_material_i);

        ai_scene.mMaterials[symbiont_material_i] = new aiMaterial();
        ai_scene.mTextures[symbiont_material_i] = new aiTexture();

        if (symbiont_material == nullptr)
        {
            std::cerr << "ERROR: `yli_convert`: `symbiont_material` is `nullptr`!\n";
            continue;
        }

        ai_scene.mTextures[symbiont_material_i]->mWidth = static_cast<unsigned int>(symbiont_material->get_image_width());
        ai_scene.mTextures[symbiont_material_i]->mHeight = static_cast<unsigned int>(symbiont_material->get_image_height());

        if (symbiont_material->get_childID() == std::numeric_limits<std::size_t>::max())
        {
            // childID is the default (invalid) value, not a real childID!
            std::cerr << "ERROR: `yli_convert`: `childID` is `std::numeric_limits<std::size_t>::max()`!\n";
            continue;
        }
    }

    for (std::size_t symbiont_species_i = 0; symbiont_species_i < symbiosis.get_number_of_symbiont_species(); symbiont_species_i++)
    {
        ai_scene.mRootNode->mMeshes[symbiont_species_i] = symbiont_species_i;
        ai_scene.mMeshes[symbiont_species_i] = new aiMesh();

        const yli::ontology::SymbiontSpecies* const symbiont_species = symbiosis.get_symbiont_species(symbiont_species_i);

        if (symbiont_species == nullptr)
        {
            std::cerr << "ERROR: `yli_convert`: `symbiont_species` is `nullptr`!\n";
            continue;
        }

        const yli::ontology::SymbiontMaterial* const symbiont_material = static_cast<yli::ontology::SymbiontMaterial*>(symbiont_species->get_parent());

        if (symbiont_material == nullptr)
        {
            std::cerr << "ERROR: `yli_convert`: `symbiont_material` is `nullptr`!\n";
            continue;
        }

        ai_scene.mMeshes[symbiont_species_i]->mMaterialIndex = symbiont_material->get_childID();

        const std::vector<glm::vec3>& vertices = symbiont_species->get_vertices();
        const std::vector<glm::vec3>& normals = symbiont_species->get_normals();
        const std::vector<glm::vec2>& uvs = symbiont_species->get_uvs();

        const std::size_t number_of_vertices = vertices.size();

        ai_scene.mMeshes[symbiont_species_i]->mNumVertices = number_of_vertices;
        ai_scene.mMeshes[symbiont_species_i]->mVertices = new aiVector3D[number_of_vertices];
        ai_scene.mMeshes[symbiont_species_i]->mNormals = new aiVector3D[number_of_vertices];
        ai_scene.mMeshes[symbiont_species_i]->mTextureCoords[0] = new aiVector3D[number_of_vertices];
        ai_scene.mMeshes[symbiont_species_i]->mNumUVComponents[0] = number_of_vertices;

        for (std::size_t vertex_i = 0; vertex_i < number_of_vertices; vertex_i++)
        {
            ai_scene.mMeshes[symbiont_species_i]->mVertices[vertex_i] = aiVector3D(vertices[vertex_i].x, vertices[vertex_i].y, vertices[vertex_i].z);
            ai_scene.mMeshes[symbiont_species_i]->mNormals[vertex_i] = aiVector3D(normals[vertex_i].x, normals[vertex_i].y, normals[vertex_i].z);
            ai_scene.mMeshes[symbiont_species_i]->mTextureCoords[0][vertex_i] = aiVector3D(uvs[vertex_i].x, uvs[vertex_i].y, 0);
        }

        const std::size_t number_of_faces = number_of_vertices / 3;
        std::cout << "Number of faces: " << number_of_faces << "\n";

        ai_scene.mMeshes[symbiont_species_i]->mFaces = new aiFace[number_of_faces];
        ai_scene.mMeshes[symbiont_species_i]->mNumFaces = number_of_faces;

        for (std::size_t face_i = 0; face_i < number_of_faces; face_i++)
        {
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mIndices = new unsigned int[3];
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mNumIndices = 3;
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mIndices[0] = 3 * face_i;
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mIndices[1] = 3 * face_i + 1;
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mIndices[2] = 3 * face_i + 2;
        }
    }

    std::cout << "Creating Assimp::Exporter ...\n";
    Assimp::Exporter assimp_exporter = Assimp::Exporter();
    std::cout << "Exporting to " << out_file << " ...\n";
    const aiReturn export_ai_return = assimp_exporter.Export(&ai_scene, out_file_format, out_file);

    if (export_ai_return == aiReturn_SUCCESS)
    {
        std::cout << "Export to " << out_file << " successful.\n";
    }
    else if (export_ai_return == aiReturn_FAILURE)
    {
        std::cerr << "ERROR: Export to " << out_file << " failed.\n";
    }
    else if (export_ai_return == aiReturn_OUTOFMEMORY)
    {
        std::cerr << "ERROR: Export to " << out_file << " failed: out of memory.\n";
    }
    else if (export_ai_return == _AI_ENFORCE_ENUM_SIZE)
    {
        std::cerr << "ERROR: Export to " << out_file << " failed: _AI_ENFORCE_ENUM_SIZE.\n";
    }
    else
    {
        std::cerr << "ERROR: Export to " << out_file << " failed: unknown error.\n";
    }
}
<commit_msg>`yli_convert`: cleaning up code.<commit_after>#include "code/ylikuutio/ontology/symbiosis.hpp"
#include "code/ylikuutio/ontology/symbiont_species.hpp"
#include "code/ylikuutio/ontology/symbiosis_struct.hpp"
#include "code/ylikuutio/command_line/command_line_master.hpp"
#include "code/ylikuutio/string/ylikuutio_string.hpp"

#include "assimp/scene.h"
#include "assimp/Exporter.hpp"
#include "assimp/types.h"

#define SDL_main main

#include "SDL.h"

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <algorithm>     // std::transform
#include <cstddef>       // std::size_t
#include <cstdlib>       // EXIT_FAILURE, EXIT_SUCCESS
#include <iostream>      // std::cout, std::cin, std::cerr
#include <limits>        // std::numeric_limits
#include <string>        // std::string
#include <vector>        // std::vector

// `yli_convert` is a command-line tool to load and export 3D data between different formats.
//
// parameters:
// required:
// `--in=foo`  load 3D data from file `foo`.
// `--in foo`  load 3D data from file `foo`.
// `--out=bar` export 3D data into file `bar`.
// `--out bar` export 3D data into file `bar`.
//
// optional:
// `--informat=baz` assume file format `baz` (if `--informat` is left out, file format is deduced from the filename extension).
// `--informat baz` assume file format `baz` (if `--informat` is left out, file format is deduced from the filename extension).
// `--outformat=qux` export 3D data into file `qux` (if `--outformat` is left out, file format is deduced from the filename extension).
// `--outformat qux` export 3D data into file `qux` (if `--outformat` is left out, file format is deduced from the filename extension).
// `--intexture=quux` use texture data into file `quux` as the material for all meshes (eg. `.obj` input file).
// `--intexture quux` use texture data into file `quux` as the material for all meshes (eg. `.obj` input file).

namespace yli
{
    namespace ontology
    {
        class Universe;
    }
}

int main(const int argc, const char* argv[])
{
    yli::command_line::CommandLineMaster command_line_master(argc, argv);
    command_line_master.print_keys_and_values();

    if (!command_line_master.is_key("in"))
    {
        std::cerr << "ERROR: --in parameter is required!\n\nexample:\n\nyli_convert --in=foo --out=bar\n";
        exit(EXIT_FAILURE);
    }

    if (!command_line_master.is_key("out"))
    {
        std::cerr << "ERROR: --out parameter is required!\n\nexample:\n\nyli_convert --in=foo --out=bar\n";
        exit(EXIT_FAILURE);
    }

    const std::string& in_file = command_line_master.get_value("in");
    const std::string& out_file = command_line_master.get_value("out");

    std::string in_file_format = ""; // dummy value.

    if (command_line_master.is_key("informat"))
    {
        in_file_format = command_line_master.get_value("informat");
    }
    else
    {
        const char separator = '.';
        yli::string::extract_last_part_of_string(in_file, in_file_format, separator);
    }

    if (in_file_format.empty())
    {
        std::cerr << "ERROR: --informat is empty!\n";
        exit(EXIT_FAILURE);
    }

    std::cout << "in file format is " << in_file_format << "\n";

    std::string out_file_format = ""; // dummy value.

    if (command_line_master.is_key("outformat"))
    {
        out_file_format = command_line_master.get_value("outformat");
    }
    else
    {
        const char separator = '.';
        yli::string::extract_last_part_of_string(out_file, out_file_format, separator);
    }

    if (out_file_format.empty())
    {
        std::cerr << "ERROR: --outformat is empty!\n";
        exit(EXIT_FAILURE);
    }

    std::cout << "out file format is " << out_file_format << "\n";

    // OK, all required input is given in the correct format.

    // convert in file format & out file format into lowercase.
    std::transform(in_file_format.begin(), in_file_format.end(), in_file_format.begin(), ::tolower);
    std::transform(out_file_format.begin(), out_file_format.end(), out_file_format.begin(), ::tolower);

    if (in_file_format != "obj" && in_file_format != "fbx")
    {
        std::cerr << "ERROR: unsupported in file format: " << in_file_format << "\n";
        exit(EXIT_FAILURE);
    }

    // Always load the 3D object into a `yli::ontology::Symbiosis`.
    SymbiosisStruct symbiosis_struct;
    symbiosis_struct.model_filename = in_file;
    symbiosis_struct.model_file_format = in_file_format;
    symbiosis_struct.triangulation_type = "bilinear_interpolation";
    symbiosis_struct.parent = nullptr;           // no parent.
    symbiosis_struct.vram_buffer_in_use = false; // do not use VRAM buffer.
    yli::ontology::Universe* const universe = nullptr;
    const yli::ontology::Symbiosis symbiosis = yli::ontology::Symbiosis(universe, symbiosis_struct);

    // Assimp usage example:
    // https://github.com/assimp/assimp/issues/203

    aiScene ai_scene;
    ai_scene.mRootNode = new aiNode();
    ai_scene.mNumMaterials = symbiosis.get_number_of_symbiont_materials();
    ai_scene.mMaterials = new aiMaterial*[ai_scene.mNumMaterials];
    ai_scene.mNumAnimations = 0;    // yli_convert does not yet support animations.
    ai_scene.mAnimations = nullptr; // yli_convert does not yet support animations.
    ai_scene.mNumTextures = symbiosis.get_number_of_symbiont_materials();
    ai_scene.mTextures = new aiTexture*[ai_scene.mNumMaterials];
    ai_scene.mNumLights = 0;        // yli_convert does not yet support lights.
    ai_scene.mLights = nullptr;     // yli_convert does not yet support lights.
    ai_scene.mNumCameras = 0;       // yli_convert does not yet support cameras.
    ai_scene.mCameras = nullptr;    // yli_convert does not yet support cameras.
    ai_scene.mMetaData = nullptr;   // yli_convert does not yet support metadata.
    ai_scene.mNumMeshes = symbiosis.get_number_of_symbiont_species();
    ai_scene.mMeshes = new aiMesh*[ai_scene.mNumMeshes];
    ai_scene.mRootNode->mNumMeshes = symbiosis.get_number_of_symbiont_species();
    ai_scene.mRootNode->mMeshes = new unsigned int[symbiosis.get_number_of_symbiont_species()];

    std::cout << "Number of `SymbiontMaterial`s: " << symbiosis.get_number_of_symbiont_materials() << "\n";

    for (std::size_t symbiont_material_i = 0; symbiont_material_i < symbiosis.get_number_of_symbiont_materials(); symbiont_material_i++)
    {
        std::cout << "Processing `SymbiontMaterial` " << symbiont_material_i << "\n";
        const yli::ontology::SymbiontMaterial* const symbiont_material = symbiosis.get_symbiont_material(symbiont_material_i);

        ai_scene.mMaterials[symbiont_material_i] = new aiMaterial();
        ai_scene.mTextures[symbiont_material_i] = new aiTexture();

        if (symbiont_material == nullptr)
        {
            std::cerr << "ERROR: `yli_convert`: `symbiont_material` is `nullptr`!\n";
            continue;
        }

        ai_scene.mTextures[symbiont_material_i]->mWidth = static_cast<unsigned int>(symbiont_material->get_image_width());
        ai_scene.mTextures[symbiont_material_i]->mHeight = static_cast<unsigned int>(symbiont_material->get_image_height());

        if (symbiont_material->get_childID() == std::numeric_limits<std::size_t>::max())
        {
            // childID is the default (invalid) value, not a real childID!
            std::cerr << "ERROR: `yli_convert`: `childID` is `std::numeric_limits<std::size_t>::max()`!\n";
            continue;
        }
    }

    for (std::size_t symbiont_species_i = 0; symbiont_species_i < symbiosis.get_number_of_symbiont_species(); symbiont_species_i++)
    {
        ai_scene.mRootNode->mMeshes[symbiont_species_i] = symbiont_species_i;
        ai_scene.mMeshes[symbiont_species_i] = new aiMesh();

        const yli::ontology::SymbiontSpecies* const symbiont_species = symbiosis.get_symbiont_species(symbiont_species_i);

        if (symbiont_species == nullptr)
        {
            std::cerr << "ERROR: `yli_convert`: `symbiont_species` is `nullptr`!\n";
            continue;
        }

        const yli::ontology::SymbiontMaterial* const symbiont_material = static_cast<yli::ontology::SymbiontMaterial*>(symbiont_species->get_parent());

        if (symbiont_material == nullptr)
        {
            std::cerr << "ERROR: `yli_convert`: `symbiont_material` is `nullptr`!\n";
            continue;
        }

        ai_scene.mMeshes[symbiont_species_i]->mMaterialIndex = symbiont_material->get_childID();

        const std::vector<glm::vec3>& vertices = symbiont_species->get_vertices();
        const std::vector<glm::vec3>& normals = symbiont_species->get_normals();
        const std::vector<glm::vec2>& uvs = symbiont_species->get_uvs();

        const std::size_t number_of_vertices = vertices.size();

        ai_scene.mMeshes[symbiont_species_i]->mNumVertices = number_of_vertices;
        ai_scene.mMeshes[symbiont_species_i]->mVertices = new aiVector3D[number_of_vertices];
        ai_scene.mMeshes[symbiont_species_i]->mNormals = new aiVector3D[number_of_vertices];
        ai_scene.mMeshes[symbiont_species_i]->mTextureCoords[0] = new aiVector3D[number_of_vertices];
        ai_scene.mMeshes[symbiont_species_i]->mNumUVComponents[0] = number_of_vertices;

        for (std::size_t vertex_i = 0; vertex_i < number_of_vertices; vertex_i++)
        {
            ai_scene.mMeshes[symbiont_species_i]->mVertices[vertex_i] = aiVector3D(vertices[vertex_i].x, vertices[vertex_i].y, vertices[vertex_i].z);
            ai_scene.mMeshes[symbiont_species_i]->mNormals[vertex_i] = aiVector3D(normals[vertex_i].x, normals[vertex_i].y, normals[vertex_i].z);
            ai_scene.mMeshes[symbiont_species_i]->mTextureCoords[0][vertex_i] = aiVector3D(uvs[vertex_i].x, uvs[vertex_i].y, 0);
        }

        const std::size_t number_of_faces = number_of_vertices / 3;
        std::cout << "Number of faces: " << number_of_faces << "\n";

        ai_scene.mMeshes[symbiont_species_i]->mFaces = new aiFace[number_of_faces];
        ai_scene.mMeshes[symbiont_species_i]->mNumFaces = number_of_faces;

        for (std::size_t face_i = 0; face_i < number_of_faces; face_i++)
        {
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mIndices = new unsigned int[3];
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mNumIndices = 3;
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mIndices[0] = 3 * face_i;
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mIndices[1] = 3 * face_i + 1;
            ai_scene.mMeshes[symbiont_species_i]->mFaces[face_i].mIndices[2] = 3 * face_i + 2;
        }
    }

    std::cout << "Creating Assimp::Exporter ...\n";
    Assimp::Exporter assimp_exporter = Assimp::Exporter();
    std::cout << "Exporting to " << out_file << " ...\n";
    const aiReturn export_ai_return = assimp_exporter.Export(&ai_scene, out_file_format, out_file);

    if (export_ai_return == aiReturn_SUCCESS)
    {
        std::cout << "Export to " << out_file << " successful.\n";
    }
    else if (export_ai_return == aiReturn_FAILURE)
    {
        std::cerr << "ERROR: Export to " << out_file << " failed.\n";
    }
    else if (export_ai_return == aiReturn_OUTOFMEMORY)
    {
        std::cerr << "ERROR: Export to " << out_file << " failed: out of memory.\n";
    }
    else if (export_ai_return == _AI_ENFORCE_ENUM_SIZE)
    {
        std::cerr << "ERROR: Export to " << out_file << " failed: _AI_ENFORCE_ENUM_SIZE.\n";
    }
    else
    {
        std::cerr << "ERROR: Export to " << out_file << " failed: unknown error.\n";
    }
}
<|endoftext|>


<commit_before>#ifndef _w32_mt_hpp__
#define _w32_mt_hpp__

// Copyright (c) 2009-2012, Andre Caron (andre.l.caron@gmail.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "__configure__.hpp"

namespace w32 {
    namespace mt {}
}

/*!
 * @defgroup w32-mt Multi-threading services.
 *
 * This group contains services for implementation of programs based on
 * multi-threading for efficient use of multi-processor architectures.  It
 * contains facilities for starting and controlling threads, as well as
 * synchronization primitives.
 */

#include <w32.mt/AutoResetEvent.hpp>
#include <w32.mt/CriticalSection.hpp>
#include <w32.mt/ManualResetEvent.hpp>
#include <w32.mt/Mutex.hpp>
#include <w32.mt/Semaphore.hpp>
#include <w32.mt/Thread.hpp>
#include <w32.mt/ThreadLocalPointer.hpp>
#if (_WIN32_WINNT >= 0x600)
#    include <w32.mt/ConditionVariable.hpp>
#    include <w32.mt/ReadWriteLock.hpp>
#endif

#include <w32.mt/increment.hpp>
#include <w32.mt/decrement.hpp>

#endif /* _w32_mt_hpp__ */
<commit_msg>Exports all w32::mt symbols.<commit_after>#ifndef _w32_mt_hpp__
#define _w32_mt_hpp__

// Copyright (c) 2009-2012, Andre Caron (andre.l.caron@gmail.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "__configure__.hpp"

namespace w32 {
    namespace mt {}
}

/*!
 * @defgroup w32-mt Multi-threading services.
 *
 * This group contains services for implementation of programs based on
 * multi-threading for efficient use of multi-processor architectures.  It
 * contains facilities for starting and controlling threads, as well as
 * synchronization primitives.
 */

#include <w32.mt/AutoResetEvent.hpp>
#include <w32.mt/CriticalSection.hpp>
#include <w32.mt/ManualResetEvent.hpp>
#include <w32.mt/Mutex.hpp>
#include <w32.mt/Semaphore.hpp>
#include <w32.mt/Timer.hpp>
#include <w32.mt/Thread.hpp>
#include <w32.mt/ThreadLocalPointer.hpp>
#if (_WIN32_WINNT >= 0x600)
#    include <w32.mt/ConditionVariable.hpp>
#    include <w32.mt/ReadWriteLock.hpp>
#endif

#include <w32.mt/and.hpp>
#include <w32.mt/atomic.hpp>
#include <w32.mt/barrier.hpp>
#include <w32.mt/compare-exchange.hpp>
#include <w32.mt/decrement.hpp>
#include <w32.mt/exchange.hpp>
#include <w32.mt/increment.hpp>
#include <w32.mt/or.hpp>
#include <w32.mt/xor.hpp>

#endif /* _w32_mt_hpp__ */
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015, University Corporation for Atmospheric Research
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstring>
#include <stdint.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sstream>
#include <signal.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>

#include <libcgroup.h>

#include <cgroup_context.h>

#include <log.h>

typedef uint64_t memory_t;

void enumerate_tasks(char* cgpath, uid_t victim, std::vector<pid_t>& cached_task_list);

uid_t get_uid(pid_t pid)
{
	char* status_path;
	asprintf(&status_path, "/proc/%d/status", pid);
	std::ifstream pid_status(status_path);
	std::string line;
	uid_t uid;
	char found = 0;
	while(pid_status.good())
	{
		std::getline(pid_status, line);
		size_t pos = line.find("Uid:",0);
		std::string token;
		if(pos != std::string::npos)
		{
			std::stringstream ls(line,std::ios_base::in);
			ls >> token; //header
			ls >> uid;
			found = 1;
			break;
		}
	}
	if(!found) {
		slog(LOG_ERR,"Error mapping UID for PID %d\n", pid);
	}
	pid_status.close();
	free(status_path);
	return(uid);
}

memory_t get_rss(pid_t pid)
{
	char* status_path;
	asprintf(&status_path, "/proc/%d/status", pid);
	std::ifstream pid_status(status_path);
	std::string line;
	uint64_t rss;
	char found = 0;

	while(pid_status.good())
	{
		std::getline(pid_status, line);
		size_t pos = line.find("VmRSS:",0);
		std::string token;
		if(pos != std::string::npos)
		{
			std::stringstream ls(line,std::ios_base::in);
			ls >> token; //header
			ls >> rss;
			//FIXME assumes always kB, which is currently correct
			//but could change
			found = 1;
			break;
		}
	}
	//if(!found) //ignore this case for now - due to races, it's fairly common
	//{
	//	//note: this can happen for kthreads
	//	slog(LOG_WARNING,"Error finding RSS for PID %d\n", pid);
	//}
	pid_status.close();
	free(status_path);
	return(rss);
}

extern "C"
{
char is_oom(struct cgroup_context* cgc)
{
	char* path;
	asprintf(&path, "/%s/%s/memory.oom_control", cgc->cgroup_path, cgc->cgroup_name);
	std::ifstream oc(path, std::ifstream::in);
	char isoom = -1;
	while(oc.good())
	{
		std::string token;
		oc >> token;
		if(token == "under_oom")
		{
			oc >> isoom;
		}
	}
	oc.close();
	free(path);
	if(isoom == -1) abort();
	if(isoom) return(1);
	return(0);
}
}

void get_cgroup_from_pid(pid_t pid, std::string& result)
{
	char* path;
	asprintf(&path, "/proc/%d/cgroup", pid);
	std::ifstream gcl(path, std::ifstream::in);
	while(gcl.good())
	{
		std::string token;
		gcl >> token;
		size_t delim = 0;
		delim = token.find_first_of(':');
		token.erase(0, delim);
		if(token.find("memory:/") != std::string::npos)
		{
			delim = token.find_first_of(':');
			if((delim+1) > token.length())
			{
				result.append("/;");
			}
			else
			{
				result.append(token.substr(delim+1));
				result.append(";");
			}
		}
	}
	gcl.close();
	free(path);
}

void sigkill_victim(pid_t pid)
{
	uid_t victim_uid;
	std::string cgroups;
	char* log_msg;
	victim_uid = get_uid(pid);
	get_cgroup_from_pid(pid, cgroups);
	asprintf(&log_msg, "killing UID:%u PID %d; cgroups: %s\n", victim_uid, pid,
			 cgroups.c_str()
			);
	slog(LOG_ALERT, log_msg);
	free(log_msg);
	kill(pid, SIGKILL);

}
void kill_victim(struct cgroup_context* cgc, uid_t victim_uid)
{

	std::vector<pid_t> cached_task_list;
	//get PID list
	char* cgpath;
	asprintf(&cgpath, "/%s/%s/", cgc->cgroup_path, cgc->cgroup_name);
	enumerate_tasks(cgpath, victim_uid, cached_task_list);
	free(cgpath);

	struct rlimit core_limit;
	core_limit.rlim_cur = 0;
	core_limit.rlim_max = 0;

	//Freeze all of user's processes
	for(std::vector<pid_t>::iterator i = cached_task_list.begin();
		i!= cached_task_list.end();
		i++)
	{
		cgroup_attach_task_pid(cgc->purgatory, *i);
	}

	char* root_freezer_path;
	char* root_memory_path;
	asprintf(&root_freezer_path, "/%s/tasks", cgc->freezer_path);
	asprintf(&root_memory_path, "/%s/tasks", cgc->cgroup_path);

	for(std::vector<pid_t>::iterator i = cached_task_list.begin();
		i!= cached_task_list.end();
		i++)
	{
		FILE* root_freezer = fopen(root_freezer_path,"w");
		FILE* root_memory = fopen(root_memory_path, "w");
		sigkill_victim(*i);
		fprintf(root_memory, "%d", *i);
		fclose(root_memory);
		fprintf(root_freezer, "%d", *i);
		fclose(root_freezer);
	}

	free(root_memory_path);
	free(root_freezer_path);
}

void enumerate_tasks(char* cgpath, uid_t victim_uid, std::vector<pid_t>& cached_task_list)
{
	pid_t pid;
	char* task_path;
	asprintf(&task_path, "/%s/tasks", cgpath);
	std::ifstream task_list(task_path,std::ifstream::in);
	while(task_list.good())
	{
		task_list >> pid;
		if(!(task_list.good())) //last read seems to be garbage
		{
			break;
		}
		uid_t uid = get_uid(pid);
		if(uid == victim_uid)
		{
			cached_task_list.push_back(pid);
		}
	}
	task_list.close();
	free(task_path);

	DIR* cgd = opendir(cgpath);
	if(cgd == NULL) slog(LOG_ALERT, "Error opening cgroup directory: %s\n", cgpath);
	struct dirent* de;
	struct stat stat_buf;
	int r;
	while((de=readdir(cgd))!=NULL)
	{
		char* tmp_path;
		asprintf(&tmp_path, "%s/%s/", cgpath, de->d_name);
		r = stat(tmp_path, &stat_buf);
		if(r!=0)
		{
			if(errno != ENOTDIR)
			{
		 	  slog(LOG_ALERT,
			   "enumerate_tasks(): stat() error code: %s on \"%s\"",
		  	   strerror(errno), tmp_path);
			}
		}
		else
		{
			if(S_ISDIR(stat_buf.st_mode) && de->d_name[0]!='.')
			{
				enumerate_tasks(tmp_path, victim_uid, cached_task_list);
			}
		}
		free(tmp_path);
	}
	closedir(cgd);
}

void enumerate_users(char* cgpath, std::map<uid_t, memory_t>& user_list)
{
	char* task_path;

	asprintf(&task_path, "/%s/tasks", cgpath);
	std::ifstream task_list(task_path,std::ifstream::in);
	pid_t pid;
	while(task_list.good())
	{
		task_list >> pid;
		if(!(task_list.good())) //last read seems to be garbage
		{
			break;
		}
		uid_t uid = get_uid(pid);
		memory_t rss = get_rss(pid);
		if(user_list.find(uid)==user_list.end())
		{
			user_list[uid] = rss;
		}
		else
		{
			user_list[uid] = user_list[uid] + rss;
		}
	}
	task_list.close();
	free(task_path);

	DIR* cgd = opendir(cgpath);
	if(cgd == NULL) slog(LOG_ALERT, "Error opening cgroup directory: %s\n", cgpath);
	struct dirent* de;
	struct stat stat_buf;
	int r;
	while((de=readdir(cgd))!=NULL)
	{
		char* tmp_path;
		asprintf(&tmp_path, "%s/%s/", cgpath, de->d_name);
		r = stat(tmp_path, &stat_buf);
		if(r!=0) {
			if(errno != ENOTDIR)
			{
			  slog(LOG_ALERT,
			  "enumerate_users(): stat() error code: %s on \"%s\"",
			  strerror(errno), tmp_path);
			}
		}
		else
		{
			if(S_ISDIR(stat_buf.st_mode) && de->d_name[0]!='.')
			{
				enumerate_users(tmp_path, user_list);
			}
		}
		free(tmp_path);
	}
	closedir(cgd);
}

extern "C"
{
	int find_victim(struct cgroup_context* cgc)
{
	std::map<uid_t,memory_t> user_list;
	char* cgpath;
	asprintf(&cgpath, "/%s/%s/", cgc->cgroup_path, cgc->cgroup_name);
	enumerate_users(cgpath, user_list);
	free(cgpath);

	if(user_list.size() < 1)
	{
		return(-1);
	}

	memory_t max = (user_list.begin())->second;
	uid_t max_uid = (user_list.begin())->first;
	for(std::map<uid_t,memory_t>::iterator i = user_list.begin();
		i!=user_list.end();
		i++)
	{
		if(i->second > max)
			{
				max_uid = i->first;
				max = i->second;
			}
	}
	kill_victim(cgc, max_uid);
	return(0);
}

}
<commit_msg>catch exception<commit_after>/*
 * Copyright (c) 2015, University Corporation for Atmospheric Research
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstring>
#include <stdint.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sstream>
#include <signal.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>
#include <exception>

#include <libcgroup.h>

#include <cgroup_context.h>

#include <log.h>

typedef uint64_t memory_t;

void enumerate_tasks(char* cgpath, uid_t victim, std::vector<pid_t>& cached_task_list);

uid_t get_uid(pid_t pid)
{
	char* status_path;
	asprintf(&status_path, "/proc/%d/status", pid);
	std::ifstream pid_status(status_path);
	std::string line;
	uid_t uid;
	char found = 0;
	while(pid_status.good())
	{
		std::getline(pid_status, line);
		size_t pos = line.find("Uid:",0);
		std::string token;
		if(pos != std::string::npos)
		{
			std::stringstream ls(line,std::ios_base::in);
			ls >> token; //header
			ls >> uid;
			found = 1;
			break;
		}
	}
	if(!found) {
		slog(LOG_ERR,"Error mapping UID for PID %d\n", pid);
	}
	pid_status.close();
	free(status_path);
	return(uid);
}

memory_t get_rss(pid_t pid)
{
	char* status_path;
	asprintf(&status_path, "/proc/%d/status", pid);
	std::ifstream pid_status(status_path);
	std::string line;
	uint64_t rss;
	char found = 0;

	while(pid_status.good())
	{
		std::getline(pid_status, line);
		size_t pos = line.find("VmRSS:",0);
		std::string token;
		if(pos != std::string::npos)
		{
			std::stringstream ls(line,std::ios_base::in);
			ls >> token; //header
			ls >> rss;
			//FIXME assumes always kB, which is currently correct
			//but could change
			found = 1;
			break;
		}
	}
	//if(!found) //ignore this case for now - due to races, it's fairly common
	//{
	//	//note: this can happen for kthreads
	//	slog(LOG_WARNING,"Error finding RSS for PID %d\n", pid);
	//}
	pid_status.close();
	free(status_path);
	return(rss);
}

extern "C"
{
char is_oom(struct cgroup_context* cgc)
{
	char* path;
	asprintf(&path, "/%s/%s/memory.oom_control", cgc->cgroup_path, cgc->cgroup_name);
	std::ifstream oc(path, std::ifstream::in);
	char isoom = -1;
	while(oc.good())
	{
		std::string token;
		oc >> token;
		if(token == "under_oom")
		{
			oc >> isoom;
		}
	}
	oc.close();
	free(path);
	if(isoom == -1) abort();
	if(isoom) return(1);
	return(0);
}
}

void get_cgroup_from_pid(pid_t pid, std::string& result)
{
	char* path;
	asprintf(&path, "/proc/%d/cgroup", pid);
	std::ifstream gcl(path, std::ifstream::in);
	try {
		while(gcl.good())
		{
			std::string token;
			gcl >> token;
			size_t delim = 0;
			delim = token.find_first_of(':');
			token.erase(0, delim);
			if(token.find("memory:/") != std::string::npos)
			{
				delim = token.find_first_of(':');
				if((delim+1) > token.length())
				{
					result.append("/;");
				}
				else
				{
					result.append(token.substr(delim+1));
					result.append(";");
				}
			}
		}
	gcl.close();
	}
	catch (std::exception& e)
	{
		//This shouldn't really ever happen
		//see gcc bug 53984
		slog(LOG_ALERT, "Unexpected Exception reading cgroup file\n");
		result.append(e.what());
	}
	free(path);
}

void sigkill_victim(pid_t pid)
{
	uid_t victim_uid;
	std::string cgroups;
	char* log_msg;
	victim_uid = get_uid(pid);
	get_cgroup_from_pid(pid, cgroups);
	asprintf(&log_msg, "killing UID:%u PID %d; cgroups: %s\n", victim_uid, pid,
			 cgroups.c_str()
			);
	slog(LOG_ALERT, log_msg);
	free(log_msg);
	kill(pid, SIGKILL);

}
void kill_victim(struct cgroup_context* cgc, uid_t victim_uid)
{

	std::vector<pid_t> cached_task_list;
	//get PID list
	char* cgpath;
	asprintf(&cgpath, "/%s/%s/", cgc->cgroup_path, cgc->cgroup_name);
	enumerate_tasks(cgpath, victim_uid, cached_task_list);
	free(cgpath);

	struct rlimit core_limit;
	core_limit.rlim_cur = 0;
	core_limit.rlim_max = 0;

	//Freeze all of user's processes
	for(std::vector<pid_t>::iterator i = cached_task_list.begin();
		i!= cached_task_list.end();
		i++)
	{
		cgroup_attach_task_pid(cgc->purgatory, *i);
	}

	char* root_freezer_path;
	char* root_memory_path;
	asprintf(&root_freezer_path, "/%s/tasks", cgc->freezer_path);
	asprintf(&root_memory_path, "/%s/tasks", cgc->cgroup_path);

	for(std::vector<pid_t>::iterator i = cached_task_list.begin();
		i!= cached_task_list.end();
		i++)
	{
		FILE* root_freezer = fopen(root_freezer_path,"w");
		FILE* root_memory = fopen(root_memory_path, "w");
		sigkill_victim(*i);
		fprintf(root_memory, "%d", *i);
		fclose(root_memory);
		fprintf(root_freezer, "%d", *i);
		fclose(root_freezer);
	}

	free(root_memory_path);
	free(root_freezer_path);
}

void enumerate_tasks(char* cgpath, uid_t victim_uid, std::vector<pid_t>& cached_task_list)
{
	pid_t pid;
	char* task_path;
	asprintf(&task_path, "/%s/tasks", cgpath);
	std::ifstream task_list(task_path,std::ifstream::in);
	while(task_list.good())
	{
		task_list >> pid;
		if(!(task_list.good())) //last read seems to be garbage
		{
			break;
		}
		uid_t uid = get_uid(pid);
		if(uid == victim_uid)
		{
			cached_task_list.push_back(pid);
		}
	}
	task_list.close();
	free(task_path);

	DIR* cgd = opendir(cgpath);
	if(cgd == NULL) slog(LOG_ALERT, "Error opening cgroup directory: %s\n", cgpath);
	struct dirent* de;
	struct stat stat_buf;
	int r;
	while((de=readdir(cgd))!=NULL)
	{
		char* tmp_path;
		asprintf(&tmp_path, "%s/%s/", cgpath, de->d_name);
		r = stat(tmp_path, &stat_buf);
		if(r!=0)
		{
			if(errno != ENOTDIR)
			{
		 	  slog(LOG_ALERT,
			   "enumerate_tasks(): stat() error code: %s on \"%s\"",
		  	   strerror(errno), tmp_path);
			}
		}
		else
		{
			if(S_ISDIR(stat_buf.st_mode) && de->d_name[0]!='.')
			{
				enumerate_tasks(tmp_path, victim_uid, cached_task_list);
			}
		}
		free(tmp_path);
	}
	closedir(cgd);
}

void enumerate_users(char* cgpath, std::map<uid_t, memory_t>& user_list)
{
	char* task_path;

	asprintf(&task_path, "/%s/tasks", cgpath);
	std::ifstream task_list(task_path,std::ifstream::in);
	pid_t pid;
	while(task_list.good())
	{
		task_list >> pid;
		if(!(task_list.good())) //last read seems to be garbage
		{
			break;
		}
		uid_t uid = get_uid(pid);
		memory_t rss = get_rss(pid);
		if(user_list.find(uid)==user_list.end())
		{
			user_list[uid] = rss;
		}
		else
		{
			user_list[uid] = user_list[uid] + rss;
		}
	}
	task_list.close();
	free(task_path);

	DIR* cgd = opendir(cgpath);
	if(cgd == NULL) slog(LOG_ALERT, "Error opening cgroup directory: %s\n", cgpath);
	struct dirent* de;
	struct stat stat_buf;
	int r;
	while((de=readdir(cgd))!=NULL)
	{
		char* tmp_path;
		asprintf(&tmp_path, "%s/%s/", cgpath, de->d_name);
		r = stat(tmp_path, &stat_buf);
		if(r!=0) {
			if(errno != ENOTDIR)
			{
			  slog(LOG_ALERT,
			  "enumerate_users(): stat() error code: %s on \"%s\"",
			  strerror(errno), tmp_path);
			}
		}
		else
		{
			if(S_ISDIR(stat_buf.st_mode) && de->d_name[0]!='.')
			{
				enumerate_users(tmp_path, user_list);
			}
		}
		free(tmp_path);
	}
	closedir(cgd);
}

extern "C"
{
	int find_victim(struct cgroup_context* cgc)
{
	std::map<uid_t,memory_t> user_list;
	char* cgpath;
	asprintf(&cgpath, "/%s/%s/", cgc->cgroup_path, cgc->cgroup_name);
	enumerate_users(cgpath, user_list);
	free(cgpath);

	if(user_list.size() < 1)
	{
		return(-1);
	}

	memory_t max = (user_list.begin())->second;
	uid_t max_uid = (user_list.begin())->first;
	for(std::map<uid_t,memory_t>::iterator i = user_list.begin();
		i!=user_list.end();
		i++)
	{
		if(i->second > max)
			{
				max_uid = i->first;
				max = i->second;
			}
	}
	kill_victim(cgc, max_uid);
	return(0);
}

}
<|endoftext|>


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

#include "CCPACSFrame.h"

#include <gp_Pnt.hxx>
#include <gp_Pln.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <ShapeExtend_WireData.hxx>
#include <ShapeFix_Wire.hxx>
#include <BRepAlgoAPI_Section.hxx>

#include "tiglcommonfunctions.h"
#include "CNamedShape.h"
#include "CCPACSFuselageSegment.h"
#include "CCPACSProfileBasedStructuralElement.h"
#include "CCPACSFuselageStringerFramePosition.h"
#include "CCPACSCrossBeamAssemblyPosition.h"
#include "CCPACSCrossBeamStrutAssemblyPosition.h"
#include "CCPACSFuselageStringer.h"

namespace tigl
{
CCPACSFrame::CCPACSFrame(CCPACSFramesAssembly* parent, CTiglUIDManager* uidMgr)
    : generated::CPACSFrame(parent, uidMgr)
{
}

void CCPACSFrame::Invalidate()
{
    for (int i = 0; i < 2; ++i) {
        m_geomCache[i] = boost::none;
    }
}

TopoDS_Shape CCPACSFrame::GetGeometry(bool just1DElements, TiglCoordinateSystem cs)
{
    if (!m_geomCache[just1DElements])
        BuildGeometry(just1DElements);

    const TopoDS_Shape shape = m_geomCache[just1DElements].value();
    if (cs == GLOBAL_COORDINATE_SYSTEM) {
        CTiglTransformation trafo = m_parent->GetParent()->GetParent()->GetTransformationMatrix();
        return trafo.Transform(shape);
    }
    else
        return shape;
}

void CCPACSFrame::BuildGeometry(bool just1DElements)
{
    if (m_framePositions.empty())
        throw CTiglError("Cannot build frame geometry, no frame positions defined in XML", TIGL_XML_ERROR);

    // this function build the frame in 2 steps :
    // 1) path definition (projection on the fuselage)
    // 2) if not just 1D element, build and sweep the profile all along the path

    CCPACSFuselage& fuselage        = *m_parent->GetParent()->GetParent();
    const TopoDS_Shape fuselageLoft = fuselage.GetLoft()->Shape();

    // initialize object needed for the frame build
    gp_Pln profilePlane;
    TopoDS_Wire path;

    if (m_framePositions.size() == 1) {
        // if there is 1 position ==> the path is around the fuselage, in an X normal orientated plane
        const CCPACSFuselageStringerFramePosition& fp = *m_framePositions[0];

        TopoDS_Shape section = BRepAlgoAPI_Section(fuselageLoft, gp_Pln(fp.GetRefPoint(), gp_Dir(1, 0, 0))).Shape();
        path                 = BuildWireFromEdges(section);

        if (!just1DElements) {
            // -1) place the point and the plane (X Axis as normal vector)
            const gp_Lin iAxe = fuselage.Intersection(fp);
            const gp_Ax1 xAxe(iAxe.Location(),
                              gp_Dir(1, 0, 0)); // parallel to x Axis => used to rotate the profile plane
            profilePlane = gp_Pln(gp_Ax3(iAxe.Location(), iAxe.Rotated(xAxe, M_PI / 2.).Direction(), gp_Dir(1, 0, 0)));
        }
    }
    else {
        // if there is more than 1 position ==> the path is projected segment by segment on the fuselage
        // create a frame in the fuselage, following the path described by all the positions in frame/frameposition[]

        // place every point on the fuselage loft
        std::vector<gp_Lin> pointList;
        for (size_t i = 0; i < m_framePositions.size(); i++)
            pointList.push_back(fuselage.Intersection(*m_framePositions[i]));

        // with a ShapeExtend_WireData, the individual segment orientation is not crucial during the wire building
        Handle(ShapeExtend_WireData) wirePath = new ShapeExtend_WireData;
        for (size_t i = 0; i < pointList.size() - 1; i++) {
            const gp_Pnt intersection0 = pointList.at(i + 0).Location();
            const gp_Pnt intersection1 = pointList.at(i + 1).Location();

            const gp_Pnt refPoint0 = m_framePositions[i + 0]->GetRefPoint();
            const gp_Pnt refPoint1 = m_framePositions[i + 1]->GetRefPoint();
            const double refAngle0 = m_framePositions[i + 0]->GetReferenceAngle();
            const double refAngle1 = m_framePositions[i + 1]->GetReferenceAngle();

            // first, we cut the initial segment in 2 parts
            const gp_Pnt midIntersection = fuselage.Intersection((refPoint0.XYZ() + refPoint1.XYZ()) / 2, Radians((refAngle0 + refAngle1) / 2.)).Location();

            const gp_Pnt midRefPoint0 = (refPoint0.XYZ() * 0.25 + refPoint1.XYZ() * 0.75);
            const gp_Pnt midRefPoint1 = (refPoint0.XYZ() * 0.75 + refPoint1.XYZ() * 0.25);

            // third, we project the sub-segment on the fuselage, according to the projections points,
            // and add it to the path add the current part in the path builder
            wirePath->Add(fuselage.projectConic(BRepBuilderAPI_MakeEdge(intersection0, midIntersection).Edge(), midRefPoint0));
            wirePath->Add(fuselage.projectConic(BRepBuilderAPI_MakeEdge(midIntersection, intersection1).Edge(), midRefPoint1));
        }

        // finally, we can create the path with all the projection obtained
        // TODO : throw exception if the frame's path creation fail
        // the ShapeFix_Wire classe will create a proper wire (direction, order) with all the data in the ShapeExtend_WireData
        ShapeFix_Wire fixWire;
        fixWire.Load(wirePath);
        fixWire.Perform();
        fixWire.FixReorder();
        fixWire.FixConnected();
        path = fixWire.WireAPIMake();
        path.Orientation(TopAbs_FORWARD);

        if (!just1DElements) {
            // creation of the profile plane
            // the plan axis system is defined by : the location, the normal vector, the x vector. It's a right-handed system
            // the rotation is necessary to have a normal vector tangent to the fuselage
            const gp_Ax1 xAxe(pointList.at(0).Location(),
                              gp_Dir(1, 0, 0)); // parallel to x Axis => used to rotate the profile plane
            profilePlane = gp_Pln(gp_Ax3(pointList.at(0).Location(),
                                         pointList.at(0).Rotated(xAxe, M_PI / 2.).Direction(), gp_Dir(1, 0, 0)));
        }
    }

    if (just1DElements) {
        m_geomCache[true] = path;
    }
    else {
        const CCPACSProfileBasedStructuralElement& pbse = m_uidMgr->ResolveObject<CCPACSProfileBasedStructuralElement>(
            m_framePositions.front()->GetStructuralElementUID());
        m_geomCache[false] = pbse.makeFromWire(path, profilePlane);
    }
}
} // namespace tigl
<commit_msg>fixed coordinate system issue with single position frames<commit_after>/*
* Copyright (c) 2018 Airbus Defence and Space and RISC Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "CCPACSFrame.h"

#include <gp_Pnt.hxx>
#include <gp_Pln.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <ShapeExtend_WireData.hxx>
#include <ShapeFix_Wire.hxx>
#include <BRepAlgoAPI_Section.hxx>

#include "tiglcommonfunctions.h"
#include "CNamedShape.h"
#include "CCPACSFuselageSegment.h"
#include "CCPACSProfileBasedStructuralElement.h"
#include "CCPACSFuselageStringerFramePosition.h"
#include "CCPACSCrossBeamAssemblyPosition.h"
#include "CCPACSCrossBeamStrutAssemblyPosition.h"
#include "CCPACSFuselageStringer.h"

namespace tigl
{
CCPACSFrame::CCPACSFrame(CCPACSFramesAssembly* parent, CTiglUIDManager* uidMgr)
    : generated::CPACSFrame(parent, uidMgr)
{
}

void CCPACSFrame::Invalidate()
{
    for (int i = 0; i < 2; ++i) {
        m_geomCache[i] = boost::none;
    }
}

TopoDS_Shape CCPACSFrame::GetGeometry(bool just1DElements, TiglCoordinateSystem cs)
{
    if (!m_geomCache[just1DElements])
        BuildGeometry(just1DElements);

    const TopoDS_Shape shape = m_geomCache[just1DElements].value();
    if (cs == GLOBAL_COORDINATE_SYSTEM) {
        CTiglTransformation trafo = m_parent->GetParent()->GetParent()->GetTransformationMatrix();
        return trafo.Transform(shape);
    }
    else
        return shape;
}

void CCPACSFrame::BuildGeometry(bool just1DElements)
{
    if (m_framePositions.empty())
        throw CTiglError("Cannot build frame geometry, no frame positions defined in XML", TIGL_XML_ERROR);

    // this function build the frame in 2 steps :
    // 1) path definition (projection on the fuselage)
    // 2) if not just 1D element, build and sweep the profile all along the path

    CCPACSFuselage& fuselage        = *m_parent->GetParent()->GetParent();

    // initialize object needed for the frame build
    gp_Pln profilePlane;
    TopoDS_Wire path;

    if (m_framePositions.size() == 1) {
        // if there is 1 position ==> the path is around the fuselage, in an X normal orientated plane
        const CCPACSFuselageStringerFramePosition& fp = *m_framePositions[0];

        const TopoDS_Shape fuselageLoft = fuselage.GetLoft(FUSELAGE_COORDINATE_SYSTEM);
        const TopoDS_Shape section = BRepAlgoAPI_Section(fuselageLoft, gp_Pln(fp.GetRefPoint(), gp_Dir(1, 0, 0))).Shape();
        path = BuildWireFromEdges(section);

        if (!just1DElements) {
            // -1) place the point and the plane (X Axis as normal vector)
            const gp_Lin iAxe = fuselage.Intersection(fp);
            const gp_Ax1 xAxe(iAxe.Location(),
                              gp_Dir(1, 0, 0)); // parallel to x Axis => used to rotate the profile plane
            profilePlane = gp_Pln(gp_Ax3(iAxe.Location(), iAxe.Rotated(xAxe, M_PI / 2.).Direction(), gp_Dir(1, 0, 0)));
        }
    }
    else {
        // if there is more than 1 position ==> the path is projected segment by segment on the fuselage
        // create a frame in the fuselage, following the path described by all the positions in frame/frameposition[]

        // place every point on the fuselage loft
        std::vector<gp_Lin> pointList;
        for (size_t i = 0; i < m_framePositions.size(); i++)
            pointList.push_back(fuselage.Intersection(*m_framePositions[i]));

        // with a ShapeExtend_WireData, the individual segment orientation is not crucial during the wire building
        Handle(ShapeExtend_WireData) wirePath = new ShapeExtend_WireData;
        for (size_t i = 0; i < pointList.size() - 1; i++) {
            const gp_Pnt intersection0 = pointList.at(i + 0).Location();
            const gp_Pnt intersection1 = pointList.at(i + 1).Location();

            const gp_Pnt refPoint0 = m_framePositions[i + 0]->GetRefPoint();
            const gp_Pnt refPoint1 = m_framePositions[i + 1]->GetRefPoint();
            const double refAngle0 = m_framePositions[i + 0]->GetReferenceAngle();
            const double refAngle1 = m_framePositions[i + 1]->GetReferenceAngle();

            // first, we cut the initial segment in 2 parts
            const gp_Pnt midIntersection = fuselage.Intersection((refPoint0.XYZ() + refPoint1.XYZ()) / 2, Radians((refAngle0 + refAngle1) / 2.)).Location();

            const gp_Pnt midRefPoint0 = (refPoint0.XYZ() * 0.25 + refPoint1.XYZ() * 0.75);
            const gp_Pnt midRefPoint1 = (refPoint0.XYZ() * 0.75 + refPoint1.XYZ() * 0.25);

            // third, we project the sub-segment on the fuselage, according to the projections points,
            // and add it to the path add the current part in the path builder
            wirePath->Add(fuselage.projectConic(BRepBuilderAPI_MakeEdge(intersection0, midIntersection).Edge(), midRefPoint0));
            wirePath->Add(fuselage.projectConic(BRepBuilderAPI_MakeEdge(midIntersection, intersection1).Edge(), midRefPoint1));
        }

        // finally, we can create the path with all the projection obtained
        // TODO : throw exception if the frame's path creation fail
        // the ShapeFix_Wire classe will create a proper wire (direction, order) with all the data in the ShapeExtend_WireData
        ShapeFix_Wire fixWire;
        fixWire.Load(wirePath);
        fixWire.Perform();
        fixWire.FixReorder();
        fixWire.FixConnected();
        path = fixWire.WireAPIMake();
        path.Orientation(TopAbs_FORWARD);

        if (!just1DElements) {
            // creation of the profile plane
            // the plan axis system is defined by : the location, the normal vector, the x vector. It's a right-handed system
            // the rotation is necessary to have a normal vector tangent to the fuselage
            const gp_Ax1 xAxe(pointList.at(0).Location(),
                              gp_Dir(1, 0, 0)); // parallel to x Axis => used to rotate the profile plane
            profilePlane = gp_Pln(gp_Ax3(pointList.at(0).Location(),
                                         pointList.at(0).Rotated(xAxe, M_PI / 2.).Direction(), gp_Dir(1, 0, 0)));
        }
    }

    if (just1DElements) {
        m_geomCache[true] = path;
    }
    else {
        const CCPACSProfileBasedStructuralElement& pbse = m_uidMgr->ResolveObject<CCPACSProfileBasedStructuralElement>(
            m_framePositions.front()->GetStructuralElementUID());
        m_geomCache[false] = pbse.makeFromWire(path, profilePlane);
    }
}
} // namespace tigl
<|endoftext|>


<commit_before>#ifdef WIN32
	#include <intrin.h>
	#include <windows.h>
#endif
#include "glresource.hpp"
#include "event.hpp"

namespace rs {
	namespace {
		bool g_bglfuncInit = false;
	}
	#define GLGETPROC(name) SDL_GL_GetProcAddress(BOOST_PP_STRINGIZE(name))
	#if defined(_WIN32)
		namespace {
			using SetSwapInterval_t = bool APIENTRY (*)(int);
			SetSwapInterval_t g_setswapinterval = nullptr;
		}
		void LoadGLAux() {
			g_setswapinterval = (SetSwapInterval_t)GLGETPROC(wglSwapIntervalEXT);
		}
	#else
		namespace {
			using SetSwapInterval_t = void APIENTRY (*)(int);
			SetSwapInterval_t g_setswapinterval = nullptr;
		}
		void LoadGLAux() {
			// EXTかSGIのどちらかが存在する事を期待
			try {
				g_setswapinterval = (SetSwapInterval_t)GLGETPROC(glXSwapIntervalSGI);
			} catch(const std::runtime_error& e) {
				g_setswapinterval = (SetSwapInterval_t)GLGETPROC(glXSwapIntervalEXT);
			}
		}
	#endif
	bool GLWrap::isGLFuncLoaded() {
		return g_bglfuncInit;
	}
	void IGL_Draw::setSwapInterval(int n) {
		g_setswapinterval(n);
	}
	void IGL_OtherSingle::setSwapInterval(int n) {
		GLW.getDrawHandler().postExec([=](){
			IGL_Draw().setSwapInterval(n);
		});
	}

	// OpenGL関数ロード
	// FuncNameで読み込めなければFuncNameARBとFuncNameEXTで試す
	#define GLDEFINE(...)
 	#define DEF_GLMETHOD(ret_type, num, name, args, argnames) \
		GLWrap::name = nullptr; \
 		GLWrap::name = (typename GLWrap::t_##name) GLGETPROC(name); \
		if(GLWrap::name == nullptr) GLWrap::name = (typename GLWrap::t_##name)GLGETPROC(BOOST_PP_CAT(name,ARB)); \
		if(GLWrap::name == nullptr) GLWrap::name = (typename GLWrap::t_##name)GLGETPROC(BOOST_PP_CAT(name,EXT)); \
		Assert(Warn, GLWrap::name != nullptr, "could not load OpenGL function: %1%", #name)
		void GLWrap::loadGLFunc() {
			// 各種API関数
			#ifndef ANDROID
				#ifdef ANDROID
					#include "android_gl.inc"
				#elif defined(WIN32)
					#include "mingw_gl.inc"
				#else
					#include "linux_gl.inc"
				#endif
			#endif
			// その他OS依存なAPI関数
			LoadGLAux();
			g_bglfuncInit = true;
		}
	#undef DEF_GLMETHOD
	#undef GLDEFINE
	// ---------------------- GLShader ----------------------
	void GLShader::_initShader() {
		_idSh = GL.glCreateShader(_flag);

		std::string ss("#version 110\n");
		ss.append(_source);
		const auto* pStr = ss.c_str();
		GL.glShaderSource(_idSh, 1, &pStr, nullptr);
		GL.glCompileShader(_idSh);

		// エラーが無かったか確認
		GLint compiled;
		GL.glGetShaderiv(_idSh, GL_COMPILE_STATUS, &compiled);
		AssertT(Trap, compiled==GL_TRUE, (GLE_ShaderError)(const std::string&)(GLuint), ss, _idSh)
	}

	GLShader::GLShader() {}
	GLShader::GLShader(GLuint flag, const std::string& src): _flag(flag), _source(src) {
		_initShader();
	}
	GLShader::~GLShader() {
		onDeviceLost();
	}
	bool GLShader::isEmpty() const {
		return _source.empty();
	}
	int GLShader::getShaderID() const {
		return _idSh;
	}
	void GLShader::onDeviceLost() {
		if(_idSh!=0) {
			GL.glDeleteShader(_idSh);
			_idSh = 0;
		}
	}
	void GLShader::onDeviceReset() {
		if(!isEmpty() && _idSh==0)
			_initShader();
	}

	// ---------------------- draw::Program ----------------------
	namespace draw {
		Program::Program(HProg hProg):
			Token(hProg), _idProg(hProg.ref()->getProgramID()) {}
		Program::Program(Program&& p): Token(p), _idProg(p._idProg) {}
		void Program::exec() {
			GL.glUseProgram(_idProg);
		}
	}

	// ---------------------- GLProgram ----------------------
	GLProgram::GLProgram(HSh vsh, HSh psh) {
		_shader[ShType::VERTEX] = vsh;
		_shader[ShType::PIXEL] = psh;
		_initProgram();
	}
	GLProgram::GLProgram(HSh vsh, HSh gsh, HSh psh) {
		_shader[ShType::VERTEX] = vsh;
		_shader[ShType::PIXEL] = psh;
		_shader[ShType::GEOMETRY] = gsh;
		_initProgram();
	}
	void GLProgram::_initProgram() {
		_idProg = GL.glCreateProgram();
		for(int i=0 ; i<static_cast<int>(ShType::NUM_SHTYPE) ; i++) {
			auto& sh = _shader[i];
			// Geometryシェーダー以外は必須
			if(sh.valid()) {
				GL.glAttachShader(_idProg, sh.cref()->getShaderID());
				GLEC_ChkP(Trap)
			} else {
				AssertT(Trap, i==ShType::GEOMETRY, (GLE_Error)(const char*), "missing shader elements (vertex or fragment)")
			}
		}

		GL.glLinkProgram(_idProg);
		// エラーが無いかチェック
		int ib;
		GL.glGetProgramiv(_idProg, GL_LINK_STATUS, &ib);
		AssertT(Trap, ib==GL_TRUE, (GLE_ProgramError)(GLuint), _idProg)
	}
	GLProgram::~GLProgram() {
		if(mgr_gl.isInDtor()) {
			for(auto& p : _shader)
				p.setNull();
		}
		onDeviceLost();
	}
	void GLProgram::onDeviceLost() {
		if(_idProg != 0) {
			// ShaderはProgramをDeleteすれば自動的にdetachされる
			GL.glDeleteProgram(_idProg);
			_idProg = 0;
		}
	}
	void GLProgram::onDeviceReset() {
		if(_idProg == 0) {
			// 先にshaderがresetされてないかもしれないので、ここでしておく
			for(auto& s : _shader) {
				if(s)
					s.cref()->onDeviceReset();
			}
			_initProgram();
		}
	}
	const HLSh& GLProgram::getShader(ShType type) const {
		return _shader[(int)type];
	}
	int GLProgram::getUniformID(const std::string& name) const {
		GLint id = getUniformIDNc(name);
		AssertT(Trap, id>=0, (GLE_ParamNotFound)(const std::string&), name)
		return id;
	}
	int GLProgram::getUniformIDNc(const std::string& name) const {
		return GL.glGetUniformLocation(getProgramID(), name.c_str());
	}
	int GLProgram::getAttribID(const std::string& name) const {
		GLint id = getAttribIDNc(name);
		AssertT(Trap, id>=0, (GLE_ParamNotFound)(const std::string&), name)
		return id;
	}
	GLuint GLProgram::getProgramID() const {
		return _idProg;
	}
	int GLProgram::getAttribIDNc(const std::string& name) const {
		return GL.glGetAttribLocation(getProgramID(), name.c_str());
	}
	void GLProgram::use() const {
		GL.glUseProgram(getProgramID());
		GLEC_ChkP(Trap)
	}
}
<commit_msg>androidでコンパイル時にAPIENTRYマクロの代わりにGL_APIENTRYを使用<commit_after>#ifdef WIN32
	#include <intrin.h>
	#include <windows.h>
#endif
#include "glresource.hpp"
#include "event.hpp"

namespace rs {
	namespace {
		bool g_bglfuncInit = false;
	}
	#define GLGETPROC(name) SDL_GL_GetProcAddress(BOOST_PP_STRINGIZE(name))
	#if defined(_WIN32)
		namespace {
			using SetSwapInterval_t = bool APIENTRY (*)(int);
			SetSwapInterval_t g_setswapinterval = nullptr;
		}
		void LoadGLAux() {
			g_setswapinterval = (SetSwapInterval_t)GLGETPROC(wglSwapIntervalEXT);
		}
	#else
		namespace {
			#ifdef __ANDROID__
				using SetSwapInterval_t = void GL_APIENTRY (*)(int);
			#else
				using SetSwapInterval_t = void APIENTRY (*)(int);
			#endif
			SetSwapInterval_t g_setswapinterval = nullptr;
		}
		void LoadGLAux() {
			// EXTかSGIのどちらかが存在する事を期待
			try {
				g_setswapinterval = (SetSwapInterval_t)GLGETPROC(glXSwapIntervalSGI);
			} catch(const std::runtime_error& e) {
				g_setswapinterval = (SetSwapInterval_t)GLGETPROC(glXSwapIntervalEXT);
			}
		}
	#endif
	bool GLWrap::isGLFuncLoaded() {
		return g_bglfuncInit;
	}
	void IGL_Draw::setSwapInterval(int n) {
		g_setswapinterval(n);
	}
	void IGL_OtherSingle::setSwapInterval(int n) {
		GLW.getDrawHandler().postExec([=](){
			IGL_Draw().setSwapInterval(n);
		});
	}

	// OpenGL関数ロード
	// FuncNameで読み込めなければFuncNameARBとFuncNameEXTで試す
	#define GLDEFINE(...)
 	#define DEF_GLMETHOD(ret_type, num, name, args, argnames) \
		GLWrap::name = nullptr; \
 		GLWrap::name = (typename GLWrap::t_##name) GLGETPROC(name); \
		if(GLWrap::name == nullptr) GLWrap::name = (typename GLWrap::t_##name)GLGETPROC(BOOST_PP_CAT(name,ARB)); \
		if(GLWrap::name == nullptr) GLWrap::name = (typename GLWrap::t_##name)GLGETPROC(BOOST_PP_CAT(name,EXT)); \
		Assert(Warn, GLWrap::name != nullptr, "could not load OpenGL function: %1%", #name)
		void GLWrap::loadGLFunc() {
			// 各種API関数
			#ifndef ANDROID
				#ifdef ANDROID
					#include "android_gl.inc"
				#elif defined(WIN32)
					#include "mingw_gl.inc"
				#else
					#include "linux_gl.inc"
				#endif
			#endif
			// その他OS依存なAPI関数
			LoadGLAux();
			g_bglfuncInit = true;
		}
	#undef DEF_GLMETHOD
	#undef GLDEFINE
	// ---------------------- GLShader ----------------------
	void GLShader::_initShader() {
		_idSh = GL.glCreateShader(_flag);

		std::string ss("#version 110\n");
		ss.append(_source);
		const auto* pStr = ss.c_str();
		GL.glShaderSource(_idSh, 1, &pStr, nullptr);
		GL.glCompileShader(_idSh);

		// エラーが無かったか確認
		GLint compiled;
		GL.glGetShaderiv(_idSh, GL_COMPILE_STATUS, &compiled);
		AssertT(Trap, compiled==GL_TRUE, (GLE_ShaderError)(const std::string&)(GLuint), ss, _idSh)
	}

	GLShader::GLShader() {}
	GLShader::GLShader(GLuint flag, const std::string& src): _flag(flag), _source(src) {
		_initShader();
	}
	GLShader::~GLShader() {
		onDeviceLost();
	}
	bool GLShader::isEmpty() const {
		return _source.empty();
	}
	int GLShader::getShaderID() const {
		return _idSh;
	}
	void GLShader::onDeviceLost() {
		if(_idSh!=0) {
			GL.glDeleteShader(_idSh);
			_idSh = 0;
		}
	}
	void GLShader::onDeviceReset() {
		if(!isEmpty() && _idSh==0)
			_initShader();
	}

	// ---------------------- draw::Program ----------------------
	namespace draw {
		Program::Program(HProg hProg):
			Token(hProg), _idProg(hProg.ref()->getProgramID()) {}
		Program::Program(Program&& p): Token(p), _idProg(p._idProg) {}
		void Program::exec() {
			GL.glUseProgram(_idProg);
		}
	}

	// ---------------------- GLProgram ----------------------
	GLProgram::GLProgram(HSh vsh, HSh psh) {
		_shader[ShType::VERTEX] = vsh;
		_shader[ShType::PIXEL] = psh;
		_initProgram();
	}
	GLProgram::GLProgram(HSh vsh, HSh gsh, HSh psh) {
		_shader[ShType::VERTEX] = vsh;
		_shader[ShType::PIXEL] = psh;
		_shader[ShType::GEOMETRY] = gsh;
		_initProgram();
	}
	void GLProgram::_initProgram() {
		_idProg = GL.glCreateProgram();
		for(int i=0 ; i<static_cast<int>(ShType::NUM_SHTYPE) ; i++) {
			auto& sh = _shader[i];
			// Geometryシェーダー以外は必須
			if(sh.valid()) {
				GL.glAttachShader(_idProg, sh.cref()->getShaderID());
				GLEC_ChkP(Trap)
			} else {
				AssertT(Trap, i==ShType::GEOMETRY, (GLE_Error)(const char*), "missing shader elements (vertex or fragment)")
			}
		}

		GL.glLinkProgram(_idProg);
		// エラーが無いかチェック
		int ib;
		GL.glGetProgramiv(_idProg, GL_LINK_STATUS, &ib);
		AssertT(Trap, ib==GL_TRUE, (GLE_ProgramError)(GLuint), _idProg)
	}
	GLProgram::~GLProgram() {
		if(mgr_gl.isInDtor()) {
			for(auto& p : _shader)
				p.setNull();
		}
		onDeviceLost();
	}
	void GLProgram::onDeviceLost() {
		if(_idProg != 0) {
			// ShaderはProgramをDeleteすれば自動的にdetachされる
			GL.glDeleteProgram(_idProg);
			_idProg = 0;
		}
	}
	void GLProgram::onDeviceReset() {
		if(_idProg == 0) {
			// 先にshaderがresetされてないかもしれないので、ここでしておく
			for(auto& s : _shader) {
				if(s)
					s.cref()->onDeviceReset();
			}
			_initProgram();
		}
	}
	const HLSh& GLProgram::getShader(ShType type) const {
		return _shader[(int)type];
	}
	int GLProgram::getUniformID(const std::string& name) const {
		GLint id = getUniformIDNc(name);
		AssertT(Trap, id>=0, (GLE_ParamNotFound)(const std::string&), name)
		return id;
	}
	int GLProgram::getUniformIDNc(const std::string& name) const {
		return GL.glGetUniformLocation(getProgramID(), name.c_str());
	}
	int GLProgram::getAttribID(const std::string& name) const {
		GLint id = getAttribIDNc(name);
		AssertT(Trap, id>=0, (GLE_ParamNotFound)(const std::string&), name)
		return id;
	}
	GLuint GLProgram::getProgramID() const {
		return _idProg;
	}
	int GLProgram::getAttribIDNc(const std::string& name) const {
		return GL.glGetAttribLocation(getProgramID(), name.c_str());
	}
	void GLProgram::use() const {
		GL.glUseProgram(getProgramID());
		GLEC_ChkP(Trap)
	}
}
<|endoftext|>