<commit_before>/**
* TLS Record Writing
* (C) 2004-2006 Jack Lloyd
*
* Released under the terms of the Botan license
*/

#include <botan/tls_record.h>
#include <botan/handshake_hash.h>
#include <botan/lookup.h>
#include <botan/loadstor.h>

namespace Botan {

/**
* Record_Writer Constructor
*/
Record_Writer::Record_Writer(Socket& sock) :
   socket(sock), buffer(DEFAULT_BUFFERSIZE)
   {
   reset();
   }

/**
* Reset the state
*/
void Record_Writer::reset()
   {
   compress.reset();
   cipher.reset();
   mac.reset();
   buffer.clear();
   do_compress = false;
   major = minor = buf_type = 0;
   pad_amount = mac_size = buf_pos = 0;
   seq_no = 0;
   }

/**
* Set the version to use
*/
void Record_Writer::set_version(Version_Code version)
   {
   if(version != SSL_V3 && version != TLS_V10)
      throw Invalid_Argument("Record_Writer: Invalid protocol version");

   major = (version >> 8) & 0xFF;
   minor = (version & 0xFF);
   }

/**
* Set the compression algorithm
*/
void Record_Writer::set_compressor(Filter* compressor)
   {
   throw TLS_Exception(INTERNAL_ERROR, "Compression not implemented (FIXME)");
   compress.append(compressor);
   }

/**
* Set the keys for writing
*/
void Record_Writer::set_keys(const CipherSuite& suite, const SessionKeys& keys,
                             Connection_Side side)
   {
   cipher.reset();
   mac.reset();

   SymmetricKey mac_key, cipher_key;
   InitializationVector iv;

   if(side == CLIENT)
      {
      cipher_key = keys.client_cipher_key();
      iv = keys.client_iv();
      mac_key = keys.client_mac_key();
      }
   else
      {
      cipher_key = keys.server_cipher_key();
      iv = keys.server_iv();
      mac_key = keys.server_mac_key();
      }

   const std::string cipher_algo = suite.cipher_algo();
   const std::string mac_algo = suite.mac_algo();

   if(have_block_cipher(cipher_algo))
      {
      cipher.append(get_cipher(
                       cipher_algo + "/CBC/NoPadding",
                       cipher_key, iv, ENCRYPTION)
         );
      pad_amount = block_size_of(cipher_algo);
      }
   else if(have_stream_cipher(cipher_algo))
      {
      cipher.append(get_cipher(cipher_algo, cipher_key, ENCRYPTION));
      pad_amount = 0;
      }
   else
      throw Invalid_Argument("Record_Writer: Unknown cipher " + cipher_algo);

   if(have_hash(mac_algo))
      {
      if(major == 3 && minor == 0)
         mac.append(new MAC_Filter("SSL3-MAC(" + mac_algo + ")", mac_key));
      else
         mac.append(new MAC_Filter("HMAC(" + mac_algo + ")", mac_key));

      mac_size = output_length_of(mac_algo);
      }
   else
      throw Invalid_Argument("Record_Writer: Unknown hash " + mac_algo);
   }

/**
* Send one or more records to the other side
*/
void Record_Writer::send(byte type, byte input)
   {
   send(type, &input, 1);
   }

/**
* Send one or more records to the other side
*/
void Record_Writer::send(byte type, const byte input[], u32bit length)
   {
   if(type != buf_type)
      flush();

   const u32bit BUFFER_SIZE = buffer.size();
   buf_type = type;

   // FIXME: compression right here

   buffer.copy(buf_pos, input, length);
   if(buf_pos + length >= BUFFER_SIZE)
      {
      send_record(buf_type, buffer, length);
      input += (BUFFER_SIZE - buf_pos);
      length -= (BUFFER_SIZE - buf_pos);
      while(length >= BUFFER_SIZE)
         {
         send_record(buf_type, input, BUFFER_SIZE);
         input += BUFFER_SIZE;
         length -= BUFFER_SIZE;
         }
      buffer.copy(input, length);
      buf_pos = 0;
      }
   buf_pos += length;
   }

/**
* Split buffer into records, and send them all
*/
void Record_Writer::flush()
   {
   const byte* buf_ptr = buffer.begin();
   u32bit offset = 0;

   while(offset != buf_pos)
      {
      u32bit record_size = buf_pos - offset;
      if(record_size > MAX_PLAINTEXT_SIZE)
         record_size = MAX_PLAINTEXT_SIZE;

      send_record(buf_type, buf_ptr + offset, record_size);
      offset += record_size;
      }
   buf_type = 0;
   buf_pos = 0;
   }

/**
* Encrypt and send the record
*/
void Record_Writer::send_record(byte type, const byte buf[], u32bit length)
   {
   if(length >= MAX_COMPRESSED_SIZE)
      throw TLS_Exception(INTERNAL_ERROR,
                          "Record_Writer: Compressed packet is too big");

   if(mac_size == 0)
      send_record(type, major, minor, buf, length);
   else
      {
      mac.start_msg();
      for(u32bit j = 0; j != 8; j++)
         mac.write(get_byte(j, seq_no));
      mac.write(type);

      if(major > 3 || (major == 3 && minor != 0))
         {
         mac.write(major);
         mac.write(minor);
         }

      mac.write(get_byte(2, length));
      mac.write(get_byte(3, length));
      mac.write(buf, length);
      mac.end_msg();

      SecureVector<byte> buf_mac = mac.read_all(Pipe::LAST_MESSAGE);

      cipher.start_msg();
      cipher.write(buf, length);
      cipher.write(buf_mac);
      if(pad_amount)
         {
         u32bit pad_val =
            (pad_amount - (1 + length + buf_mac.size())) % pad_amount;

         for(u32bit j = 0; j != pad_val + 1; j++)
            cipher.write(pad_val);
         }
      cipher.end_msg();

      SecureVector<byte> output = cipher.read_all(Pipe::LAST_MESSAGE);

      send_record(type, major, minor, output, output.size());

      seq_no++;
      }
   }

/**
* Send a final record packet
*/
void Record_Writer::send_record(byte type, byte major, byte minor,
                                const byte out[], u32bit length)
   {
   if(length >= MAX_CIPHERTEXT_SIZE)
      throw TLS_Exception(INTERNAL_ERROR,
                          "Record_Writer: Record is too big");

   byte header[5] = { type, major, minor, 0 };
   for(u32bit j = 0; j != 2; j++)
      header[j+3] = get_byte<u16bit>(j, length);

   // FIXME: tradoff of TCP/syscall overhead vs copy overhead
   socket.write(header, 5);
   socket.write(out, length);
   }

/**
* Send an alert
*/
void Record_Writer::alert(Alert_Level level, Alert_Type type)
   {
   byte alert[2] = { level, type };
   send(ALERT, alert, sizeof(alert));
   flush();
   }

}
<commit_msg>Also remove compression bits from record writer<commit_after>/**
* TLS Record Writing
* (C) 2004-2010 Jack Lloyd
*
* Released under the terms of the Botan license
*/

#include <botan/tls_record.h>
#include <botan/handshake_hash.h>
#include <botan/lookup.h>
#include <botan/loadstor.h>

namespace Botan {

/**
* Record_Writer Constructor
*/
Record_Writer::Record_Writer(Socket& sock) :
   socket(sock), buffer(DEFAULT_BUFFERSIZE)
   {
   reset();
   }

/**
* Reset the state
*/
void Record_Writer::reset()
   {
   cipher.reset();
   mac.reset();
   buffer.clear();
   major = minor = buf_type = 0;
   pad_amount = mac_size = buf_pos = 0;
   seq_no = 0;
   }

/**
* Set the version to use
*/
void Record_Writer::set_version(Version_Code version)
   {
   if(version != SSL_V3 && version != TLS_V10)
      throw Invalid_Argument("Record_Writer: Invalid protocol version");

   major = (version >> 8) & 0xFF;
   minor = (version & 0xFF);
   }

/**
* Set the keys for writing
*/
void Record_Writer::set_keys(const CipherSuite& suite, const SessionKeys& keys,
                             Connection_Side side)
   {
   cipher.reset();
   mac.reset();

   SymmetricKey mac_key, cipher_key;
   InitializationVector iv;

   if(side == CLIENT)
      {
      cipher_key = keys.client_cipher_key();
      iv = keys.client_iv();
      mac_key = keys.client_mac_key();
      }
   else
      {
      cipher_key = keys.server_cipher_key();
      iv = keys.server_iv();
      mac_key = keys.server_mac_key();
      }

   const std::string cipher_algo = suite.cipher_algo();
   const std::string mac_algo = suite.mac_algo();

   if(have_block_cipher(cipher_algo))
      {
      cipher.append(get_cipher(
                       cipher_algo + "/CBC/NoPadding",
                       cipher_key, iv, ENCRYPTION)
         );
      pad_amount = block_size_of(cipher_algo);
      }
   else if(have_stream_cipher(cipher_algo))
      {
      cipher.append(get_cipher(cipher_algo, cipher_key, ENCRYPTION));
      pad_amount = 0;
      }
   else
      throw Invalid_Argument("Record_Writer: Unknown cipher " + cipher_algo);

   if(have_hash(mac_algo))
      {
      if(major == 3 && minor == 0)
         mac.append(new MAC_Filter("SSL3-MAC(" + mac_algo + ")", mac_key));
      else
         mac.append(new MAC_Filter("HMAC(" + mac_algo + ")", mac_key));

      mac_size = output_length_of(mac_algo);
      }
   else
      throw Invalid_Argument("Record_Writer: Unknown hash " + mac_algo);
   }

/**
* Send one or more records to the other side
*/
void Record_Writer::send(byte type, byte input)
   {
   send(type, &input, 1);
   }

/**
* Send one or more records to the other side
*/
void Record_Writer::send(byte type, const byte input[], u32bit length)
   {
   if(type != buf_type)
      flush();

   const u32bit BUFFER_SIZE = buffer.size();
   buf_type = type;

   // FIXME: compression right here

   buffer.copy(buf_pos, input, length);
   if(buf_pos + length >= BUFFER_SIZE)
      {
      send_record(buf_type, buffer, length);
      input += (BUFFER_SIZE - buf_pos);
      length -= (BUFFER_SIZE - buf_pos);
      while(length >= BUFFER_SIZE)
         {
         send_record(buf_type, input, BUFFER_SIZE);
         input += BUFFER_SIZE;
         length -= BUFFER_SIZE;
         }
      buffer.copy(input, length);
      buf_pos = 0;
      }
   buf_pos += length;
   }

/**
* Split buffer into records, and send them all
*/
void Record_Writer::flush()
   {
   const byte* buf_ptr = buffer.begin();
   u32bit offset = 0;

   while(offset != buf_pos)
      {
      u32bit record_size = buf_pos - offset;
      if(record_size > MAX_PLAINTEXT_SIZE)
         record_size = MAX_PLAINTEXT_SIZE;

      send_record(buf_type, buf_ptr + offset, record_size);
      offset += record_size;
      }
   buf_type = 0;
   buf_pos = 0;
   }

/**
* Encrypt and send the record
*/
void Record_Writer::send_record(byte type, const byte buf[], u32bit length)
   {
   if(length >= MAX_COMPRESSED_SIZE)
      throw TLS_Exception(INTERNAL_ERROR,
                          "Record_Writer: Compressed packet is too big");

   if(mac_size == 0)
      send_record(type, major, minor, buf, length);
   else
      {
      mac.start_msg();
      for(u32bit j = 0; j != 8; j++)
         mac.write(get_byte(j, seq_no));
      mac.write(type);

      if(major > 3 || (major == 3 && minor != 0))
         {
         mac.write(major);
         mac.write(minor);
         }

      mac.write(get_byte(2, length));
      mac.write(get_byte(3, length));
      mac.write(buf, length);
      mac.end_msg();

      // TODO: This could all use a single buffer

      SecureVector<byte> buf_mac = mac.read_all(Pipe::LAST_MESSAGE);

      cipher.start_msg();
      cipher.write(buf, length);
      cipher.write(buf_mac);
      if(pad_amount)
         {
         u32bit pad_val =
            (pad_amount - (1 + length + buf_mac.size())) % pad_amount;

         for(u32bit j = 0; j != pad_val + 1; j++)
            cipher.write(pad_val);
         }
      cipher.end_msg();

      SecureVector<byte> output = cipher.read_all(Pipe::LAST_MESSAGE);

      send_record(type, major, minor, output, output.size());

      seq_no++;
      }
   }

/**
* Send a final record packet
*/
void Record_Writer::send_record(byte type, byte major, byte minor,
                                const byte out[], u32bit length)
   {
   if(length >= MAX_CIPHERTEXT_SIZE)
      throw TLS_Exception(INTERNAL_ERROR,
                          "Record_Writer: Record is too big");

   byte header[5] = { type, major, minor, 0 };
   for(u32bit j = 0; j != 2; j++)
      header[j+3] = get_byte<u16bit>(j, length);

   socket.write(header, 5);
   socket.write(out, length);
   }

/**
* Send an alert
*/
void Record_Writer::alert(Alert_Level level, Alert_Type type)
   {
   byte alert[2] = { level, type };
   send(ALERT, alert, sizeof(alert));
   flush();
   }

}
<|endoftext|>


<commit_before>#include "ModuleImport.h"
#include "GameObject.h"
#include "ComponentMeshRenderer.h"
#include "Component.h"
#include "Application.h"
#include "ModuleScene.h"
#include "OpenGL.h"
#include "Mesh.h"

#include "Assimp/include/scene.h"
#include "Assimp/include/postprocess.h"
#include "Assimp/include/cfileio.h"
#pragma comment (lib, "Assimp/libx86/assimp.lib")

#include "Devil/include/il.h"
#include "Devil/include/ilut.h"
#pragma comment ( lib, "Devil/libx86/DevIL.lib" )
#pragma comment ( lib, "Devil/libx86/ILU.lib" )
#pragma comment ( lib, "Devil/libx86/ILUT.lib" )

ModuleImport::ModuleImport(Application * app, bool start_enabled) : Module(app, start_enabled)
{
	name = "Importer";
	stream = aiGetPredefinedLogStream(aiDefaultLogStream_DEBUGGER, nullptr);
	aiAttachLogStream(&stream);

	ilInit();
	iluInit();
	ilutInit();
	ilutRenderer(ILUT_OPENGL);

	root_gameobject = nullptr;
}

ModuleImport::~ModuleImport()
{
	ilShutDown();
}

bool ModuleImport::CleanUp()
{
	aiDetachAllLogStreams();
	return true;
}

void ModuleImport::LoadFile(std::string path)
{
	if (path.find(".fbx") != std::string::npos || path.find(".FBX") != std::string::npos)
	{
		LoadMesh(path.c_str());
	}
	else if(path.find(".png") != std::string::npos || path.find(".jpg") != std::string::npos)
	{
		LoadTexture(path.c_str(), true); //<-- Right now, we will use the textures only to attach them to gameobjects (Assigment1)
	}
}

bool ModuleImport::LoadMesh(const char* path)
{
	bool ret = true;

	if (!App->scene->root_gameobjects.empty())
	{
		App->scene->AddGameObjectToDestroy(App->scene->root_gameobjects.front());
	}

	const aiScene* scene = aiImportFile(path, aiProcessPreset_TargetRealtime_MaxQuality);
	if (scene != nullptr && scene->HasMeshes())
	{
		aiNode* root_node = scene->mRootNode;
		if (root_node->mNumChildren > 0)
		{
			root_gameobject = new GameObject();
			root_gameobject->SetName(GetFileName(path).c_str());
			App->scene->AddGameObjectToScene(root_gameobject);
		}
		for (int i = 0; i < root_node->mNumChildren; i++)
		{
			aiNode* node = root_node->mChildren[i];
			for (int j = 0; j < node->mNumMeshes; j++)
			{
				aiMesh* ai_mesh = scene->mMeshes[node->mMeshes[j]];
				Mesh* mesh = new Mesh();
				mesh->name = (std::string)ai_mesh->mName.C_Str();
				mesh->num_vertices = ai_mesh->mNumVertices;
				mesh->vertices = new float[mesh->num_vertices * 3];
				memcpy(mesh->vertices, ai_mesh->mVertices, sizeof(float) * mesh->num_vertices * 3);
				CONSOLE_LOG("New mesh with %d vertices", mesh->num_vertices);

				if (ai_mesh->HasFaces())
				{
					mesh->num_indices = ai_mesh->mNumFaces * 3;
					mesh->indices = new uint[mesh->num_indices]; // assume each face is a triangle
					for (uint k = 0; k < ai_mesh->mNumFaces; ++k)
					{
						if (ai_mesh->mFaces[k].mNumIndices != 3)
						{
							CONSOLE_LOG("WARNING, geometry face with != 3 indices!");
							ret = false;
						}
						else
						{
							memcpy(&mesh->indices[k * 3], ai_mesh->mFaces[k].mIndices, 3 * sizeof(uint));
						}
					}
				}

				if (ai_mesh->HasNormals())
				{
					mesh->normals = new float[mesh->num_vertices * 3];
					memcpy(mesh->normals, ai_mesh->mNormals, sizeof(float) * mesh->num_vertices * 3);
				}

				if (ai_mesh->HasVertexColors(0))
				{
					mesh->colors = new float[mesh->num_vertices * 3];
					memcpy(mesh->colors, ai_mesh->mColors, sizeof(float) * mesh->num_vertices * 3);
				}

				if (ai_mesh->HasTextureCoords(0))
				{
					mesh->texture_coords = new float[mesh->num_vertices * 3];
					memcpy(mesh->texture_coords, ai_mesh->mTextureCoords[0], sizeof(float) * mesh->num_vertices * 3);
				}

				glGenBuffers(1, &mesh->id_vertices);
				glBindBuffer(GL_ARRAY_BUFFER, mesh->id_vertices);
				glBufferData(GL_ARRAY_BUFFER, sizeof(float)*mesh->num_vertices * 3, mesh->vertices, GL_STATIC_DRAW);
				glBindBuffer(GL_ARRAY_BUFFER, 0);

				glGenBuffers(1, &mesh->id_indices);
				glBindBuffer(GL_ARRAY_BUFFER, mesh->id_indices);
				glBufferData(GL_ARRAY_BUFFER, sizeof(uint)*mesh->num_indices, mesh->indices, GL_STATIC_DRAW);
				glBindBuffer(GL_ARRAY_BUFFER, 0);

				if (mesh->normals != nullptr)
				{
					glGenBuffers(1, &(mesh->id_normals));
					glBindBuffer(GL_ARRAY_BUFFER, mesh->id_normals);
					glBufferData(GL_ARRAY_BUFFER, sizeof(float) * mesh->num_vertices * 3, mesh->normals, GL_STATIC_DRAW);
					glBindBuffer(GL_ARRAY_BUFFER, 0);
				}

				if (mesh->colors != nullptr)
				{
					glGenBuffers(1, &(mesh->id_colors));
					glBindBuffer(GL_ARRAY_BUFFER, mesh->id_colors);
					glBufferData(GL_ARRAY_BUFFER, sizeof(float) * mesh->num_vertices * 3, mesh->colors, GL_STATIC_DRAW);
					glBindBuffer(GL_ARRAY_BUFFER, 0);
				}

				if (mesh->texture_coords != nullptr)
				{
					glGenBuffers(1, &(mesh->id_texture_coords));
					glBindBuffer(GL_ARRAY_BUFFER, mesh->id_texture_coords);
					glBufferData(GL_ARRAY_BUFFER, sizeof(float) * mesh->num_vertices * 3, mesh->texture_coords, GL_STATIC_DRAW);
					glBindBuffer(GL_ARRAY_BUFFER, 0);
				}

				GameObject* go = new GameObject(root_gameobject);
				ComponentMeshRenderer* mesh_renderer = (ComponentMeshRenderer*)go->AddComponent(Component::MeshRenderer);
				mesh_renderer->LoadMesh(mesh);
				go->SetName((std::string)node->mName.C_Str());
				App->scene->AddGameObjectToScene(go);
			}
		}

		aiReleaseImport(scene);
	}
	else
		CONSOLE_LOG("Error loading scene %s", path);

	return ret;
}

Texture* ModuleImport::LoadTexture(const char * path, bool attach_to_gameobject)
{
	//If gameobjects are not selected, don't load a texture. This is just for the assigment 1.
	//In the future if we drag a image without selected gameobjects, a texture should be created and stored in assets/library
	if (App->scene->selected_gameobjects.empty() && attach_to_gameobject) return nullptr;

	ILuint image_id;
	ilGenImages(1, &image_id);
	ilBindImage(image_id);

	Texture* tmp_texture = nullptr;

	if (ilLoadImage(path))
	{
		ILinfo ImageInfo;
		iluGetImageInfo(&ImageInfo);

		tmp_texture = new Texture();
		tmp_texture->SetID(ilutGLBindTexImage());
		tmp_texture->SetWidth(ImageInfo.Width);
		tmp_texture->SetHeight(ImageInfo.Height);
		tmp_texture->SetPath(path);
		tmp_texture->SetName(GetFileName(path).c_str());

		switch (ImageInfo.Format)
		{
			case IL_COLOUR_INDEX: tmp_texture->SetFormat(Texture::ColorIndex); break;
			case IL_ALPHA: tmp_texture->SetFormat(Texture::alpha); break;
			case IL_RGB: tmp_texture->SetFormat(Texture::rgb); break;
			case IL_RGBA: tmp_texture->SetFormat(Texture::rgba); break;
			case IL_BGR: tmp_texture->SetFormat(Texture::bgr); break;
			case IL_BGRA: tmp_texture->SetFormat(Texture::bgra); break;
			case IL_LUMINANCE: tmp_texture->SetFormat(Texture::luminance); break;
			case IL_LUMINANCE_ALPHA: tmp_texture->SetFormat(Texture::luminance_alpha); break;
			default: tmp_texture->SetFormat(Texture::UnknownFormat); break;
		}
		switch (ImageInfo.Type)
		{
			case IL_BMP: tmp_texture->SetType(Texture::TextureType::bmp); break;
			case IL_JPG: tmp_texture->SetType(Texture::TextureType::jpg); break;
			case IL_PNG: tmp_texture->SetType(Texture::TextureType::png); break;
			case IL_TGA: tmp_texture->SetType(Texture::TextureType::tga); break;
			case IL_DDS: tmp_texture->SetType(Texture::TextureType::dds); break;
			default: tmp_texture->SetType(Texture::TextureType::UnknownType); break;
		}
		ilDeleteImages(1, &image_id);
	}
	else
	{
		CONSOLE_LOG("Cannot load image %s. Error: %s", path, iluErrorString(ilGetError()));
	}

	if (attach_to_gameobject) App->scene->ApplyTextureToSelectedGameObjects(tmp_texture);

	return tmp_texture;
}

std::string ModuleImport::GetFileName(const char * path)
{
	std::string name;
	std::string temp_path;
	temp_path = path;
	uint pos_bar = temp_path.find_last_of('\\');
	uint pos_point = temp_path.find_last_of('.');
	for (int i = pos_bar + 1; i < pos_point; i++)
	{
		name.push_back(temp_path[i]);
	}
	return name;
}

<commit_msg>When a FBX file or texture is imported, information will be displayed on the console<commit_after>#include "ModuleImport.h"
#include "GameObject.h"
#include "ComponentMeshRenderer.h"
#include "Component.h"
#include "Application.h"
#include "ModuleScene.h"
#include "OpenGL.h"
#include "Mesh.h"
#include "ConsoleWindow.h"

#include "Assimp/include/scene.h"
#include "Assimp/include/postprocess.h"
#include "Assimp/include/cfileio.h"
#pragma comment (lib, "Assimp/libx86/assimp.lib")

#include "Devil/include/il.h"
#include "Devil/include/ilut.h"
#pragma comment ( lib, "Devil/libx86/DevIL.lib" )
#pragma comment ( lib, "Devil/libx86/ILU.lib" )
#pragma comment ( lib, "Devil/libx86/ILUT.lib" )

ModuleImport::ModuleImport(Application * app, bool start_enabled) : Module(app, start_enabled)
{
	name = "Importer";
	stream = aiGetPredefinedLogStream(aiDefaultLogStream_DEBUGGER, nullptr);
	aiAttachLogStream(&stream);

	ilInit();
	iluInit();
	ilutInit();
	ilutRenderer(ILUT_OPENGL);

	root_gameobject = nullptr;
}

ModuleImport::~ModuleImport()
{
	ilShutDown();
}

bool ModuleImport::CleanUp()
{
	aiDetachAllLogStreams();
	return true;
}

void ModuleImport::LoadFile(std::string path)
{
	if (path.find(".fbx") != std::string::npos || path.find(".FBX") != std::string::npos)
	{
		LoadMesh(path.c_str());
	}
	else if(path.find(".png") != std::string::npos || path.find(".jpg") != std::string::npos)
	{
		LoadTexture(path.c_str(), true); //<-- Right now, we will use the textures only to attach them to gameobjects (Assigment1)
	}
}

bool ModuleImport::LoadMesh(const char* path)
{
	bool ret = true;

	if (!App->scene->root_gameobjects.empty())
	{
		App->scene->AddGameObjectToDestroy(App->scene->root_gameobjects.front());
	}

	const aiScene* scene = aiImportFile(path, aiProcessPreset_TargetRealtime_MaxQuality);
	if (scene != nullptr && scene->HasMeshes())
	{
		aiNode* root_node = scene->mRootNode;
		if (root_node->mNumChildren > 0)
		{
			root_gameobject = new GameObject();
			root_gameobject->SetName(GetFileName(path).c_str());
			App->scene->AddGameObjectToScene(root_gameobject);
		}
		for (int i = 0; i < root_node->mNumChildren; i++)
		{
			aiNode* node = root_node->mChildren[i];
			for (int j = 0; j < node->mNumMeshes; j++)
			{
				aiMesh* ai_mesh = scene->mMeshes[node->mMeshes[j]];
				Mesh* mesh = new Mesh();
				mesh->name = (std::string)ai_mesh->mName.C_Str();
				mesh->num_vertices = ai_mesh->mNumVertices;
				mesh->vertices = new float[mesh->num_vertices * 3];
				memcpy(mesh->vertices, ai_mesh->mVertices, sizeof(float) * mesh->num_vertices * 3);
				CONSOLE_LOG("New mesh with %d vertices", mesh->num_vertices);
				App->editor->console_window->AddLog(std::string("New mesh with " + std::to_string(mesh->num_vertices) + " vertices. (" + node->mName.C_Str() + ")"));
				if (ai_mesh->HasFaces())
				{
					mesh->num_indices = ai_mesh->mNumFaces * 3;
					mesh->indices = new uint[mesh->num_indices]; // assume each face is a triangle
					for (uint k = 0; k < ai_mesh->mNumFaces; ++k)
					{
						if (ai_mesh->mFaces[k].mNumIndices != 3)
						{
							CONSOLE_LOG("WARNING, geometry face with != 3 indices!");
							App->editor->console_window->AddLog(std::string("WARNING, geometry face with != 3 indices!"));
							ret = false;
						}
						else
						{
							memcpy(&mesh->indices[k * 3], ai_mesh->mFaces[k].mIndices, 3 * sizeof(uint));
						}
					}
				}

				if (ai_mesh->HasNormals())
				{
					mesh->normals = new float[mesh->num_vertices * 3];
					memcpy(mesh->normals, ai_mesh->mNormals, sizeof(float) * mesh->num_vertices * 3);
					App->editor->console_window->AddLog(std::string("Mesh has normals (" + std::string(node->mName.C_Str()) + ")"));
				}

				if (ai_mesh->HasVertexColors(0))
				{
					mesh->colors = new float[mesh->num_vertices * 3];
					memcpy(mesh->colors, ai_mesh->mColors, sizeof(float) * mesh->num_vertices * 3);
					App->editor->console_window->AddLog(std::string("Mesh has Vertex Colors (" + std::string(node->mName.C_Str()) + ")"));
				}

				if (ai_mesh->HasTextureCoords(0))
				{
					mesh->texture_coords = new float[mesh->num_vertices * 3];
					memcpy(mesh->texture_coords, ai_mesh->mTextureCoords[0], sizeof(float) * mesh->num_vertices * 3);
					App->editor->console_window->AddLog(std::string("Has Texture Coords (" + std::string(node->mName.C_Str()) + ")"));
				}

				glGenBuffers(1, &mesh->id_vertices);
				glBindBuffer(GL_ARRAY_BUFFER, mesh->id_vertices);
				glBufferData(GL_ARRAY_BUFFER, sizeof(float)*mesh->num_vertices * 3, mesh->vertices, GL_STATIC_DRAW);
				glBindBuffer(GL_ARRAY_BUFFER, 0);

				glGenBuffers(1, &mesh->id_indices);
				glBindBuffer(GL_ARRAY_BUFFER, mesh->id_indices);
				glBufferData(GL_ARRAY_BUFFER, sizeof(uint)*mesh->num_indices, mesh->indices, GL_STATIC_DRAW);
				glBindBuffer(GL_ARRAY_BUFFER, 0);

				if (mesh->normals != nullptr)
				{
					glGenBuffers(1, &(mesh->id_normals));
					glBindBuffer(GL_ARRAY_BUFFER, mesh->id_normals);
					glBufferData(GL_ARRAY_BUFFER, sizeof(float) * mesh->num_vertices * 3, mesh->normals, GL_STATIC_DRAW);
					glBindBuffer(GL_ARRAY_BUFFER, 0);
				}

				if (mesh->colors != nullptr)
				{
					glGenBuffers(1, &(mesh->id_colors));
					glBindBuffer(GL_ARRAY_BUFFER, mesh->id_colors);
					glBufferData(GL_ARRAY_BUFFER, sizeof(float) * mesh->num_vertices * 3, mesh->colors, GL_STATIC_DRAW);
					glBindBuffer(GL_ARRAY_BUFFER, 0);
				}

				if (mesh->texture_coords != nullptr)
				{
					glGenBuffers(1, &(mesh->id_texture_coords));
					glBindBuffer(GL_ARRAY_BUFFER, mesh->id_texture_coords);
					glBufferData(GL_ARRAY_BUFFER, sizeof(float) * mesh->num_vertices * 3, mesh->texture_coords, GL_STATIC_DRAW);
					glBindBuffer(GL_ARRAY_BUFFER, 0);
				}

				GameObject* go = new GameObject(root_gameobject);
				ComponentMeshRenderer* mesh_renderer = (ComponentMeshRenderer*)go->AddComponent(Component::MeshRenderer);
				mesh_renderer->LoadMesh(mesh);
				go->SetName((std::string)node->mName.C_Str());
				App->scene->AddGameObjectToScene(go);
			}
		}

		aiReleaseImport(scene);
		App->editor->console_window->AddLog(std::string("Object succesfully loaded from" + std::string(path)));
	}
	else
	{
		CONSOLE_LOG("Error loading object %s", path);
		App->editor->console_window->AddLog(std::string("Error loading object" + std::string(path)), true, false);
	}

	return ret;
}

Texture* ModuleImport::LoadTexture(const char * path, bool attach_to_gameobject)
{
	//If gameobjects are not selected, don't load a texture. This is just for the assigment 1.
	//In the future if we drag a image without selected gameobjects, a texture should be created and stored in assets/library
	if (App->scene->selected_gameobjects.empty() && attach_to_gameobject) return nullptr;

	ILuint image_id;
	ilGenImages(1, &image_id);
	ilBindImage(image_id);

	Texture* tmp_texture = nullptr;

	if (ilLoadImage(path))
	{
		ILinfo ImageInfo;
		iluGetImageInfo(&ImageInfo);

		tmp_texture = new Texture();
		tmp_texture->SetID(ilutGLBindTexImage());
		tmp_texture->SetWidth(ImageInfo.Width);
		tmp_texture->SetHeight(ImageInfo.Height);
		tmp_texture->SetPath(path);
		tmp_texture->SetName(GetFileName(path).c_str());

		switch (ImageInfo.Format)
		{
			case IL_COLOUR_INDEX: tmp_texture->SetFormat(Texture::ColorIndex); break;
			case IL_ALPHA: tmp_texture->SetFormat(Texture::alpha); break;
			case IL_RGB: tmp_texture->SetFormat(Texture::rgb); break;
			case IL_RGBA: tmp_texture->SetFormat(Texture::rgba); break;
			case IL_BGR: tmp_texture->SetFormat(Texture::bgr); break;
			case IL_BGRA: tmp_texture->SetFormat(Texture::bgra); break;
			case IL_LUMINANCE: tmp_texture->SetFormat(Texture::luminance); break;
			case IL_LUMINANCE_ALPHA: tmp_texture->SetFormat(Texture::luminance_alpha); break;
			default: tmp_texture->SetFormat(Texture::UnknownFormat); break;
		}
		switch (ImageInfo.Type)
		{
			case IL_BMP: tmp_texture->SetType(Texture::TextureType::bmp); break;
			case IL_JPG: tmp_texture->SetType(Texture::TextureType::jpg); break;
			case IL_PNG: tmp_texture->SetType(Texture::TextureType::png); break;
			case IL_TGA: tmp_texture->SetType(Texture::TextureType::tga); break;
			case IL_DDS: tmp_texture->SetType(Texture::TextureType::dds); break;
			default: tmp_texture->SetType(Texture::TextureType::UnknownType); break;
		}
		ilDeleteImages(1, &image_id);
		App->editor->console_window->AddLog(std::string("Texture Loaded " + std::string(path)));
	}
	else
	{
		CONSOLE_LOG("Cannot load image %s. Error: %s", path, iluErrorString(ilGetError()));
		App->editor->console_window->AddLog(std::string("Cannot load image " + std::string(path) + " Error number: " + iluErrorString(ilGetError())), true, false);
	}

	if (attach_to_gameobject) App->scene->ApplyTextureToSelectedGameObjects(tmp_texture);

	return tmp_texture;
}

std::string ModuleImport::GetFileName(const char * path)
{
	std::string name;
	std::string temp_path;
	temp_path = path;
	uint pos_bar = temp_path.find_last_of('\\');
	uint pos_point = temp_path.find_last_of('.');
	for (int i = pos_bar + 1; i < pos_point; i++)
	{
		name.push_back(temp_path[i]);
	}
	return name;
}

<|endoftext|>


<commit_before>//  Copyright (c) 2007-2008 Facebook
//
//  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.
//
// See accompanying file LICENSE or visit the Scribe site at:
// http://developers.facebook.com/scribe/
//
// @author Bobby Johnson
// @author James Wang
// @author Jason Sobel
// @author Anthony Giardullo

#include "common.h"
#include "scribe_server.h"

using namespace std;
using namespace boost;
using namespace scribe::thrift;

#define DEFAULT_TARGET_WRITE_SIZE  16384
#define DEFAULT_MAX_WRITE_INTERVAL 10

void* threadStatic(void *this_ptr) {
  StoreQueue *queue_ptr = (StoreQueue*)this_ptr;
  queue_ptr->threadMember();
  return NULL;
}

StoreQueue::StoreQueue(const string& type, const string& category,
                       unsigned check_period, bool is_model, bool multi_category)
  : msgQueueSize(0),
    hasWork(false),
    stopping(false),
    isModel(is_model),
    multiCategory(multi_category),
    categoryHandled(category),
    checkPeriod(check_period),
    targetWriteSize(DEFAULT_TARGET_WRITE_SIZE),
    maxWriteInterval(DEFAULT_MAX_WRITE_INTERVAL),
    mustSucceed(true) {

  store = Store::createStore(type, category, false, multiCategory);
  if (!store) {
    throw std::runtime_error("createStore failed in StoreQueue constructor. Invalid type?");
  }
  storeInitCommon();
}

StoreQueue::StoreQueue(const shared_ptr<StoreQueue> example,
                       const std::string &category)
  : msgQueueSize(0),
    hasWork(false),
    stopping(false),
    isModel(false),
    multiCategory(example->multiCategory),
    categoryHandled(category),
    checkPeriod(example->checkPeriod),
    targetWriteSize(example->targetWriteSize),
    maxWriteInterval(example->maxWriteInterval),
    mustSucceed(example->mustSucceed) {

  store = example->copyStore(category);
  if (!store) {
    throw std::runtime_error("createStore failed copying model store");
  }
  storeInitCommon();
}

StoreQueue::~StoreQueue() {
  if (!isModel) {
    pthread_mutex_destroy(&cmdMutex);
    pthread_mutex_destroy(&msgMutex);
    pthread_mutex_destroy(&hasWorkMutex);
    pthread_cond_destroy(&hasWorkCond);
  }
}

// WARNING: the number could change after you check this, so don't
// expect it to be exact. Use for hueristics ONLY.
unsigned long StoreQueue::getSize() {
  unsigned long retval;
  pthread_mutex_lock(&msgMutex);
  retval = msgQueueSize;
  pthread_mutex_unlock(&msgMutex);
  return retval;
}

void StoreQueue::addMessage(boost::shared_ptr<LogEntry> entry) {
  if (isModel) {
    LOG_OPER("ERROR: called addMessage on model store");
  } else {
    pthread_mutex_lock(&msgMutex);
    msgQueue->push_back(entry);
    msgQueueSize += entry->message.size();
    pthread_mutex_unlock(&msgMutex);

    // Wake up store thread if we have enough messages
    if (msgQueueSize >= targetWriteSize) {
      // signal that there is work to do if not already signaled
      if (!hasWork) {
        pthread_mutex_lock(&hasWorkMutex);
        hasWork = true;
        pthread_cond_signal(&hasWorkCond);
        pthread_mutex_unlock(&hasWorkMutex);
      }
    }
  }
}

void StoreQueue::configureAndOpen(pStoreConf configuration) {
  // model store has to handle this inline since it has no queue
  if (isModel) {
    configureInline(configuration);
  } else {
    pthread_mutex_lock(&cmdMutex);
    StoreCommand cmd(CMD_CONFIGURE, configuration);
    cmdQueue.push(cmd);
    pthread_mutex_unlock(&cmdMutex);

    // signal that there is work to do if not already signaled
    if (!hasWork) {
      pthread_mutex_lock(&hasWorkMutex);
      hasWork = true;
      pthread_cond_signal(&hasWorkCond);
      pthread_mutex_unlock(&hasWorkMutex);
    }
  }
}

void StoreQueue::stop() {
  if (isModel) {
    LOG_OPER("ERROR: called stop() on model store");
  } else if(!stopping) {
    pthread_mutex_lock(&cmdMutex);
    StoreCommand cmd(CMD_STOP);
    cmdQueue.push(cmd);
    stopping = true;
    pthread_mutex_unlock(&cmdMutex);

    // signal that there is work to do if not already signaled
    if (!hasWork) {
      pthread_mutex_lock(&hasWorkMutex);
      hasWork = true;
      pthread_cond_signal(&hasWorkCond);
      pthread_mutex_unlock(&hasWorkMutex);
    }

    pthread_join(storeThread, NULL);
  }
}

void StoreQueue::open() {
  if (isModel) {
    LOG_OPER("ERROR: called open() on model store");
  } else {
    pthread_mutex_lock(&cmdMutex);
    StoreCommand cmd(CMD_OPEN);
    cmdQueue.push(cmd);
    pthread_mutex_unlock(&cmdMutex);

    // signal that there is work to do if not already signaled
    if (!hasWork) {
      pthread_mutex_lock(&hasWorkMutex);
      hasWork = true;
      pthread_cond_signal(&hasWorkCond);
      pthread_mutex_unlock(&hasWorkMutex);
    }
  }
}

shared_ptr<Store> StoreQueue::copyStore(const std::string &category) {
  return store->copy(category);
}

std::string StoreQueue::getCategoryHandled() {
  return categoryHandled;
}

std::string StoreQueue::getStatus() {
  return store->getStatus();
}

std::string StoreQueue::getBaseType() {
  return store->getType();
}

void StoreQueue::threadMember() {
  LOG_OPER("store thread starting");

  if (isModel) {
    LOG_OPER("ERROR: store thread starting on model store, exiting");
    return;
  }

  if (!store) {
    LOG_OPER("store is NULL, store thread exiting");
    return;
  }

  // init time of last periodic check to time of 0
  time_t last_periodic_check = 0;

  time_t last_handle_messages;
  time(&last_handle_messages);

  // initialize absolute timestamp
  struct timespec abs_timeout;
  memset(&abs_timeout, 0, sizeof(struct timespec));

  bool stop = false;
  bool open = false;
  while (!stop) {

    // handle commands
    //
    pthread_mutex_lock(&cmdMutex);
    while (!cmdQueue.empty()) {
      StoreCommand cmd = cmdQueue.front();
      cmdQueue.pop();

      switch (cmd.command) {
      case CMD_CONFIGURE:
        configureInline(cmd.configuration);
        openInline();
        open = true;
        break;
      case CMD_OPEN:
        openInline();
        open = true;
        break;
      case CMD_STOP:
        stop = true;
        break;
      default:
        LOG_OPER("LOGIC ERROR: unknown command to store queue");
        break;
      }
    }

    // handle periodic tasks
    //
    time_t this_loop;
    time(&this_loop);
    if (!stop && open && this_loop - last_periodic_check > checkPeriod) {
      store->periodicCheck();
      last_periodic_check = this_loop;
    }

    pthread_mutex_lock(&msgMutex);
    pthread_mutex_unlock(&cmdMutex);

    boost::shared_ptr<logentry_vector_t> messages;

    // handle messages if stopping, enough time has passed, or queue is large
    //
    if (stop ||
        (this_loop - last_handle_messages > maxWriteInterval) ||
        msgQueueSize >= targetWriteSize) {

      if (failedMessages) {
        // process any messages we were not able to process last time
        messages = failedMessages;
        failedMessages = boost::shared_ptr<logentry_vector_t>();
      } else if (msgQueueSize > 0) {
        // process message in queue
        messages = msgQueue;
        msgQueue = boost::shared_ptr<logentry_vector_t>(new logentry_vector_t);
        msgQueueSize = 0;
      }

      // reset timer
      last_handle_messages = this_loop;
    }

    pthread_mutex_unlock(&msgMutex);

    if (messages) {
      if (!store->handleMessages(messages)) {
        // Store could not handle these messages
        processFailedMessages(messages);
      }
      store->flush();
    }

    if (!stop) {
      // set timeout to when we need to handle messages or do a periodic check
      abs_timeout.tv_sec = min(last_periodic_check + checkPeriod,
                               last_handle_messages + maxWriteInterval);

      // must wait until after this time
      abs_timeout.tv_sec++;

      // wait until there's some work to do or we timeout
      pthread_mutex_lock(&hasWorkMutex);
      if (!hasWork) {
        pthread_cond_timedwait(&hasWorkCond, &hasWorkMutex, &abs_timeout);
      }
      hasWork = false;
      pthread_mutex_unlock(&hasWorkMutex);
    }

  } // while (!stop)

  store->close();
}

void StoreQueue::processFailedMessages(shared_ptr<logentry_vector_t> messages) {
  // If the store was not able to process these messages, we will either
  // requeue them or give up depending on the value of mustSucceed

  if (mustSucceed) {
    // Save failed messages
    failedMessages = messages;

    LOG_OPER("[%s] WARNING: Re-queueing %lu messages!",
             categoryHandled.c_str(), messages->size());
    g_Handler->incrementCounter("requeue", messages->size());
  } else {
    // record messages as being lost
    LOG_OPER("[%s] WARNING: Lost %lu messages!",
             categoryHandled.c_str(), messages->size());
    g_Handler->incrementCounter("lost", messages->size());
  }
}

void StoreQueue::storeInitCommon() {
  // model store doesn't need this stuff
  if (!isModel) {
    msgQueue = boost::shared_ptr<logentry_vector_t>(new logentry_vector_t);
    pthread_mutex_init(&cmdMutex, NULL);
    pthread_mutex_init(&msgMutex, NULL);
    pthread_mutex_init(&hasWorkMutex, NULL);
    pthread_cond_init(&hasWorkCond, NULL);

    pthread_create(&storeThread, NULL, threadStatic, (void*) this);
  }
}

void StoreQueue::configureInline(pStoreConf configuration) {
  // Constructor defaults are fine if these don't exist
  configuration->getUnsigned("target_write_size", (unsigned long&) targetWriteSize);
  configuration->getUnsigned("max_write_interval", (unsigned long&) maxWriteInterval);

  string tmp;
  if (configuration->getString("must_succeed", tmp) && tmp == "no") {
    mustSucceed = false;
  }

  store->configure(configuration);
}

void StoreQueue::openInline() {
  if (store->isOpen()) {
    store->close();
  }
  if (!isModel) {
    store->open();
  }
}
<commit_msg>fix minor race conditions<commit_after>//  Copyright (c) 2007-2008 Facebook
//
//  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.
//
// See accompanying file LICENSE or visit the Scribe site at:
// http://developers.facebook.com/scribe/
//
// @author Bobby Johnson
// @author James Wang
// @author Jason Sobel
// @author Anthony Giardullo

#include "common.h"
#include "scribe_server.h"

using namespace std;
using namespace boost;
using namespace scribe::thrift;

#define DEFAULT_TARGET_WRITE_SIZE  16384
#define DEFAULT_MAX_WRITE_INTERVAL 10

void* threadStatic(void *this_ptr) {
  StoreQueue *queue_ptr = (StoreQueue*)this_ptr;
  queue_ptr->threadMember();
  return NULL;
}

StoreQueue::StoreQueue(const string& type, const string& category,
                       unsigned check_period, bool is_model, bool multi_category)
  : msgQueueSize(0),
    hasWork(false),
    stopping(false),
    isModel(is_model),
    multiCategory(multi_category),
    categoryHandled(category),
    checkPeriod(check_period),
    targetWriteSize(DEFAULT_TARGET_WRITE_SIZE),
    maxWriteInterval(DEFAULT_MAX_WRITE_INTERVAL),
    mustSucceed(true) {

  store = Store::createStore(type, category, false, multiCategory);
  if (!store) {
    throw std::runtime_error("createStore failed in StoreQueue constructor. Invalid type?");
  }
  storeInitCommon();
}

StoreQueue::StoreQueue(const shared_ptr<StoreQueue> example,
                       const std::string &category)
  : msgQueueSize(0),
    hasWork(false),
    stopping(false),
    isModel(false),
    multiCategory(example->multiCategory),
    categoryHandled(category),
    checkPeriod(example->checkPeriod),
    targetWriteSize(example->targetWriteSize),
    maxWriteInterval(example->maxWriteInterval),
    mustSucceed(example->mustSucceed) {

  store = example->copyStore(category);
  if (!store) {
    throw std::runtime_error("createStore failed copying model store");
  }
  storeInitCommon();
}

StoreQueue::~StoreQueue() {
  if (!isModel) {
    pthread_mutex_destroy(&cmdMutex);
    pthread_mutex_destroy(&msgMutex);
    pthread_mutex_destroy(&hasWorkMutex);
    pthread_cond_destroy(&hasWorkCond);
  }
}

// WARNING: the number could change after you check this, so don't
// expect it to be exact. Use for hueristics ONLY.
unsigned long StoreQueue::getSize() {
  unsigned long retval;
  pthread_mutex_lock(&msgMutex);
  retval = msgQueueSize;
  pthread_mutex_unlock(&msgMutex);
  return retval;
}

void StoreQueue::addMessage(boost::shared_ptr<LogEntry> entry) {
  if (isModel) {
    LOG_OPER("ERROR: called addMessage on model store");
  } else {
    bool waitForWork = false;

    pthread_mutex_lock(&msgMutex);
    msgQueue->push_back(entry);
    msgQueueSize += entry->message.size();

    waitForWork = (msgQueueSize >= targetWriteSize) ? true : false;
    pthread_mutex_unlock(&msgMutex);

    // Wake up store thread if we have enough messages
    if (waitForWork == true) {
      // signal that there is work to do if not already signaled
      pthread_mutex_lock(&hasWorkMutex);
      if (!hasWork) {
        hasWork = true;
        pthread_cond_signal(&hasWorkCond);
      }
      pthread_mutex_unlock(&hasWorkMutex);
    }
  }
}

void StoreQueue::configureAndOpen(pStoreConf configuration) {
  // model store has to handle this inline since it has no queue
  if (isModel) {
    configureInline(configuration);
  } else {
    pthread_mutex_lock(&cmdMutex);
    StoreCommand cmd(CMD_CONFIGURE, configuration);
    cmdQueue.push(cmd);
    pthread_mutex_unlock(&cmdMutex);

    // signal that there is work to do if not already signaled
    pthread_mutex_lock(&hasWorkMutex);
    if (!hasWork) {
      hasWork = true;
      pthread_cond_signal(&hasWorkCond);
    }
    pthread_mutex_unlock(&hasWorkMutex);
  }
}

void StoreQueue::stop() {
  if (isModel) {
    LOG_OPER("ERROR: called stop() on model store");
  } else if(!stopping) {
    pthread_mutex_lock(&cmdMutex);
    StoreCommand cmd(CMD_STOP);
    cmdQueue.push(cmd);
    stopping = true;
    pthread_mutex_unlock(&cmdMutex);

    // signal that there is work to do if not already signaled
    pthread_mutex_lock(&hasWorkMutex);
    if (!hasWork) {
      hasWork = true;
      pthread_cond_signal(&hasWorkCond);
    }
    pthread_mutex_unlock(&hasWorkMutex);

    pthread_join(storeThread, NULL);
  }
}

void StoreQueue::open() {
  if (isModel) {
    LOG_OPER("ERROR: called open() on model store");
  } else {
    pthread_mutex_lock(&cmdMutex);
    StoreCommand cmd(CMD_OPEN);
    cmdQueue.push(cmd);
    pthread_mutex_unlock(&cmdMutex);

    // signal that there is work to do if not already signaled
    pthread_mutex_lock(&hasWorkMutex);
    if (!hasWork) {
      hasWork = true;
      pthread_cond_signal(&hasWorkCond);
    }
    pthread_mutex_unlock(&hasWorkMutex);
  }
}

shared_ptr<Store> StoreQueue::copyStore(const std::string &category) {
  return store->copy(category);
}

std::string StoreQueue::getCategoryHandled() {
  return categoryHandled;
}

std::string StoreQueue::getStatus() {
  return store->getStatus();
}

std::string StoreQueue::getBaseType() {
  return store->getType();
}

void StoreQueue::threadMember() {
  LOG_OPER("store thread starting");

  if (isModel) {
    LOG_OPER("ERROR: store thread starting on model store, exiting");
    return;
  }

  if (!store) {
    LOG_OPER("store is NULL, store thread exiting");
    return;
  }

  // init time of last periodic check to time of 0
  time_t last_periodic_check = 0;

  time_t last_handle_messages;
  time(&last_handle_messages);

  // initialize absolute timestamp
  struct timespec abs_timeout;
  memset(&abs_timeout, 0, sizeof(struct timespec));

  bool stop = false;
  bool open = false;
  while (!stop) {

    // handle commands
    //
    pthread_mutex_lock(&cmdMutex);
    while (!cmdQueue.empty()) {
      StoreCommand cmd = cmdQueue.front();
      cmdQueue.pop();

      switch (cmd.command) {
      case CMD_CONFIGURE:
        configureInline(cmd.configuration);
        openInline();
        open = true;
        break;
      case CMD_OPEN:
        openInline();
        open = true;
        break;
      case CMD_STOP:
        stop = true;
        break;
      default:
        LOG_OPER("LOGIC ERROR: unknown command to store queue");
        break;
      }
    }

    // handle periodic tasks
    //
    time_t this_loop;
    time(&this_loop);
    if (!stop && open && this_loop - last_periodic_check > checkPeriod) {
      store->periodicCheck();
      last_periodic_check = this_loop;
    }

    pthread_mutex_lock(&msgMutex);
    pthread_mutex_unlock(&cmdMutex);

    boost::shared_ptr<logentry_vector_t> messages;

    // handle messages if stopping, enough time has passed, or queue is large
    //
    if (stop ||
        (this_loop - last_handle_messages > maxWriteInterval) ||
        msgQueueSize >= targetWriteSize) {

      if (failedMessages) {
        // process any messages we were not able to process last time
        messages = failedMessages;
        failedMessages = boost::shared_ptr<logentry_vector_t>();
      } else if (msgQueueSize > 0) {
        // process message in queue
        messages = msgQueue;
        msgQueue = boost::shared_ptr<logentry_vector_t>(new logentry_vector_t);
        msgQueueSize = 0;
      }

      // reset timer
      last_handle_messages = this_loop;
    }

    pthread_mutex_unlock(&msgMutex);

    if (messages) {
      if (!store->handleMessages(messages)) {
        // Store could not handle these messages
        processFailedMessages(messages);
      }
      store->flush();
    }

    if (!stop) {
      // set timeout to when we need to handle messages or do a periodic check
      abs_timeout.tv_sec = min(last_periodic_check + checkPeriod,
                               last_handle_messages + maxWriteInterval);

      // must wait until after this time
      abs_timeout.tv_sec++;

      // wait until there's some work to do or we timeout
      pthread_mutex_lock(&hasWorkMutex);
      if (!hasWork) {
	pthread_cond_timedwait(&hasWorkCond, &hasWorkMutex, &abs_timeout);
      }
      hasWork = false;
      pthread_mutex_unlock(&hasWorkMutex);
    }

  } // while (!stop)

  store->close();
}

void StoreQueue::processFailedMessages(shared_ptr<logentry_vector_t> messages) {
  // If the store was not able to process these messages, we will either
  // requeue them or give up depending on the value of mustSucceed

  if (mustSucceed) {
    // Save failed messages
    failedMessages = messages;

    LOG_OPER("[%s] WARNING: Re-queueing %lu messages!",
             categoryHandled.c_str(), messages->size());
    g_Handler->incrementCounter("requeue", messages->size());
  } else {
    // record messages as being lost
    LOG_OPER("[%s] WARNING: Lost %lu messages!",
             categoryHandled.c_str(), messages->size());
    g_Handler->incrementCounter("lost", messages->size());
  }
}

void StoreQueue::storeInitCommon() {
  // model store doesn't need this stuff
  if (!isModel) {
    msgQueue = boost::shared_ptr<logentry_vector_t>(new logentry_vector_t);
    pthread_mutex_init(&cmdMutex, NULL);
    pthread_mutex_init(&msgMutex, NULL);
    pthread_mutex_init(&hasWorkMutex, NULL);
    pthread_cond_init(&hasWorkCond, NULL);

    pthread_create(&storeThread, NULL, threadStatic, (void*) this);
  }
}

void StoreQueue::configureInline(pStoreConf configuration) {
  // Constructor defaults are fine if these don't exist
  configuration->getUnsigned("target_write_size", (unsigned long&) targetWriteSize);
  configuration->getUnsigned("max_write_interval", (unsigned long&) maxWriteInterval);

  string tmp;
  if (configuration->getString("must_succeed", tmp) && tmp == "no") {
    mustSucceed = false;
  }

  store->configure(configuration);
}

void StoreQueue::openInline() {
  if (store->isOpen()) {
    store->close();
  }
  if (!isModel) {
    store->open();
  }
}
<|endoftext|>


<commit_before>#include <tests/Base.hh>

#include <aleph/persistentHomology/Calculation.hh>
#include <aleph/persistentHomology/algorithms/Standard.hh>
#include <aleph/persistentHomology/algorithms/Twist.hh>

#include <aleph/topology/BoundaryMatrix.hh>

#include <aleph/topology/representations/Set.hh>
#include <aleph/topology/representations/Vector.hh>

#include <vector>

template <class T> void testNonSquare()
{
  ALEPH_TEST_BEGIN( "Boundary matrix reduction for non-square matrices" );

  using namespace aleph;
  using namespace topology;
  using namespace representations;

  using Representation = Vector<T>;
  using Matrix         = BoundaryMatrix<Representation>;
  using Index          = typename Matrix::Index;

  Matrix M;
  M.setNumColumns( Index(4) );

  std::vector<T> columnA = { 1+4, 3+4, 4+4      };
  std::vector<T> columnB = { 2+4, 7+4, 8+4      };
  std::vector<T> columnC = { 5+4, 4+4, 7+4      };
  std::vector<T> columnE = { 1+4, 2+4, 5+4, 6+4 };

  M.setColumn( 0, columnA.begin(), columnA.end() );
  M.setColumn( 1, columnB.begin(), columnB.end() );
  M.setColumn( 2, columnC.begin(), columnC.end() );
  M.setColumn( 3, columnE.begin(), columnE.end() );

  using StandardAlgorithm = aleph::persistentHomology::algorithms::StandardRectangular;
  StandardAlgorithm algorithm;
  algorithm( M );

  Index i;
  bool valid;

  std::tie( i, valid ) = M.getMaximumIndex( 3 );

  ALEPH_ASSERT_EQUAL( valid, true  );
  ALEPH_ASSERT_EQUAL( i    ,   6+4 );

  ALEPH_TEST_END();
}

template <class M> void reduceBoundaryMatrix( const M& m )
{
  ALEPH_TEST_BEGIN( "Boundary matrix reduction" );

  ALEPH_ASSERT_THROW( m.getNumColumns() > 0 );

  using StandardAlgorithm = aleph::persistentHomology::algorithms::Standard;
  using TwistAlgorithm    = aleph::persistentHomology::algorithms::Twist;

  using Index   = typename M::Index;
  using Pairing = aleph::PersistencePairing<Index>;

  std::vector<Pairing> pairings;
  pairings.reserve( 4 );

  pairings.push_back( aleph::calculatePersistencePairing<StandardAlgorithm>( m ) );
  pairings.push_back( aleph::calculatePersistencePairing<StandardAlgorithm>( m.dualize() ) );

  pairings.push_back( aleph::calculatePersistencePairing<TwistAlgorithm>( m ) );
  pairings.push_back( aleph::calculatePersistencePairing<TwistAlgorithm>( m.dualize() ) );

  ALEPH_ASSERT_THROW( m != m.dualize() );
  ALEPH_ASSERT_THROW( m == m.dualize().dualize() );

  for( auto&& pairing : pairings )
  {
    ALEPH_ASSERT_THROW( pairing.empty() == false );
    ALEPH_ASSERT_THROW( pairing.size()  == 4 );
  }

  for( auto&& pairing1 : pairings )
    for( auto&& pairing2 : pairings )
      ALEPH_ASSERT_THROW( pairing1 == pairing2 );

  ALEPH_ASSERT_THROW( pairings.front().contains( Index(0) ) );
  ALEPH_ASSERT_THROW( pairings.front().contains( Index(1), Index(3) ) );
  ALEPH_ASSERT_THROW( pairings.front().contains( Index(2), Index(4) ) );
  ALEPH_ASSERT_THROW( pairings.front().contains( Index(5), Index(6) ) );

  ALEPH_TEST_END();
}

template <class T> void setupBoundaryMatrix()
{
  using namespace aleph;
  using namespace topology;
  using namespace representations;

  ALEPH_TEST_BEGIN( "Boundary matrix setup & loading" );

  using Set    = Set<T>;
  using Vector = Vector<T>;

  auto m1 = BoundaryMatrix<Set>::load( CMAKE_SOURCE_DIR + std::string( "/tests/input/Triangle.txt" ) );
  auto m2 = BoundaryMatrix<Vector>::load( CMAKE_SOURCE_DIR + std::string( "/tests/input/Triangle.txt" ) );

  reduceBoundaryMatrix( m1 );
  reduceBoundaryMatrix( m2 );

  ALEPH_TEST_END();
}

int main()
{
  setupBoundaryMatrix<unsigned int> ();
  setupBoundaryMatrix<unsigned long>();
  setupBoundaryMatrix<int>();
  setupBoundaryMatrix<long>();

  testNonSquare<int>          ();
  testNonSquare<long>         ();
  testNonSquare<unsigned int> ();
  testNonSquare<unsigned long>();
}
<commit_msg>Input & output for dual and original matrix<commit_after>#include <tests/Base.hh>

#include <aleph/persistentHomology/Calculation.hh>
#include <aleph/persistentHomology/algorithms/Standard.hh>
#include <aleph/persistentHomology/algorithms/Twist.hh>

#include <aleph/topology/BoundaryMatrix.hh>

#include <aleph/topology/representations/Set.hh>
#include <aleph/topology/representations/Vector.hh>

#include <vector>

template <class T> void testNonSquare()
{
  ALEPH_TEST_BEGIN( "Boundary matrix reduction for non-square matrices" );

  using namespace aleph;
  using namespace topology;
  using namespace representations;

  using Representation = Vector<T>;
  using Matrix         = BoundaryMatrix<Representation>;
  using Index          = typename Matrix::Index;

  // Rectangular matrix reduction --------------------------------------

  Matrix M;
  M.setNumColumns( Index(4) );

  std::vector<T> columnA = { 1+4, 3+4, 4+4      };
  std::vector<T> columnB = { 2+4, 7+4, 8+4      };
  std::vector<T> columnC = { 5+4, 4+4, 7+4      };
  std::vector<T> columnE = { 1+4, 2+4, 5+4, 6+4 };

  M.setColumn( 0, columnA.begin(), columnA.end() );
  M.setColumn( 1, columnB.begin(), columnB.end() );
  M.setColumn( 2, columnC.begin(), columnC.end() );
  M.setColumn( 3, columnE.begin(), columnE.end() );

  using StandardAlgorithm = aleph::persistentHomology::algorithms::StandardRectangular;
  StandardAlgorithm algorithm;

  algorithm( M );

  Index i;
  bool valid;

  std::tie( i, valid ) = M.getMaximumIndex( 3 );

  ALEPH_ASSERT_EQUAL( valid, true  );
  ALEPH_ASSERT_EQUAL( i    ,   6+4 );

  // Quadratic matrix reduction w/ constraints -------------------------

  Matrix N;
  N.setNumColumns( Index(12) );

  columnA = { 1+3, 3+3, 4+3      };
  columnB = { 2+3, 7+3, 8+3      };
  columnC = { 5+3, 4+3, 7+3      };
  columnE = { 1+3, 2+3, 5+3, 6+3 };

  N.setColumn( 0, columnA.begin(), columnA.end() );
  N.setColumn( 1, columnB.begin(), columnB.end() );
  N.setColumn( 2, columnC.begin(), columnC.end() );
  N.setColumn( 3, columnE.begin(), columnE.end() );

  auto N1 = N;

  algorithm( N1 );

  std::tie( i, valid ) = N1.getMaximumIndex( 3 );

  ALEPH_ASSERT_EQUAL( valid, true  );
  ALEPH_ASSERT_EQUAL( i    ,   6+3 );

  std::cerr << std::string( 72, '-' ) << "\n"
            << "Original space:\n"
            << std::string( 72, '-' ) << "\n\n";

  for( Index j = 0; j < N1.getNumColumns(); j++ )
  {
    std::tie( i, valid ) = N1.getMaximumIndex( j );

    if( valid )
      std::cerr << j << ": " << i << "\n";
  }

  auto N2 = N.dualize();

  algorithm( N2 );

  std::cerr << N2 << "\n";

  std::cerr << std::string( 72, '-' ) << "\n"
            << "Dual space:\n"
            << std::string( 72, '-' ) << "\n\n";

  for( Index j = 0; j < N2.getNumColumns(); j++ )
  {
    std::tie( i, valid ) = N2.getMaximumIndex( j );

    if( valid )
    {
      std::cerr << j << ": " << i << "\n"
                << 12 - 1 - j << ": " << 12 - 1 - i << "\n";
    }
  }

  ALEPH_TEST_END();
}

template <class M> void reduceBoundaryMatrix( const M& m )
{
  ALEPH_TEST_BEGIN( "Boundary matrix reduction" );

  ALEPH_ASSERT_THROW( m.getNumColumns() > 0 );

  using StandardAlgorithm = aleph::persistentHomology::algorithms::Standard;
  using TwistAlgorithm    = aleph::persistentHomology::algorithms::Twist;

  using Index   = typename M::Index;
  using Pairing = aleph::PersistencePairing<Index>;

  std::vector<Pairing> pairings;
  pairings.reserve( 4 );

  pairings.push_back( aleph::calculatePersistencePairing<StandardAlgorithm>( m ) );
  pairings.push_back( aleph::calculatePersistencePairing<StandardAlgorithm>( m.dualize() ) );

  pairings.push_back( aleph::calculatePersistencePairing<TwistAlgorithm>( m ) );
  pairings.push_back( aleph::calculatePersistencePairing<TwistAlgorithm>( m.dualize() ) );

  ALEPH_ASSERT_THROW( m != m.dualize() );
  ALEPH_ASSERT_THROW( m == m.dualize().dualize() );

  for( auto&& pairing : pairings )
  {
    ALEPH_ASSERT_THROW( pairing.empty() == false );
    ALEPH_ASSERT_THROW( pairing.size()  == 4 );
  }

  for( auto&& pairing1 : pairings )
    for( auto&& pairing2 : pairings )
      ALEPH_ASSERT_THROW( pairing1 == pairing2 );

  ALEPH_ASSERT_THROW( pairings.front().contains( Index(0) ) );
  ALEPH_ASSERT_THROW( pairings.front().contains( Index(1), Index(3) ) );
  ALEPH_ASSERT_THROW( pairings.front().contains( Index(2), Index(4) ) );
  ALEPH_ASSERT_THROW( pairings.front().contains( Index(5), Index(6) ) );

  ALEPH_TEST_END();
}

template <class T> void setupBoundaryMatrix()
{
  using namespace aleph;
  using namespace topology;
  using namespace representations;

  ALEPH_TEST_BEGIN( "Boundary matrix setup & loading" );

  using Set    = Set<T>;
  using Vector = Vector<T>;

  auto m1 = BoundaryMatrix<Set>::load( CMAKE_SOURCE_DIR + std::string( "/tests/input/Triangle.txt" ) );
  auto m2 = BoundaryMatrix<Vector>::load( CMAKE_SOURCE_DIR + std::string( "/tests/input/Triangle.txt" ) );

  reduceBoundaryMatrix( m1 );
  reduceBoundaryMatrix( m2 );

  ALEPH_TEST_END();
}

int main()
{
  setupBoundaryMatrix<unsigned int> ();
  setupBoundaryMatrix<unsigned long>();
  setupBoundaryMatrix<int>();
  setupBoundaryMatrix<long>();

  testNonSquare<int>          ();
  testNonSquare<long>         ();
  testNonSquare<unsigned int> ();
  testNonSquare<unsigned long>();
}
<|endoftext|>


<commit_before>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/menu/menu_controller.h"

#include "base/run_loop.h"
#include "ui/aura/window.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/views/test/views_test_base.h"
#include "ui/wm/public/dispatcher_client.h"

#if defined(OS_WIN)
#include "base/message_loop/message_pump_dispatcher.h"
#elif defined(USE_X11)
#include <X11/Xlib.h>
#undef Bool
#undef None
#endif

namespace views {

namespace {

class TestPlatformEventSource : public ui::PlatformEventSource {
 public:
  TestPlatformEventSource() {}
  virtual ~TestPlatformEventSource() {}

  uint32_t Dispatch(const ui::PlatformEvent& event) {
    return DispatchEvent(event);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource);
};

class TestDispatcherClient : public aura::client::DispatcherClient {
 public:
  TestDispatcherClient() : dispatcher_(NULL) {}
  virtual ~TestDispatcherClient() {}

  base::MessagePumpDispatcher* dispatcher() {
    return dispatcher_;
  }

  // aura::client::DispatcherClient:
  virtual void RunWithDispatcher(
      base::MessagePumpDispatcher* dispatcher) OVERRIDE {
    base::AutoReset<base::MessagePumpDispatcher*> reset_dispatcher(&dispatcher_,
                                                                   dispatcher);
    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    base::RunLoop run_loop;
    quit_callback_ = run_loop.QuitClosure();
    run_loop.Run();
  }

  virtual void QuitNestedMessageLoop() OVERRIDE { quit_callback_.Run(); }

 private:
  base::MessagePumpDispatcher* dispatcher_;
  base::Closure quit_callback_;

  DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient);
};

}  // namespace

class MenuControllerTest : public ViewsTestBase {
 public:
  MenuControllerTest() : controller_(NULL) {}
  virtual ~MenuControllerTest() {}

  // Dispatches |count| number of items, each in a separate iteration of the
  // message-loop, by posting a task.
  void Step3_DispatchEvents(int count) {
    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    controller_->exit_type_ = MenuController::EXIT_ALL;

#if defined(USE_X11)
    XEvent xevent;
    memset(&xevent, 0, sizeof(xevent));
    event_source_.Dispatch(&xevent);
#else
    MSG msg;
    memset(&msg, 0, sizeof(MSG));
    dispatcher_client_.dispatcher()->Dispatch(msg);
#endif

    if (count) {
      base::MessageLoop::current()->PostTask(
          FROM_HERE,
          base::Bind(&MenuControllerTest::Step3_DispatchEvents,
                     base::Unretained(this),
                     count - 1));
    } else {
      EXPECT_TRUE(run_loop_->running());
      run_loop_->Quit();
    }
  }

  // Runs a nested message-loop that does not involve the menu itself.
  void Step2_RunNestedLoop() {
    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&MenuControllerTest::Step3_DispatchEvents,
                   base::Unretained(this),
                   3));
    run_loop_.reset(new base::RunLoop());
    run_loop_->Run();
  }

  void Step1_RunMenu() {
    Widget widget;
    Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
    params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    widget.Init(params);
    widget.Show();

    aura::client::SetDispatcherClient(widget.GetNativeWindow()->GetRootWindow(),
                                      &dispatcher_client_);

    controller_ = new MenuController(NULL, true, NULL);
    controller_->owner_ = &widget;
    base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&MenuControllerTest::Step2_RunNestedLoop,
                   base::Unretained(this)));
    controller_->RunMessageLoop(false);
  }

 private:
  MenuController* controller_;
  scoped_ptr<base::RunLoop> run_loop_;
  TestPlatformEventSource event_source_;
  TestDispatcherClient dispatcher_client_;

  DISALLOW_COPY_AND_ASSIGN(MenuControllerTest);
};

TEST_F(MenuControllerTest, Basic) {
  base::MessageLoop::ScopedNestableTaskAllower allow_nested(
      base::MessageLoop::current());
  message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this)));
}

}  // namespace views
<commit_msg>ozone: views: Fix menu_controller_unittest test build<commit_after>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/menu/menu_controller.h"

#include "base/run_loop.h"
#include "ui/aura/window.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/views/test/views_test_base.h"
#include "ui/wm/public/dispatcher_client.h"

#if defined(OS_WIN)
#include "base/message_loop/message_pump_dispatcher.h"
#elif defined(USE_X11)
#include <X11/Xlib.h>
#undef Bool
#undef None
#elif defined(USE_OZONE)
#include "ui/events/event.h"
#endif

namespace views {

namespace {

class TestPlatformEventSource : public ui::PlatformEventSource {
 public:
  TestPlatformEventSource() {}
  virtual ~TestPlatformEventSource() {}

  uint32_t Dispatch(const ui::PlatformEvent& event) {
    return DispatchEvent(event);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource);
};

class TestDispatcherClient : public aura::client::DispatcherClient {
 public:
  TestDispatcherClient() : dispatcher_(NULL) {}
  virtual ~TestDispatcherClient() {}

  base::MessagePumpDispatcher* dispatcher() {
    return dispatcher_;
  }

  // aura::client::DispatcherClient:
  virtual void RunWithDispatcher(
      base::MessagePumpDispatcher* dispatcher) OVERRIDE {
    base::AutoReset<base::MessagePumpDispatcher*> reset_dispatcher(&dispatcher_,
                                                                   dispatcher);
    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    base::RunLoop run_loop;
    quit_callback_ = run_loop.QuitClosure();
    run_loop.Run();
  }

  virtual void QuitNestedMessageLoop() OVERRIDE { quit_callback_.Run(); }

 private:
  base::MessagePumpDispatcher* dispatcher_;
  base::Closure quit_callback_;

  DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient);
};

}  // namespace

class MenuControllerTest : public ViewsTestBase {
 public:
  MenuControllerTest() : controller_(NULL) {}
  virtual ~MenuControllerTest() {}

  // Dispatches |count| number of items, each in a separate iteration of the
  // message-loop, by posting a task.
  void Step3_DispatchEvents(int count) {
    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    controller_->exit_type_ = MenuController::EXIT_ALL;

#if defined(USE_X11)
    XEvent xevent;
    memset(&xevent, 0, sizeof(xevent));
    event_source_.Dispatch(&xevent);
#elif defined(OS_WIN)
    MSG msg;
    memset(&msg, 0, sizeof(MSG));
    dispatcher_client_.dispatcher()->Dispatch(msg);
#elif defined(USE_OZONE)
    ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0, true);
    dispatcher_client_.dispatcher()->Dispatch(&event);
#else
#error Unsupported platform
#endif

    if (count) {
      base::MessageLoop::current()->PostTask(
          FROM_HERE,
          base::Bind(&MenuControllerTest::Step3_DispatchEvents,
                     base::Unretained(this),
                     count - 1));
    } else {
      EXPECT_TRUE(run_loop_->running());
      run_loop_->Quit();
    }
  }

  // Runs a nested message-loop that does not involve the menu itself.
  void Step2_RunNestedLoop() {
    base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
    base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&MenuControllerTest::Step3_DispatchEvents,
                   base::Unretained(this),
                   3));
    run_loop_.reset(new base::RunLoop());
    run_loop_->Run();
  }

  void Step1_RunMenu() {
    Widget widget;
    Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
    params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    widget.Init(params);
    widget.Show();

    aura::client::SetDispatcherClient(widget.GetNativeWindow()->GetRootWindow(),
                                      &dispatcher_client_);

    controller_ = new MenuController(NULL, true, NULL);
    controller_->owner_ = &widget;
    base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&MenuControllerTest::Step2_RunNestedLoop,
                   base::Unretained(this)));
    controller_->RunMessageLoop(false);
  }

 private:
  MenuController* controller_;
  scoped_ptr<base::RunLoop> run_loop_;
  TestPlatformEventSource event_source_;
  TestDispatcherClient dispatcher_client_;

  DISALLOW_COPY_AND_ASSIGN(MenuControllerTest);
};

TEST_F(MenuControllerTest, Basic) {
  base::MessageLoop::ScopedNestableTaskAllower allow_nested(
      base::MessageLoop::current());
  message_loop()->PostTask(
      FROM_HERE,
      base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this)));
}

}  // namespace views
<|endoftext|>