<commit_before>#include "world.hh"
#include "actor.hh"
#include "common.hh"

void World::generate(int seed) {
	srand(seed);
	createCity(10, 10);
}

Actor& World::addActor(Actor* actor) {
	actors.push_back(actor);
	actors.back().setWorld(this);
	return actors.back();
}

/// Function: updateView
/// Updates the Actor's view.
void World::updateView(Actor& actor) {
	tilearray& view = actor.getView();
	for (int j = actor.y - viewYDist; j <= actor.y + viewYDist; j++) {
		for (int i = actor.x - viewXDist; i <= actor.x + viewXDist; i++) {
			if (i < 0 || j < 0 || i >= width || j >= height) continue;
			bool visible = hasLOS(actor, i, j);
			bool explored = visible ? true : actor.hasExplored(i,j);
			Tile& tile = view[j][i];
			if (visible || explored) tile = getTile(i, j);
			if (!visible) tile.actor = NULL;
			tile.visible = visible;
			tile.explored = explored;
		}
	}
}

/// Function updateVisibleActors
/// Updates all visibile actors lists of all actors
void World::updateVisibleActors() {
	for (Actors::iterator it = actors.begin(); it != actors.end(); it++) {
		it->visible_actors.clear();
	}
	for (Actors::iterator it = actors.begin(); it != actors.end(); it++) {
		for (Actors::iterator it2 = actors.begin(); it2 != actors.end(); it2++) {
			if (it == it2) continue;
			if (it->getConstView()[it2->y][it2->x].visible) {
				it->visible_actors.push_back(&(*it2));
			}
		}
	}
}

/// Function: hasLOS
/// Tests if given coordinates are visible for a player.
bool World::hasLOS(const Actor& actor, int x, int y) const {
	float dist = distance2d(float(x), float(y), float(actor.x), float(actor.y));

	if (dist > actor.viewDist) return false; // Out of reach
	if (x < 0 || y < 0 || x >= width || y >= height) return false; // Out of world
	if (dist <= 1.5) return true; // Very close

	float dx = float(x - actor.x) / (dist * 2.0);
	float dy = float(y - actor.y) / (dist * 2.0);
	float xx = actor.x + dx;
	float yy = actor.y + dy;
	float dd = distance2d(0.0f,0.0f,dx,dy);
	float d = 0.5 + dd;
	do {
		if (d > getTile(round(xx), round(yy)).blocks_vision_dist) return false;
		xx += dx; yy += dy; d += dd;
	} while (distance2d(float(x), float(y), xx, yy) > 0.5);
	return true;
}

/// Function: update
/// Updates the world - views, visibilities and AI
void World::update(bool skipAI) {
	// Reset tiles' actor pointers
	int w = getWidth(), h = getHeight();
	for (int j = 0; j < h; j++)
		for (int i = 0; i < w; i++) tiles[j][i].actor = NULL;
	// Update tiles' actor pointers
	for (Actors::iterator it = actors.begin(); it != actors.end(); ++it)
		tiles[it->y][it->x].actor = &(*it);
	// Update views
	for (Actors::iterator it = actors.begin(); it != actors.end(); ++it)
		updateView(*it);
	// Update visible actors
	updateVisibleActors();
	// Do AI
	if (skipAI) return;
	for (Actors::iterator it = actors.begin(); it != actors.end(); ++it)
		if (it->useAI) it->AI();
}

/// Function: draw
/// Draws the world with actors.
void World::draw(Actor& actor) {
	// Cls
	werase(worldwin);
	// Border
	wattroff(worldwin, A_BOLD);
	wcolor_set(worldwin, COLOR_GREEN, 0);
	box(worldwin, 0 , 0);
	// Tiles
	const tilearray& view = actor.getConstView();
	for (int j = actor.y - viewYDist; j <= actor.y + viewYDist; j++) {
		for (int i = actor.x - viewXDist; i <= actor.x + viewXDist; i++) {
			Tile tile;
			if (i >= 0 && j >= 0 && i < width && j < height) tile = view[j][i];
			if (tile.visible) wattron(worldwin, A_BOLD);
			else wattroff(worldwin, A_BOLD);
			wcolor_set(worldwin, tile.color, 0);
			mvwaddch(worldwin, y2scr(j, actor.y), x2scr(i, actor.x), tile.ch);
		}
	}
	// ActorPtrs
	for (ActorPtrs::const_iterator it = actor.visible_actors.begin(); it != actor.visible_actors.end(); ++it) {
		setColor(worldwin, (*it)->getColor());
		mvwaddch(worldwin, y2scr((*it)->y, actor.y),
						   x2scr((*it)->x, actor.x), (*it)->getChar());
	}
	// Player
	setColor(worldwin, actor.getColor());
	mvwaddch(worldwin, y2scr(actor.y, actor.y),
					   x2scr(actor.x, actor.x), actor.getChar());
	// Flush
	wrefresh(worldwin);
}
<commit_msg>Much better FOV algorithm.<commit_after>#include "world.hh"
#include "actor.hh"
#include "common.hh"

namespace {
	// FOV algorithm from reflexivelos roguelike engine under MIT License.
	// Based on generalization of Bresenham, runs in O(N^3).
	void fov_dir(int dir, int dis, int px, int py, tilearray& view, World& world) {
		int cx, cy;
		for (int q = 1; q <= dis; ++q) for (int p = 0; p < q+1; p++) {
			for (int ad1 = 1, ad2[2] = {0, 0}, s[2] = {0, q-1}, eps[2] = {0, q-1}; ad1 <= dis && s[0] <= s[1]; ++ad1)
			  for (int i = 0; i < 2; i++) {
				eps[i] += p;
				if (eps[i] >= q) { // This should look familiar
					eps[i] -= q;
					++ad2[i];
				}
				cx = ad1, cy = ad2[i];
				if (dir&1) cx = -cx;
				if (dir&2) cy = -cy;
				if (dir&4) cx ^= cy ^= cx ^= cy;
				cx += px, cy += py;
				int d = (cx-px)*(cx-px) + (cy-py)*(cy-py); // Squared distance from origin
				// Visible
				if (!(cx < 0 || cy < 0 || cx >= world.getWidth() || cy >= world.getHeight())) {
					if (d <= dis*dis + 1) {
						view[cy][cx] = world.getTile(cx, cy);
						view[cy][cx].explored = true;
						view[cy][cx].visible = true;
					}
				}
				// Update range of possibilities for s
				int dd = world.getTile(cx, cy).blocks_vision_dist;
				if (d > dd*dd) { // if blocks_vision
					if (i == 0) s[i] += q-eps[i], eps[i] = 0, ++ad2[0];
					if (i == 1) s[i] -= eps[i]+1, eps[i] = q-1, --ad2[1];
				}
			}
		}
	}
}

void World::generate(int seed) {
	srand(seed);
	createCity(10, 10);
}

Actor& World::addActor(Actor* actor) {
	actors.push_back(actor);
	actors.back().setWorld(this);
	return actors.back();
}

/// Function: updateView
/// Updates the Actor's view.
void World::updateView(Actor& actor) {
	tilearray& view = actor.getView();
	// Reset light
	for (int j = actor.y - viewYDist; j <= actor.y + viewYDist; j++) {
		for (int i = actor.x - viewXDist; i <= actor.x + viewXDist; i++) {
			if (i < 0 || j < 0 || i >= width || j >= height) continue;
			view[j][i].visible = false;
		}
	}
	// Calculate FOV
	for (int i = 0; i < 8; ++i) fov_dir(i, actor.viewDist, actor.x, actor.y, view, *this);
}

/// Function updateVisibleActors
/// Updates all visibile actors lists of all actors
void World::updateVisibleActors() {
	for (Actors::iterator it = actors.begin(); it != actors.end(); it++) {
		it->visible_actors.clear();
	}
	for (Actors::iterator it = actors.begin(); it != actors.end(); it++) {
		for (Actors::iterator it2 = actors.begin(); it2 != actors.end(); it2++) {
			if (it == it2) continue;
			if (it->getConstView()[it2->y][it2->x].visible) {
				it->visible_actors.push_back(&(*it2));
			}
		}
	}
}

/// Function: hasLOS
/// Tests if given coordinates are visible for a player.
bool World::hasLOS(const Actor& actor, int x, int y) const {
	float dist = distance2d(float(x), float(y), float(actor.x), float(actor.y));

	if (dist > actor.viewDist) return false; // Out of reach
	if (x < 0 || y < 0 || x >= width || y >= height) return false; // Out of world
	if (dist <= 1.5) return true; // Very close

	float dx = float(x - actor.x) / (dist * 2.0);
	float dy = float(y - actor.y) / (dist * 2.0);
	float xx = actor.x + dx;
	float yy = actor.y + dy;
	float dd = distance2d(0.0f,0.0f,dx,dy);
	float d = 0.5 + dd;
	do {
		if (d > getTile(round(xx), round(yy)).blocks_vision_dist) return false;
		xx += dx; yy += dy; d += dd;
	} while (distance2d(float(x), float(y), xx, yy) > 0.5);
	return true;
}

/// Function: update
/// Updates the world - views, visibilities and AI
void World::update(bool skipAI) {
	// Reset tiles' actor pointers
	int w = getWidth(), h = getHeight();
	for (int j = 0; j < h; j++)
		for (int i = 0; i < w; i++) tiles[j][i].actor = NULL;
	// Update tiles' actor pointers
	for (Actors::iterator it = actors.begin(); it != actors.end(); ++it)
		tiles[it->y][it->x].actor = &(*it);
	// Update views
	for (Actors::iterator it = actors.begin(); it != actors.end(); ++it)
		updateView(*it);
	// Update visible actors
	updateVisibleActors();
	// Do AI
	if (skipAI) return;
	for (Actors::iterator it = actors.begin(); it != actors.end(); ++it)
		if (it->useAI) it->AI();
}

/// Function: draw
/// Draws the world with actors.
void World::draw(Actor& actor) {
	// Cls
	werase(worldwin);
	// Border
	wattroff(worldwin, A_BOLD);
	wcolor_set(worldwin, COLOR_GREEN, 0);
	box(worldwin, 0 , 0);
	// Tiles
	const tilearray& view = actor.getConstView();
	for (int j = actor.y - viewYDist; j <= actor.y + viewYDist; j++) {
		for (int i = actor.x - viewXDist; i <= actor.x + viewXDist; i++) {
			Tile tile;
			if (i >= 0 && j >= 0 && i < width && j < height) tile = view[j][i];
			if (tile.visible) wattron(worldwin, A_BOLD);
			else wattroff(worldwin, A_BOLD);
			wcolor_set(worldwin, tile.color, 0);
			mvwaddch(worldwin, y2scr(j, actor.y), x2scr(i, actor.x), tile.ch);
		}
	}
	// ActorPtrs
	for (ActorPtrs::const_iterator it = actor.visible_actors.begin(); it != actor.visible_actors.end(); ++it) {
		setColor(worldwin, (*it)->getColor());
		mvwaddch(worldwin, y2scr((*it)->y, actor.y),
						   x2scr((*it)->x, actor.x), (*it)->getChar());
	}
	// Player
	setColor(worldwin, actor.getColor());
	mvwaddch(worldwin, y2scr(actor.y, actor.y),
					   x2scr(actor.x, actor.x), actor.getChar());
	// Flush
	wrefresh(worldwin);
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "TerrainChunkRenderer.h"
#include "GL3DProgram.h"
#include "TerrainChunk.h"

TerrainChunkRenderer::TerrainChunkRenderer() {

}
TerrainChunkRenderer::~TerrainChunkRenderer() {
	//Destroy all the render slots
	for (auto slot : renderSlots)
		delete slot.second;
	for (auto slot : openSlots)
		delete slot;
}

TerrainChunkRenderer::HotChunk::HotChunk() {
	glGenBuffers(1,&vertexData);
	glGenVertexArrays(1,&vertexArrayBuffer);
}
TerrainChunkRenderer::HotChunk::~HotChunk() {
	glDeleteBuffers(1,&vertexData);
	glDeleteVertexArrays(1,&vertexArrayBuffer);
}
//Render the set chunk
void TerrainChunkRenderer::HotChunk::Render(GL3DProgram * shader) {
	glBindVertexArray ( vertexArrayBuffer );
	glBindBuffer ( GL_ARRAY_BUFFER, vertexData );
	glVertexAttribPointer ( shader->AttributeVertex(), 3, GL_FLOAT, GL_FALSE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,Vertex) );
	glVertexAttribPointer ( shader->AttributeTexture(), 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,TextureCoordinateX) );
	glDrawArrays( GL_TRIANGLES, 0, vertexCount);
}
void TerrainChunkRenderer::HotChunk::Set(TerrainChunk * chunk, GL3DProgram * shader) {
	vertexCount = chunk->VertexDataSize;

	glBindVertexArray ( vertexArrayBuffer );
	//vertex positions
	glBindBuffer ( GL_ARRAY_BUFFER, vertexData );
	glBufferData ( GL_ARRAY_BUFFER, vertexCount*sizeof(TerrainChunk::ChunkVertexData), chunk->VertexData, GL_STATIC_DRAW );
	glEnableVertexAttribArray ( shader->AttributeVertex() );
	glVertexAttribPointer ( shader->AttributeVertex(), 3, GL_FLOAT, GL_FALSE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,Vertex) );
	glEnableVertexAttribArray ( shader->AttributeTexture() );
	glVertexAttribPointer ( shader->AttributeTexture(), 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,TextureCoordinateX) );

	//glVertexAttribPointer ( shader->AttributeTexture(), 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,TextureCoordinate) );

}

void TerrainChunkRenderer::StartRendering(GL3DProgram * shader) {

}

void TerrainChunkRenderer::RenderTerrainChunk(vec2 tilePos, TerrainChunk * chunk, GL3DProgram * shader) {
	//Draw the chunk relative to itself
	shader->Model.PushMatrix();
	shader->Model.Translate(chunk->X,chunk->Y,0);
	shader->Model.Apply();

	//Check if this chunk is already buffered
	vec2 chunkPosition = tilePos+vec2(chunk->X,chunk->Y);
	auto slotIterator = renderSlots.find(chunkPosition);
	HotChunk * renderer;
	if (slotIterator == renderSlots.end()) {
		//Doesn't exist, buffer it
		//find an open slot
		HotChunk * openSlot;
		if (openSlots.size() > 0) {
			openSlot = openSlots.back();
			openSlots.pop_back();
		}
		else
			openSlot = new HotChunk();

		//Set the slot to hold the given chunk
		openSlot->Set(chunk,shader);

		//Save the slot as in use
		renderSlots[chunkPosition] = openSlot;
		renderer = openSlot;
	}
	else
		renderer = slotIterator->second;

	//Run the renderer
	renderer->Render(shader);
	//Mark it as used
	renderer->Rendered = true;

	shader->Model.PopMatrix();
}

void TerrainChunkRenderer::FinishRendering(GL3DProgram * shader) {
	//Find renderers which were not used and mark them as free
	for (auto it = renderSlots.begin(); it != renderSlots.end();) {
		if (it->second->Rendered) {
			it->second->Rendered = false;
			it++;
		}
		else {
			openSlots.push_back(it->second);
			it = renderSlots.erase(it);
		}
	}
}<commit_msg>Added a check to make sure vertex data is packing correctly<commit_after>#include "stdafx.h"
#include "TerrainChunkRenderer.h"
#include "GL3DProgram.h"
#include "TerrainChunk.h"

TerrainChunkRenderer::TerrainChunkRenderer() {
	cout << "Chunk vertex data size: " << sizeof(TerrainChunk::ChunkVertexData) << "\n";
}
TerrainChunkRenderer::~TerrainChunkRenderer() {
	//Destroy all the render slots
	for (auto slot : renderSlots)
		delete slot.second;
	for (auto slot : openSlots)
		delete slot;
}

TerrainChunkRenderer::HotChunk::HotChunk() {
	glGenBuffers(1,&vertexData);
	glGenVertexArrays(1,&vertexArrayBuffer);
}
TerrainChunkRenderer::HotChunk::~HotChunk() {
	glDeleteBuffers(1,&vertexData);
	glDeleteVertexArrays(1,&vertexArrayBuffer);
}
//Render the set chunk
void TerrainChunkRenderer::HotChunk::Render(GL3DProgram * shader) {
	glBindVertexArray ( vertexArrayBuffer );
	glBindBuffer ( GL_ARRAY_BUFFER, vertexData );
	glVertexAttribPointer ( shader->AttributeVertex(), 3, GL_FLOAT, GL_FALSE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,Vertex) );
	glVertexAttribPointer ( shader->AttributeTexture(), 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,TextureCoordinateX) );
	glDrawArrays( GL_TRIANGLES, 0, vertexCount);
}
void TerrainChunkRenderer::HotChunk::Set(TerrainChunk * chunk, GL3DProgram * shader) {
	vertexCount = chunk->VertexDataSize;

	glBindVertexArray ( vertexArrayBuffer );
	//vertex positions
	glBindBuffer ( GL_ARRAY_BUFFER, vertexData );
	glBufferData ( GL_ARRAY_BUFFER, vertexCount*sizeof(TerrainChunk::ChunkVertexData), chunk->VertexData, GL_STATIC_DRAW );
	glEnableVertexAttribArray ( shader->AttributeVertex() );
	glVertexAttribPointer ( shader->AttributeVertex(), 3, GL_FLOAT, GL_FALSE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,Vertex) );
	glEnableVertexAttribArray ( shader->AttributeTexture() );
	glVertexAttribPointer ( shader->AttributeTexture(), 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,TextureCoordinateX) );

	//glVertexAttribPointer ( shader->AttributeTexture(), 1, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(TerrainChunk::ChunkVertexData), (void*)offsetof(TerrainChunk::ChunkVertexData,TextureCoordinate) );

}

void TerrainChunkRenderer::StartRendering(GL3DProgram * shader) {

}

void TerrainChunkRenderer::RenderTerrainChunk(vec2 tilePos, TerrainChunk * chunk, GL3DProgram * shader) {
	//Draw the chunk relative to itself
	shader->Model.PushMatrix();
	shader->Model.Translate(chunk->X,chunk->Y,0);
	shader->Model.Apply();

	//Check if this chunk is already buffered
	vec2 chunkPosition = tilePos+vec2(chunk->X,chunk->Y);
	auto slotIterator = renderSlots.find(chunkPosition);
	HotChunk * renderer;
	if (slotIterator == renderSlots.end()) {
		//Doesn't exist, buffer it
		//find an open slot
		HotChunk * openSlot;
		if (openSlots.size() > 0) {
			openSlot = openSlots.back();
			openSlots.pop_back();
		}
		else
			openSlot = new HotChunk();

		//Set the slot to hold the given chunk
		openSlot->Set(chunk,shader);

		//Save the slot as in use
		renderSlots[chunkPosition] = openSlot;
		renderer = openSlot;
	}
	else
		renderer = slotIterator->second;

	//Run the renderer
	renderer->Render(shader);
	//Mark it as used
	renderer->Rendered = true;

	shader->Model.PopMatrix();
}

void TerrainChunkRenderer::FinishRendering(GL3DProgram * shader) {
	//Find renderers which were not used and mark them as free
	for (auto it = renderSlots.begin(); it != renderSlots.end();) {
		if (it->second->Rendered) {
			it->second->Rendered = false;
			it++;
		}
		else {
			openSlots.push_back(it->second);
			it = renderSlots.erase(it);
		}
	}
}<|endoftext|>


<commit_before>//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Engine.h"
#include "Camera.h"
#include "CoreEvents.h"
#include "Font.h"
#include "Graphics.h"
#include "Input.h"
#include "Material.h"
#include "Model.h"
#include "Octree.h"
#include "Renderer.h"
#include "ResourceCache.h"
#include "StaticModel.h"
#include "Text.h"
#include "UI.h"

#include "StaticScene.h"

#include "DebugNew.h"

// Expands to this example's entry-point
DEFINE_APPLICATION_MAIN(StaticScene)

StaticScene::StaticScene(Context* context) :
    Sample(context),
    yaw_(0.0f),
    pitch_(0.0f)
{
}

void StaticScene::Start()
{
    // Execute base class startup
    Sample::Start();

    // Create the scene content
    CreateScene();

    // Create the UI content
    CreateInstructions();

    // Setup the viewport for displaying the scene
    SetupViewport();

    // Hook up to the frame update events
    SubscribeToEvents();
}

void StaticScene::CreateScene()
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();

    scene_ = new Scene(context_);

    // Create the Octree component to the scene. This is required before adding any drawable components, or else nothing will
    // show up. The default octree volume will be from (-1000, -1000, -1000) to (1000, 1000, 1000) in world coordinates; it
    // is also legal to place objects outside the volume but their visibility can then not be checked in a hierarchically
    // optimizing manner
    scene_->CreateComponent<Octree>();

    // Create a child scene node (at world origin) and a StaticModel component into it. Set the StaticModel to show a simple
    // plane mesh with a "stone" material. Note that naming the scene nodes is optional. Scale the scene node larger
    // (100 x 100 world units)
    Node* planeNode = scene_->CreateChild("Plane");
    StaticModel* planeObject = planeNode->CreateComponent<StaticModel>();
    planeNode->SetScale(Vector3(100.0f, 1.0f, 100.0f));
    planeObject->SetModel(cache->GetResource<Model>("Models/Plane.mdl"));
    planeObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));

    // Create a directional light to the world so that we can see something. The light scene node's orientation controls the
    // light direction; we will use the SetDirection() function which calculates the orientation from a forward direction vector.
    // The light will use default settings (white light, no shadows)
    Node* lightNode = scene_->CreateChild("Directional light");
    lightNode->SetDirection(Vector3(-1.0f, -1.0f, -1.0f)); // The direction vector does not need to be normalized
    Light* light = lightNode->CreateComponent<Light>();
    light->SetLightType(LIGHT_DIRECTIONAL);

    // Create more StaticModel objects to the scene, randomly positioned, rotated and scaled. For rotation, we construct a
    // quaternion from Euler angles where the Y angle (rotation about the Y axis) is randomized. The mushroom model contains
    // LOD levels, so the StaticModel component will automatically select the LOD level according to the view distance (you'll
    // see the model get simpler as it moves further away). Finally, rendering a large number of the same object with the
    // same material allows instancing to be used, if the GPU supports it. This reduces the amount of CPU work in rendering the
    // scene.
    const unsigned NUM_OBJECTS = 200;
    for (unsigned i = 0; i < NUM_OBJECTS; ++i)
    {
        Node* mushroomNode = scene_->CreateChild("Mushroom");
        StaticModel* mushroomObject = mushroomNode->CreateComponent<StaticModel>();
        mushroomNode->SetPosition(Vector3(Random(90.0f) - 45.0f, 0.0f, Random(90.0f) - 45.0f));
        mushroomNode->SetRotation(Quaternion(0.0f, Random(360.0f), 0.0f));
        mushroomNode->SetScale(1.0f + Random(1.0f));
        mushroomObject->SetModel(cache->GetResource<Model>("Models/Mushroom.mdl"));
        mushroomObject->SetMaterial(cache->GetResource<Material>("Materials/Mushroom.xml"));
    }

    // Create a scene node for the camera, which we will move around
    // The camera will use default settings (1000 far clip distance, 45 degrees FOV, set aspect ratio automatically)
    cameraNode_ = scene_->CreateChild("Camera");
    cameraNode_->CreateComponent<Camera>();

    // Set an initial position for the camera scene node above the plane
    cameraNode_->SetPosition(Vector3(0.0f, 5.0f, 0.0f));
}

void StaticScene::CreateInstructions()
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    UI* ui = GetSubsystem<UI>();

    // Construct new Text object, set string to display and font to use
    Text* instructionText = ui->GetRoot()->CreateChild<Text>();
    instructionText->SetText("Use WASD keys and mouse to move");
    instructionText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);

    // Position the text relative to the screen center
    instructionText->SetHorizontalAlignment(HA_CENTER);
    instructionText->SetVerticalAlignment(VA_CENTER);
    instructionText->SetPosition(0, ui->GetRoot()->GetHeight() / 4);
}

void StaticScene::SetupViewport()
{
    Renderer* renderer = GetSubsystem<Renderer>();

    // Set up a viewport to the Renderer subsystem so that the 3D scene can be seen. We need to define the scene and the camera
    // at minimum. Additionally we could configure the viewport screen size and the rendering path (eg. forward / deferred) to
    // use, but now we just use full screen and default render path configured in the engine command line options
    SharedPtr<Viewport> viewport(new Viewport(context_, scene_, cameraNode_->GetComponent<Camera>()));
    renderer->SetViewport(0, viewport);
}

void StaticScene::MoveCamera(float timeStep)
{
    Input* input = GetSubsystem<Input>();

    // Movement speed as world units per second
    const float MOVE_SPEED = 20.0f;
    // Mouse sensitivity as degrees per pixel
    const float MOUSE_SENSITIVITY = 0.1f;

    // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
    IntVector2 mouseMove = input->GetMouseMove();
    yaw_ += MOUSE_SENSITIVITY * mouseMove.x_;
    pitch_ += MOUSE_SENSITIVITY * mouseMove.y_;
    pitch_ = Clamp(pitch_, -90.0f, 90.0f);

    // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero.
    cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));

    // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
    // Use the TranslateRelative() function to move relative to the node's orientation. Alternatively we could
    // multiply the desired direction with the node's orientation quaternion, and use just Translate()
    if (input->GetKeyDown('W'))
        cameraNode_->TranslateRelative(Vector3::FORWARD * MOVE_SPEED * timeStep);
    if (input->GetKeyDown('S'))
        cameraNode_->TranslateRelative(Vector3::BACK * MOVE_SPEED * timeStep);
    if (input->GetKeyDown('A'))
        cameraNode_->TranslateRelative(Vector3::LEFT * MOVE_SPEED * timeStep);
    if (input->GetKeyDown('D'))
        cameraNode_->TranslateRelative(Vector3::RIGHT * MOVE_SPEED * timeStep);
}

void StaticScene::SubscribeToEvents()
{
    // Subscribes HandleUpdate() method for processing update events
    SubscribeToEvent(E_UPDATE, HANDLER(StaticScene, HandleUpdate));
}

void StaticScene::HandleUpdate(StringHash eventType, VariantMap& eventData)
{
    // Event parameters are always defined inside a namespace corresponding to the event's name
    using namespace Update;

    // Take the frame time step, which is stored as a float
    float timeStep = eventData[P_TIMESTEP].GetFloat();

    // Move the camera, scale movement with time step
    MoveCamera(timeStep);
}
<commit_msg>Disable camera controls when the UI has a focused element.<commit_after>//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Engine.h"
#include "Camera.h"
#include "CoreEvents.h"
#include "Font.h"
#include "Graphics.h"
#include "Input.h"
#include "Material.h"
#include "Model.h"
#include "Octree.h"
#include "Renderer.h"
#include "ResourceCache.h"
#include "StaticModel.h"
#include "Text.h"
#include "UI.h"

#include "StaticScene.h"

#include "DebugNew.h"

// Expands to this example's entry-point
DEFINE_APPLICATION_MAIN(StaticScene)

StaticScene::StaticScene(Context* context) :
    Sample(context),
    yaw_(0.0f),
    pitch_(0.0f)
{
}

void StaticScene::Start()
{
    // Execute base class startup
    Sample::Start();

    // Create the scene content
    CreateScene();

    // Create the UI content
    CreateInstructions();

    // Setup the viewport for displaying the scene
    SetupViewport();

    // Hook up to the frame update events
    SubscribeToEvents();
}

void StaticScene::CreateScene()
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();

    scene_ = new Scene(context_);

    // Create the Octree component to the scene. This is required before adding any drawable components, or else nothing will
    // show up. The default octree volume will be from (-1000, -1000, -1000) to (1000, 1000, 1000) in world coordinates; it
    // is also legal to place objects outside the volume but their visibility can then not be checked in a hierarchically
    // optimizing manner
    scene_->CreateComponent<Octree>();

    // Create a child scene node (at world origin) and a StaticModel component into it. Set the StaticModel to show a simple
    // plane mesh with a "stone" material. Note that naming the scene nodes is optional. Scale the scene node larger
    // (100 x 100 world units)
    Node* planeNode = scene_->CreateChild("Plane");
    StaticModel* planeObject = planeNode->CreateComponent<StaticModel>();
    planeNode->SetScale(Vector3(100.0f, 1.0f, 100.0f));
    planeObject->SetModel(cache->GetResource<Model>("Models/Plane.mdl"));
    planeObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));

    // Create a directional light to the world so that we can see something. The light scene node's orientation controls the
    // light direction; we will use the SetDirection() function which calculates the orientation from a forward direction vector.
    // The light will use default settings (white light, no shadows)
    Node* lightNode = scene_->CreateChild("Directional light");
    lightNode->SetDirection(Vector3(-1.0f, -1.0f, -1.0f)); // The direction vector does not need to be normalized
    Light* light = lightNode->CreateComponent<Light>();
    light->SetLightType(LIGHT_DIRECTIONAL);

    // Create more StaticModel objects to the scene, randomly positioned, rotated and scaled. For rotation, we construct a
    // quaternion from Euler angles where the Y angle (rotation about the Y axis) is randomized. The mushroom model contains
    // LOD levels, so the StaticModel component will automatically select the LOD level according to the view distance (you'll
    // see the model get simpler as it moves further away). Finally, rendering a large number of the same object with the
    // same material allows instancing to be used, if the GPU supports it. This reduces the amount of CPU work in rendering the
    // scene.
    const unsigned NUM_OBJECTS = 200;
    for (unsigned i = 0; i < NUM_OBJECTS; ++i)
    {
        Node* mushroomNode = scene_->CreateChild("Mushroom");
        StaticModel* mushroomObject = mushroomNode->CreateComponent<StaticModel>();
        mushroomNode->SetPosition(Vector3(Random(90.0f) - 45.0f, 0.0f, Random(90.0f) - 45.0f));
        mushroomNode->SetRotation(Quaternion(0.0f, Random(360.0f), 0.0f));
        mushroomNode->SetScale(1.0f + Random(1.0f));
        mushroomObject->SetModel(cache->GetResource<Model>("Models/Mushroom.mdl"));
        mushroomObject->SetMaterial(cache->GetResource<Material>("Materials/Mushroom.xml"));
    }

    // Create a scene node for the camera, which we will move around
    // The camera will use default settings (1000 far clip distance, 45 degrees FOV, set aspect ratio automatically)
    cameraNode_ = scene_->CreateChild("Camera");
    cameraNode_->CreateComponent<Camera>();

    // Set an initial position for the camera scene node above the plane
    cameraNode_->SetPosition(Vector3(0.0f, 5.0f, 0.0f));
}

void StaticScene::CreateInstructions()
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    UI* ui = GetSubsystem<UI>();

    // Construct new Text object, set string to display and font to use
    Text* instructionText = ui->GetRoot()->CreateChild<Text>();
    instructionText->SetText("Use WASD keys and mouse to move");
    instructionText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);

    // Position the text relative to the screen center
    instructionText->SetHorizontalAlignment(HA_CENTER);
    instructionText->SetVerticalAlignment(VA_CENTER);
    instructionText->SetPosition(0, ui->GetRoot()->GetHeight() / 4);
}

void StaticScene::SetupViewport()
{
    Renderer* renderer = GetSubsystem<Renderer>();

    // Set up a viewport to the Renderer subsystem so that the 3D scene can be seen. We need to define the scene and the camera
    // at minimum. Additionally we could configure the viewport screen size and the rendering path (eg. forward / deferred) to
    // use, but now we just use full screen and default render path configured in the engine command line options
    SharedPtr<Viewport> viewport(new Viewport(context_, scene_, cameraNode_->GetComponent<Camera>()));
    renderer->SetViewport(0, viewport);
}

void StaticScene::MoveCamera(float timeStep)
{
    // Do not move if the UI has a focused element (the console)
    UI* ui = GetSubsystem<UI>();
    if (ui->GetFocusElement())
        return;

    Input* input = GetSubsystem<Input>();

    // Movement speed as world units per second
    const float MOVE_SPEED = 20.0f;
    // Mouse sensitivity as degrees per pixel
    const float MOUSE_SENSITIVITY = 0.1f;

    // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
    IntVector2 mouseMove = input->GetMouseMove();
    yaw_ += MOUSE_SENSITIVITY * mouseMove.x_;
    pitch_ += MOUSE_SENSITIVITY * mouseMove.y_;
    pitch_ = Clamp(pitch_, -90.0f, 90.0f);

    // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero.
    cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));

    // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
    // Use the TranslateRelative() function to move relative to the node's orientation. Alternatively we could
    // multiply the desired direction with the node's orientation quaternion, and use just Translate()
    if (input->GetKeyDown('W'))
        cameraNode_->TranslateRelative(Vector3::FORWARD * MOVE_SPEED * timeStep);
    if (input->GetKeyDown('S'))
        cameraNode_->TranslateRelative(Vector3::BACK * MOVE_SPEED * timeStep);
    if (input->GetKeyDown('A'))
        cameraNode_->TranslateRelative(Vector3::LEFT * MOVE_SPEED * timeStep);
    if (input->GetKeyDown('D'))
        cameraNode_->TranslateRelative(Vector3::RIGHT * MOVE_SPEED * timeStep);
}

void StaticScene::SubscribeToEvents()
{
    // Subscribes HandleUpdate() method for processing update events
    SubscribeToEvent(E_UPDATE, HANDLER(StaticScene, HandleUpdate));
}

void StaticScene::HandleUpdate(StringHash eventType, VariantMap& eventData)
{
    // Event parameters are always defined inside a namespace corresponding to the event's name
    using namespace Update;

    // Take the frame time step, which is stored as a float
    float timeStep = eventData[P_TIMESTEP].GetFloat();

    // Move the camera, scale movement with time step
    MoveCamera(timeStep);
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * input_manager.cpp : Manage an input and interact with its GUI elements
 ****************************************************************************
 * Copyright (C) 2000-2005 the VideoLAN team
 * $Id: wxwidgets.cpp 15731 2006-05-25 14:43:53Z zorglub $
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "input_manager.hpp"
#include "dialogs_provider.hpp"
#include "qt4.hpp"

/**********************************************************************
 * InputManager implementation
 **********************************************************************/

InputManager::InputManager( QObject *parent, intf_thread_t *_p_intf) :
                           QObject( parent ), p_intf( _p_intf )
{
    p_input = NULL;
    /* Subscribe to updates */
    connect( DialogsProvider::getInstance( p_intf )->fixed_timer,
             SIGNAL( timeout() ), this, SLOT( update() ) );
}

InputManager::~InputManager()
{
}

void InputManager::setInput( input_thread_t *_p_input )
{
    p_input = _p_input;
    emit positionUpdated( 0.0,0,0 );
}

void InputManager::update()
{
    /// \todo Emit the signals only if it changed
    if( !p_input || p_input->b_die ) return;

    if( p_input->b_dead )
    {
        emit positionUpdated( 0.0, 0, 0 );
        emit navigationChanged( 0 );
        emit statusChanged( 0 ); // 0 = STOPPED, 1 = PAUSE, 2 = PLAY
    }

    /* Update position */
    mtime_t i_length, i_time;
    float f_pos;
    i_length = var_GetTime( p_input, "length" ) / 1000000;
    i_time = var_GetTime( p_input, "time") / 1000000;
    f_pos = var_GetFloat( p_input, "position" );
    emit positionUpdated( f_pos, i_time, i_length );

    /* Update disc status */
    vlc_value_t val;
    var_Change( p_input, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
    if( val.i_int > 0 )
    {
        vlc_value_t val;
        var_Change( p_input, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
    if( val.i_int > 0 )
        emit navigationChanged( 1 ); // 1 = chapter, 2 = title, 3 = NO
    else
        emit navigationChanged( 2 );
    }
    else
    {
        emit navigationChanged( 0 );
    }

    /* Update text */
    QString text;
    if( p_input->input.p_item->p_meta &&
        p_input->input.p_item->p_meta->psz_nowplaying &&
        *p_input->input.p_item->p_meta->psz_nowplaying )
    {
        text.sprintf( "%s - %s",
                  p_input->input.p_item->p_meta->psz_nowplaying,
                  p_input->input.p_item->psz_name );
    }
    else
    {
        text.sprintf( "%s", p_input->input.p_item->psz_name );
    }
    emit nameChanged( text );

}

void InputManager::sliderUpdate( float new_pos )
{
   if( p_input && !p_input->b_die && !p_input->b_dead )
        var_SetFloat( p_input, "position", new_pos );
}

/**********************************************************************
 * MainInputManager implementation. Wrap an input manager and
 * take care of updating the main playlist input
 **********************************************************************/
MainInputManager * MainInputManager::instance = NULL;

MainInputManager::MainInputManager( intf_thread_t *_p_intf ) : QObject(NULL),
                                                p_intf( _p_intf )
{
    p_input = NULL;
    im = new InputManager( this, p_intf );
    /* Get timer updates */
    connect( DialogsProvider::getInstance(p_intf)->fixed_timer,
             SIGNAL(timeout() ), this, SLOT( updateInput() ) );
    /* Warn our embedded IM about input changes */
    connect( this, SIGNAL( inputChanged( input_thread_t * ) ),
             im, SLOT( setInput( input_thread_t * ) ) );
}

void MainInputManager::updateInput()
{
    vlc_mutex_lock( &p_intf->change_lock );
    if( p_input && p_input->b_dead )
    {
        vlc_object_release( p_input );
        p_input = NULL;
        emit inputChanged( NULL );
    }

    if( !p_input )
    {
        playlist_t *p_playlist = (playlist_t *) vlc_object_find( p_intf,
                                        VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
        assert( p_playlist );
        PL_LOCK;
        p_input = p_playlist->p_input;
        if( p_input )
        {
            vlc_object_yield( p_input );
            emit inputChanged( p_input );
        }
        PL_UNLOCK;
        vlc_object_release( p_playlist );
    }
    vlc_mutex_unlock( &p_intf->change_lock );
}
<commit_msg>qt4/input_manager.cpp: compile fix<commit_after>/*****************************************************************************
 * input_manager.cpp : Manage an input and interact with its GUI elements
 ****************************************************************************
 * Copyright (C) 2000-2005 the VideoLAN team
 * $Id: wxwidgets.cpp 15731 2006-05-25 14:43:53Z zorglub $
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include <assert.h>

#include "input_manager.hpp"
#include "dialogs_provider.hpp"
#include "qt4.hpp"

/**********************************************************************
 * InputManager implementation
 **********************************************************************/

InputManager::InputManager( QObject *parent, intf_thread_t *_p_intf) :
                           QObject( parent ), p_intf( _p_intf )
{
    p_input = NULL;
    /* Subscribe to updates */
    connect( DialogsProvider::getInstance( p_intf )->fixed_timer,
             SIGNAL( timeout() ), this, SLOT( update() ) );
}

InputManager::~InputManager()
{
}

void InputManager::setInput( input_thread_t *_p_input )
{
    p_input = _p_input;
    emit positionUpdated( 0.0,0,0 );
}

void InputManager::update()
{
    /// \todo Emit the signals only if it changed
    if( !p_input || p_input->b_die ) return;

    if( p_input->b_dead )
    {
        emit positionUpdated( 0.0, 0, 0 );
        emit navigationChanged( 0 );
        emit statusChanged( 0 ); // 0 = STOPPED, 1 = PAUSE, 2 = PLAY
    }

    /* Update position */
    mtime_t i_length, i_time;
    float f_pos;
    i_length = var_GetTime( p_input, "length" ) / 1000000;
    i_time = var_GetTime( p_input, "time") / 1000000;
    f_pos = var_GetFloat( p_input, "position" );
    emit positionUpdated( f_pos, i_time, i_length );

    /* Update disc status */
    vlc_value_t val;
    var_Change( p_input, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
    if( val.i_int > 0 )
    {
        vlc_value_t val;
        var_Change( p_input, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
    if( val.i_int > 0 )
        emit navigationChanged( 1 ); // 1 = chapter, 2 = title, 3 = NO
    else
        emit navigationChanged( 2 );
    }
    else
    {
        emit navigationChanged( 0 );
    }

    /* Update text */
    QString text;
    if( p_input->input.p_item->p_meta &&
        p_input->input.p_item->p_meta->psz_nowplaying &&
        *p_input->input.p_item->p_meta->psz_nowplaying )
    {
        text.sprintf( "%s - %s",
                  p_input->input.p_item->p_meta->psz_nowplaying,
                  p_input->input.p_item->psz_name );
    }
    else
    {
        text.sprintf( "%s", p_input->input.p_item->psz_name );
    }
    emit nameChanged( text );

}

void InputManager::sliderUpdate( float new_pos )
{
   if( p_input && !p_input->b_die && !p_input->b_dead )
        var_SetFloat( p_input, "position", new_pos );
}

/**********************************************************************
 * MainInputManager implementation. Wrap an input manager and
 * take care of updating the main playlist input
 **********************************************************************/
MainInputManager * MainInputManager::instance = NULL;

MainInputManager::MainInputManager( intf_thread_t *_p_intf ) : QObject(NULL),
                                                p_intf( _p_intf )
{
    p_input = NULL;
    im = new InputManager( this, p_intf );
    /* Get timer updates */
    connect( DialogsProvider::getInstance(p_intf)->fixed_timer,
             SIGNAL(timeout() ), this, SLOT( updateInput() ) );
    /* Warn our embedded IM about input changes */
    connect( this, SIGNAL( inputChanged( input_thread_t * ) ),
             im, SLOT( setInput( input_thread_t * ) ) );
}

void MainInputManager::updateInput()
{
    vlc_mutex_lock( &p_intf->change_lock );
    if( p_input && p_input->b_dead )
    {
        vlc_object_release( p_input );
        p_input = NULL;
        emit inputChanged( NULL );
    }

    if( !p_input )
    {
        playlist_t *p_playlist = (playlist_t *) vlc_object_find( p_intf,
                                        VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
        assert( p_playlist );
        PL_LOCK;
        p_input = p_playlist->p_input;
        if( p_input )
        {
            vlc_object_yield( p_input );
            emit inputChanged( p_input );
        }
        PL_UNLOCK;
        vlc_object_release( p_playlist );
    }
    vlc_mutex_unlock( &p_intf->change_lock );
}
<|endoftext|>


<commit_before>/*
 * Reference: http://www.indiana.edu/~iulg/trm/
 *
 * Instruction set
 * ===============
 * Add 1 to Rn   : 1^n#
 *   Rn.push(1)
 *
 * Add # to Rn   : 1^n##
 *   Rn.push(#)
 *
 * Go forward n  : 1^n###
 *   pc += n
 * Go backward n : 1^n####
 *   pc -= n
 *
 * Cases on Rn   : 1^n#####
 *   if Rn.empty(), pc += 1
 *   if Rn.pop() == 1, pc += 2
 *   if Rn.pop() == #, pc += 3
 */

#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <queue>

using namespace std;

enum Code {
    O,
    H
};

enum Op {
    Add1 = 1,
    Add0 = 2,
    Inc = 3,
    Dec = 4,
    Case = 5
};

struct Instr {
    Op o;
    int n;
};

vector< Instr > program;
map< int, queue<Code> > mem;

void push(int i, Code c) {
    mem[i].push(c);
}

int cases(int i) {
    queue<Code>& Rn = mem[i];
    if (Rn.empty()) {
        return 1;
    } else if (Rn.front() == O) {
        Rn.pop();
        return 2;
    } else if (Rn.front() == H) {
        Rn.pop();
        return 3;
    }
}

void print_registers() {
    for (map< int, queue<Code> >::iterator it=mem.begin(); it != mem.end(); it++) {
        cout << "R" << it->first << ":";
        queue<Code> content = it->second;
        while (content.empty() == false) {
            Code c = content.front();
            content.pop();
            if (c == O) {
                cout << "1";
            } else {
                cout << "#";
            }
        }
        cout << endl;
    }
}

string op2string(Op o) {
    switch (o) {
        case Add1: return "Add1";
        case Add0: return "Add#";
        case Inc:  return "Inc";
        case Dec:  return "Dec";
        case Case: return "Case";
    }
}

string instr2string(Instr in) {
     return op2string(in.o) + " " + to_string(in.n);
}

void print_program() {
    for ( auto &p : program) {
        cout << instr2string(p) << endl;
    }
}

void eval(int pc) {
    while (pc < program.size()) {
        Instr in = program[pc];
        //cout << "pc:" + to_string(pc) + " " + "instr: " + instr2string(in) << endl;
        switch (in.o) {
            case Add1:
                push(in.n, O);
                pc++;
                break;

            case Add0:
                push(in.n, H);
                pc++;
                break;

            case Inc:
                pc += in.n;
                break;

            case Dec:
                pc -= in.n;
                break;

            case Case:
                pc += cases(in.n);
                break;
        }
    }
}

void load_program() {
    cin.sync_with_stdio(false);

    char curr;

    int n = 0;
    int c = 0;

    while (cin >> curr) {
        if (curr == '1') {
            if (c > 0) {
                n = 0;
                c = 0;
            }
            n++;
        } else if (curr == '#') {
            c++;
            if (c == 1) {
                program.push_back({Add1, n});
            } else {
                program.back().o = (Op)c;
            }
        }
    }
}

int main() {
    load_program();
    //print_program();
    eval(0);
    print_registers();
    return 0;
}
<commit_msg>rename member o to op<commit_after>/*
 * Reference: http://www.indiana.edu/~iulg/trm/
 *
 * Instruction set
 * ===============
 * Add 1 to Rn   : 1^n#
 *   Rn.push(1)
 *
 * Add # to Rn   : 1^n##
 *   Rn.push(#)
 *
 * Go forward n  : 1^n###
 *   pc += n
 * Go backward n : 1^n####
 *   pc -= n
 *
 * Cases on Rn   : 1^n#####
 *   if Rn.empty(), pc += 1
 *   if Rn.pop() == 1, pc += 2
 *   if Rn.pop() == #, pc += 3
 */

#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <queue>

using namespace std;

enum Code {
    O,
    H
};

enum Op {
    Add1 = 1,
    Add0 = 2,
    Inc = 3,
    Dec = 4,
    Case = 5
};

struct Instr {
    Op op;
    int n;
};

vector< Instr > program;
map< int, queue<Code> > mem;

void push(int i, Code c) {
    mem[i].push(c);
}

int cases(int i) {
    queue<Code>& Rn = mem[i];
    if (Rn.empty()) {
        return 1;
    } else if (Rn.front() == O) {
        Rn.pop();
        return 2;
    } else if (Rn.front() == H) {
        Rn.pop();
        return 3;
    }
}

void print_registers() {
    for (map< int, queue<Code> >::iterator it=mem.begin(); it != mem.end(); it++) {
        cout << "R" << it->first << ":";
        queue<Code> content = it->second;
        while (content.empty() == false) {
            Code c = content.front();
            content.pop();
            if (c == O) {
                cout << "1";
            } else {
                cout << "#";
            }
        }
        cout << endl;
    }
}

string op2string(Op o) {
    switch (o) {
        case Add1: return "Add1";
        case Add0: return "Add#";
        case Inc:  return "Inc";
        case Dec:  return "Dec";
        case Case: return "Case";
    }
}

string instr2string(Instr in) {
     return op2string(in.op) + " " + to_string(in.n);
}

void print_program() {
    for ( auto &p : program) {
        cout << instr2string(p) << endl;
    }
}

void eval(int pc) {
    while (pc < program.size()) {
        Instr in = program[pc];
        //cout << "pc:" + to_string(pc) + " " + "instr: " + instr2string(in) << endl;
        switch (in.op) {
            case Add1:
                push(in.n, O);
                pc++;
                break;

            case Add0:
                push(in.n, H);
                pc++;
                break;

            case Inc:
                pc += in.n;
                break;

            case Dec:
                pc -= in.n;
                break;

            case Case:
                pc += cases(in.n);
                break;
        }
    }
}

void load_program() {
    cin.sync_with_stdio(false);

    char curr;

    int n = 0;
    int c = 0;

    while (cin >> curr) {
        if (curr == '1') {
            if (c > 0) {
                n = 0;
                c = 0;
            }
            n++;
        } else if (curr == '#') {
            c++;
            if (c == 1) {
                program.push_back({Add1, n});
            } else {
                program.back().op = (Op)c;
            }
        }
    }
}

int main() {
    load_program();
    //print_program();
    eval(0);
    print_registers();
    return 0;
}
<|endoftext|>


<commit_before>/*
    kabcexport.cpp - Export Contacts to Address Book Wizard for Kopete

    Copyright (c) 2005 by Will Stephenson        <will@stevello.free-online.co.uk>
    Resource selector taken from KRES::SelectDialog
    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
    Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>

    Kopete    (c) 2002-2005 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <qpushbutton.h>
#include <q3listbox.h>
#include <q3listview.h>
#include <q3ptrlist.h>
#include <qmap.h>

#include <klocale.h>
#include <kmessagebox.h>
#include <kabc/addressee.h>
#include <kabc/addressbook.h>
#include <kabc/phonenumber.h>
#include <kabc/picture.h>
#include <kabc/resource.h>
#include <kabc/stdaddressbook.h>

#include <kabcpersistence.h>
#include <kopetecontact.h>
#include <kopetecontactlist.h>
#include <kopetecontactproperty.h>
#include <kopeteglobal.h>
#include <kopetemetacontact.h>

#include "kabcpersistence.h"

#include "kabcexport.h"

class ContactLVI : public Q3CheckListItem
{
	public:
		ContactLVI ( Kopete::MetaContact * mc, Q3ListView * parent, const QString & text, Type tt = RadioButtonController ) : Q3CheckListItem( parent, text, tt ), mc( mc )
		{	}
		Kopete::MetaContact * mc;
		QString uid;
};

// ctor populates the resource list and contact list, and enables the next button on the first page
KabcExportWizard::KabcExportWizard( QWidget *parent, const char *name )
	: KabcExportWizard_Base( parent, name )
{
	connect( m_addrBooks, SIGNAL( selectionChanged( Q3ListBoxItem * ) ), SLOT( slotResourceSelectionChanged( Q3ListBoxItem * ) ) );

	connect( m_btnSelectAll, SIGNAL( clicked() ), SLOT( slotSelectAll() ) );
	connect( m_btnDeselectAll, SIGNAL( clicked() ), SLOT( slotDeselectAll() ) );

	// fill resource selector
	m_addressBook = Kopete::KABCPersistence::self()->addressBook();

	Q3PtrList<KABC::Resource> kabcResources = m_addressBook->resources();

	Q3PtrListIterator<KABC::Resource> resIt( kabcResources );
	KABC::Resource *resource;

	uint counter = 0;
	while ( ( resource = resIt.current() ) != 0 )
	{
		++resIt;
		if ( !resource->readOnly() )
		{
			m_resourceMap.insert( counter, resource );
			m_addrBooks->insertItem( resource->resourceName() );
			counter++;
		}
	}
	setNextEnabled( Q3Wizard::page( 0 ), false );
	setFinishEnabled( Q3Wizard::page( 1 ), true );
	// if there were no writable address books, tell the user
	if ( counter == 0 )
	{
		m_addrBooks->insertItem( i18n( "No writeable addressbook resource found." ) );
		m_addrBooks->insertItem( i18n( "Add or enable one using the KDE Control Center." ) );
		m_addrBooks->setEnabled( false );
	}

	if ( m_addrBooks->count() == 1 )
		m_addrBooks->setSelected( 0, true );

	// fill contact list
	QList<Kopete::MetaContact*> contacts = Kopete::ContactList::self()->metaContacts();
	QList<Kopete::MetaContact*>::iterator it, itEnd = contacts.end();
	counter = 0;
	QString alreadyIn = i18n( " (already in address book)" );
	for ( it = contacts.begin(); it != itEnd; ++it)
	{
		Kopete::MetaContact* mc = (*it);
		m_contactMap.insert( counter, mc );
		Q3CheckListItem * lvi = new ContactLVI( mc, m_contactList,
				mc->displayName(), Q3CheckListItem::CheckBox );
		lvi->setOn( false );
		if ( mc->metaContactId().contains(':') )
		{
			lvi->setOn( true );
			lvi->setEnabled( true );
		}
		else
			lvi->setText( 0, lvi->text( 0 ) + alreadyIn );
	}
}

KabcExportWizard::~KabcExportWizard()
{

}

void KabcExportWizard::slotDeselectAll()
{
	Q3ListViewItemIterator it( m_contactList );
	while ( it.current() )
	{
		ContactLVI *item = static_cast<ContactLVI *>( it.current() );
		item->setOn( false );
		++it;
	}
}

void KabcExportWizard::slotSelectAll()
{
	Q3ListViewItemIterator it( m_contactList );
	while ( it.current() )
	{
		ContactLVI *item = static_cast<ContactLVI *>( it.current() );
		++it;
		if ( !item->isEnabled() )
			continue;
		item->setOn( true );
	}
}

void KabcExportWizard::slotResourceSelectionChanged( Q3ListBoxItem * lbi )
{
	setNextEnabled( Q3Wizard::page( 0 ), lbi->isSelected() );
}

// accept runs the export algorithm
void KabcExportWizard::accept()
{
	// first add an addressee to the selected resource
	// then set the metacontactId of each MC to that of the new addressee
	KABC::Resource * selectedResource =
			m_resourceMap[ ( m_addrBooks->index( m_addrBooks->selectedItem() ) ) ];
	// for each item checked
	{
		Q3ListViewItemIterator it( m_contactList );
		while ( it.current() )
		{
			ContactLVI *item = static_cast<ContactLVI *>( it.current() );
			// if it is checked and enabled
			if ( item->isEnabled() && item->isOn() )
			{
				KABC::Addressee addr;
				addr = m_addressBook->findByUid( item->mc->metaContactId() );
				if ( addr.isEmpty() ) // unassociated contact
				{
					kdDebug( 14000 ) << "creating addressee " << item->mc->displayName() << " in address book " << selectedResource->resourceName() << endl;
					// create a new addressee in the selected resource
					addr.setResource( selectedResource );

					// set name
					QList<Kopete::Contact*> contacts = item->mc->contacts();
					if ( contacts.count() == 1 )
					{
						Kopete::ContactProperty prop;
						prop = contacts.first()->property(
								Kopete::Global::Properties::self()->fullName() );
						if ( prop.isNull() )
							addr.setNameFromString( item->mc->displayName() );
						else
							addr.setNameFromString(  prop.value().toString() );
					}
					else
						addr.setNameFromString( item->mc->displayName() );

					// set details
					exportDetails( item->mc, addr );
					m_addressBook->insertAddressee( addr );
					// set the metacontact's id to that of the new addressee
					// - this causes the addressbook to be written by libkopete
					item->mc->setMetaContactId( addr.uid() );
				}
				else
				{
					exportDetails( item->mc, addr );
					m_addressBook->insertAddressee( addr );
				}
			}
			++it;
		}
	}
	// request a write in case we only changed details on existing linked addressee
	Kopete::KABCPersistence::self()->writeAddressBook( selectedResource );
	QDialog::accept();
}

void KabcExportWizard::exportDetails( Kopete::MetaContact * mc, KABC::Addressee & addr )
{
	QList<Kopete::Contact*> contacts = mc->contacts();
	QList<Kopete::Contact*>::iterator cit, citEnd = contacts.begin();
	for( cit = contacts.begin(); cit != citEnd; ++cit )
	{
		Kopete::ContactProperty prop;
		prop = (*cit)->property( Kopete::Global::Properties::self()->emailAddress() );
		if ( !prop.isNull() )
		{
			addr.insertEmail( prop.value().toString() );
		}
		prop = (*cit)->property( Kopete::Global::Properties::self()->privatePhone() );
		if ( !prop.isNull() )
		{
			addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Home ) );
		}
		prop = (*cit)->property( Kopete::Global::Properties::self()->workPhone() );
		if ( !prop.isNull() )
		{
			addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Work ) );
		}
		prop = (*cit)->property( Kopete::Global::Properties::self()->privateMobilePhone() );
		if ( !prop.isNull() )
		{
			addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Cell ) );
		}

	}

	QImage photo = mc->photo();
	if ( !photo.isNull() )
		addr.setPhoto( KABC::Picture( photo ) );
}

#include "kabcexport.moc"
<commit_msg>compile (port to kabc changes)<commit_after>/*
    kabcexport.cpp - Export Contacts to Address Book Wizard for Kopete

    Copyright (c) 2005 by Will Stephenson        <will@stevello.free-online.co.uk>
    Resource selector taken from KRES::SelectDialog
    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
    Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>

    Kopete    (c) 2002-2005 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <qpushbutton.h>
#include <q3listbox.h>
#include <q3listview.h>
#include <qmap.h>

#include <klocale.h>
#include <kmessagebox.h>
#include <kabc/addressee.h>
#include <kabc/addressbook.h>
#include <kabc/phonenumber.h>
#include <kabc/picture.h>
#include <kabc/resource.h>
#include <kabc/stdaddressbook.h>

#include <kabcpersistence.h>
#include <kopetecontact.h>
#include <kopetecontactlist.h>
#include <kopetecontactproperty.h>
#include <kopeteglobal.h>
#include <kopetemetacontact.h>

#include "kabcpersistence.h"

#include "kabcexport.h"

class ContactLVI : public Q3CheckListItem
{
	public:
		ContactLVI ( Kopete::MetaContact * mc, Q3ListView * parent, const QString & text, Type tt = RadioButtonController ) : Q3CheckListItem( parent, text, tt ), mc( mc )
		{	}
		Kopete::MetaContact * mc;
		QString uid;
};

// ctor populates the resource list and contact list, and enables the next button on the first page
KabcExportWizard::KabcExportWizard( QWidget *parent, const char *name )
	: KabcExportWizard_Base( parent, name )
{
	connect( m_addrBooks, SIGNAL( selectionChanged( Q3ListBoxItem * ) ), SLOT( slotResourceSelectionChanged( Q3ListBoxItem * ) ) );

	connect( m_btnSelectAll, SIGNAL( clicked() ), SLOT( slotSelectAll() ) );
	connect( m_btnDeselectAll, SIGNAL( clicked() ), SLOT( slotDeselectAll() ) );

	// fill resource selector
	m_addressBook = Kopete::KABCPersistence::self()->addressBook();

	QList<KABC::Resource*> kabcResources = m_addressBook->resources();

	QListIterator<KABC::Resource*> resIt( kabcResources );
	KABC::Resource *resource;

	uint counter = 0;
	while ( resIt.hasNext() )
	{
		resource = resIt.next();
		if ( !resource->readOnly() )
		{
			m_resourceMap.insert( counter, resource );
			m_addrBooks->insertItem( resource->resourceName() );
			counter++;
		}
	}
	setNextEnabled( Q3Wizard::page( 0 ), false );
	setFinishEnabled( Q3Wizard::page( 1 ), true );
	// if there were no writable address books, tell the user
	if ( counter == 0 )
	{
		m_addrBooks->insertItem( i18n( "No writeable addressbook resource found." ) );
		m_addrBooks->insertItem( i18n( "Add or enable one using the KDE Control Center." ) );
		m_addrBooks->setEnabled( false );
	}

	if ( m_addrBooks->count() == 1 )
		m_addrBooks->setSelected( 0, true );

	// fill contact list
	QList<Kopete::MetaContact*> contacts = Kopete::ContactList::self()->metaContacts();
	QList<Kopete::MetaContact*>::iterator it, itEnd = contacts.end();
	counter = 0;
	QString alreadyIn = i18n( " (already in address book)" );
	for ( it = contacts.begin(); it != itEnd; ++it)
	{
		Kopete::MetaContact* mc = (*it);
		m_contactMap.insert( counter, mc );
		Q3CheckListItem * lvi = new ContactLVI( mc, m_contactList,
				mc->displayName(), Q3CheckListItem::CheckBox );
		lvi->setOn( false );
		if ( mc->metaContactId().contains(':') )
		{
			lvi->setOn( true );
			lvi->setEnabled( true );
		}
		else
			lvi->setText( 0, lvi->text( 0 ) + alreadyIn );
	}
}

KabcExportWizard::~KabcExportWizard()
{

}

void KabcExportWizard::slotDeselectAll()
{
	Q3ListViewItemIterator it( m_contactList );
	while ( it.current() )
	{
		ContactLVI *item = static_cast<ContactLVI *>( it.current() );
		item->setOn( false );
		++it;
	}
}

void KabcExportWizard::slotSelectAll()
{
	Q3ListViewItemIterator it( m_contactList );
	while ( it.current() )
	{
		ContactLVI *item = static_cast<ContactLVI *>( it.current() );
		++it;
		if ( !item->isEnabled() )
			continue;
		item->setOn( true );
	}
}

void KabcExportWizard::slotResourceSelectionChanged( Q3ListBoxItem * lbi )
{
	setNextEnabled( Q3Wizard::page( 0 ), lbi->isSelected() );
}

// accept runs the export algorithm
void KabcExportWizard::accept()
{
	// first add an addressee to the selected resource
	// then set the metacontactId of each MC to that of the new addressee
	KABC::Resource * selectedResource =
			m_resourceMap[ ( m_addrBooks->index( m_addrBooks->selectedItem() ) ) ];
	// for each item checked
	{
		Q3ListViewItemIterator it( m_contactList );
		while ( it.current() )
		{
			ContactLVI *item = static_cast<ContactLVI *>( it.current() );
			// if it is checked and enabled
			if ( item->isEnabled() && item->isOn() )
			{
				KABC::Addressee addr;
				addr = m_addressBook->findByUid( item->mc->metaContactId() );
				if ( addr.isEmpty() ) // unassociated contact
				{
					kdDebug( 14000 ) << "creating addressee " << item->mc->displayName() << " in address book " << selectedResource->resourceName() << endl;
					// create a new addressee in the selected resource
					addr.setResource( selectedResource );

					// set name
					QList<Kopete::Contact*> contacts = item->mc->contacts();
					if ( contacts.count() == 1 )
					{
						Kopete::ContactProperty prop;
						prop = contacts.first()->property(
								Kopete::Global::Properties::self()->fullName() );
						if ( prop.isNull() )
							addr.setNameFromString( item->mc->displayName() );
						else
							addr.setNameFromString(  prop.value().toString() );
					}
					else
						addr.setNameFromString( item->mc->displayName() );

					// set details
					exportDetails( item->mc, addr );
					m_addressBook->insertAddressee( addr );
					// set the metacontact's id to that of the new addressee
					// - this causes the addressbook to be written by libkopete
					item->mc->setMetaContactId( addr.uid() );
				}
				else
				{
					exportDetails( item->mc, addr );
					m_addressBook->insertAddressee( addr );
				}
			}
			++it;
		}
	}
	// request a write in case we only changed details on existing linked addressee
	Kopete::KABCPersistence::self()->writeAddressBook( selectedResource );
	QDialog::accept();
}

void KabcExportWizard::exportDetails( Kopete::MetaContact * mc, KABC::Addressee & addr )
{
	QList<Kopete::Contact*> contacts = mc->contacts();
	QList<Kopete::Contact*>::iterator cit, citEnd = contacts.begin();
	for( cit = contacts.begin(); cit != citEnd; ++cit )
	{
		Kopete::ContactProperty prop;
		prop = (*cit)->property( Kopete::Global::Properties::self()->emailAddress() );
		if ( !prop.isNull() )
		{
			addr.insertEmail( prop.value().toString() );
		}
		prop = (*cit)->property( Kopete::Global::Properties::self()->privatePhone() );
		if ( !prop.isNull() )
		{
			addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Home ) );
		}
		prop = (*cit)->property( Kopete::Global::Properties::self()->workPhone() );
		if ( !prop.isNull() )
		{
			addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Work ) );
		}
		prop = (*cit)->property( Kopete::Global::Properties::self()->privateMobilePhone() );
		if ( !prop.isNull() )
		{
			addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Cell ) );
		}

	}

	QImage photo = mc->photo();
	if ( !photo.isNull() )
		addr.setPhoto( KABC::Picture( photo ) );
}

#include "kabcexport.moc"
<|endoftext|>


<commit_before>// Copyright © 2016 by Donald King <chronos@chronos-tachyon.net>
// Available under the MIT License. See LICENSE for details.

#include "gtest/gtest.h"

#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <array>
#include <condition_variable>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <thread>
#include <vector>

#include "base/cleanup.h"
#include "base/fd.h"
#include "base/logging.h"
#include "base/result_testing.h"
#include "io/util.h"

static std::unique_lock<std::mutex> acquire_lock(std::mutex& mu) {
  return std::unique_lock<std::mutex>(mu);
}

static void sleep_ms(unsigned int ms) {
  struct timespec tv;
  ::bzero(&tv, sizeof(tv));
  tv.tv_sec = (ms / 1000);
  tv.tv_nsec = (ms % 1000) * 1000000;
  ::nanosleep(&tv, nullptr);
}

static event::Manager make_manager() {
  event::Manager m;
  event::ManagerOptions mo;
  mo.set_num_pollers(0, 1);
  mo.dispatcher().set_type(event::DispatcherType::async_dispatcher);
  event::new_manager(&m, mo).assert_ok();
  return m;
}

TEST(Copy, StringReaderStringWriter) {
  std::string in = "0123456789ab";
  std::string out;
  io::Reader r = io::stringreader(in);
  io::Writer w = io::stringwriter(&out);

  event::Task task;
  std::size_t n;
  io::copy(&task, &n, w, r);
  event::wait_all({w.manager(), r.manager()}, {&task});
  EXPECT_OK(task.result());
  EXPECT_EQ(12U, n);
  EXPECT_EQ(out, in);
}

static void TestFileFileCopy(io::Options o) {
  std::string srcpath, dstpath;
  base::FD srcfd, dstfd;

  ASSERT_OK(base::make_tempfile(&srcpath, &srcfd, "mojo-io-util-test.XXXXXX"));
  auto cleanup0 = base::cleanup([srcpath] { ::unlink(srcpath.c_str()); });

  ASSERT_OK(base::make_tempfile(&dstpath, &dstfd, "mojo-io-util-test.XXXXXX"));
  auto cleanup1 = base::cleanup([dstpath] { ::unlink(dstpath.c_str()); });

  constexpr std::size_t N = 4096;

  std::vector<char> in;
  in.assign(N, 'A');
  in.resize(N, 'B');

  io::Reader r;
  io::Writer w;
  event::Task task;
  std::size_t n = 42;

  w = io::fdwriter(srcfd, o);
  w.write(&task, &n, in.data(), N);
  while (!task.is_finished()) {
    EXPECT_OK(o.manager().donate(false));
  }
  EXPECT_OK(task.result());
  EXPECT_EQ(N, n);

  {
    auto pair = srcfd->acquire_fd();
    EXPECT_EQ(0, ::lseek(pair.first, 0, SEEK_SET));
  }

  r = io::fdreader(srcfd, o);
  w = io::fdwriter(dstfd, o);
  task.reset();
  io::copy_n(&task, &n, N, w, r);
  while (!task.is_finished()) {
    EXPECT_OK(o.manager().donate(false));
  }
  EXPECT_OK(task.result());
  EXPECT_EQ(N, n);

  {
    auto pair = dstfd->acquire_fd();
    EXPECT_EQ(0, ::lseek(pair.first, 0, SEEK_SET));
  }

  std::vector<char> out;
  out.resize(2 * N);
  r = io::fdreader(dstfd, o);
  task.reset();
  r.read(&task, out.data(), &n, 1, out.size());
  while (!task.is_finished()) {
    EXPECT_OK(o.manager().donate(false));
  }
  EXPECT_OK(task.result());
  EXPECT_EQ(N, n);
  out.resize(n);

  EXPECT_EQ(std::string(in.begin(), in.end()),
            std::string(out.begin(), out.end()));
}

TEST(Copy, FileFileLoop512) {
  io::Options o;
  o.set_manager(make_manager());
  o.set_block_size(512);
  o.set_transfer_mode(io::TransferMode::read_write);
  TestFileFileCopy(o);
}

TEST(Copy, FileFileLoop4K) {
  io::Options o;
  o.set_manager(make_manager());
  o.set_block_size(4096);
  o.set_transfer_mode(io::TransferMode::read_write);
  TestFileFileCopy(o);
}

TEST(Copy, FileFileSendfile) {
  io::Options o;
  o.set_manager(make_manager());
  o.set_transfer_mode(io::TransferMode::sendfile);
  TestFileFileCopy(o);
}

TEST(Copy, FileFileSplice) {
  io::Options o;
  o.set_manager(make_manager());
  o.set_transfer_mode(io::TransferMode::splice);
  TestFileFileCopy(o);
}

TEST(Copy, HeterogenousManagers) {
  event::Manager rm = make_manager();
  event::Manager wm = make_manager();
  io::Options ro, wo;
  ro.set_manager(rm);
  ro.set_block_size(4096);
  ro.set_transfer_mode(io::TransferMode::read_write);
  wo.set_manager(wm);
  wo.set_block_size(4096);
  wo.set_transfer_mode(io::TransferMode::read_write);

  base::SocketPair rdpair, wrpair;
  ASSERT_OK(base::make_socketpair(&rdpair, AF_UNIX, SOCK_STREAM, 0));
  ASSERT_OK(base::make_socketpair(&wrpair, AF_UNIX, SOCK_STREAM, 0));
  ASSERT_OK(base::set_blocking(rdpair.left, true));
  ASSERT_OK(base::set_blocking(wrpair.right, true));
  ASSERT_OK(base::shutdown(rdpair.right, SHUT_WR));
  ASSERT_OK(base::shutdown(wrpair.right, SHUT_WR));

  LOG(INFO) << "sockets are ready";

  std::mutex mu;
  std::size_t blocks_transferred = 0;

  std::thread t1([&rdpair] {
    std::vector<char> buf;
    buf.assign(4096, 'A');
    for (std::size_t i = 0; i < 16; ++i) {
      sleep_ms(1);
      LOG(INFO) << "writing block #" << i;
      auto r =
          base::write_exactly(rdpair.left, buf.data(), buf.size(), "rdpair");
      EXPECT_OK(r);
      if (!r) break;
    }
    sleep_ms(1);
    LOG(INFO) << "sending EOF on rdpair";
    EXPECT_OK(rdpair.left->close());
  });

  std::thread t2([&wrpair, &mu, &blocks_transferred] {
    std::vector<char> buf;
    buf.resize(1024);
    std::size_t i = 0;
    base::Result r;
    while (true) {
      sleep_ms(1);
      LOG(INFO) << "reading block #" << (i / 4) << "." << ((i % 4) * 25);
      r = base::read_exactly(wrpair.right, buf.data(), buf.size(), "wrpair");
      if (!r) break;
      auto lock = acquire_lock(mu);
      ++blocks_transferred;
      ++i;
    }
    EXPECT_EOF(r);
    LOG(INFO) << "got EOF on wrpair";
  });

  io::Reader r = io::fdreader(rdpair.right, ro);
  io::Writer w = io::fdwriter(wrpair.left, wo);

  event::Task task;
  std::size_t n;
  LOG(INFO) << "starting copy";
  io::copy(&task, &n, w, r);
  LOG(INFO) << "waiting on copy";
  event::wait_all({rm, wm}, {&task});
  LOG(INFO) << "copy complete";
  EXPECT_OK(task.result());
  LOG(INFO) << "sending EOF on wrpair";
  EXPECT_OK(wrpair.left->close());

  LOG(INFO) << "joining threads";
  t1.join();
  t2.join();
  EXPECT_EQ(4U * 16U, blocks_transferred);
}

static void init() __attribute__((constructor));
static void init() { base::log_stderr_set_level(VLOG_LEVEL(6)); }
<commit_msg>Flush logs at the end of each test, to make the output a little more predictable at V=6.<commit_after>// Copyright © 2016 by Donald King <chronos@chronos-tachyon.net>
// Available under the MIT License. See LICENSE for details.

#include "gtest/gtest.h"

#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <array>
#include <condition_variable>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <thread>
#include <vector>

#include "base/cleanup.h"
#include "base/fd.h"
#include "base/logging.h"
#include "base/result_testing.h"
#include "io/util.h"

static std::unique_lock<std::mutex> acquire_lock(std::mutex& mu) {
  return std::unique_lock<std::mutex>(mu);
}

static void sleep_ms(unsigned int ms) {
  struct timespec tv;
  ::bzero(&tv, sizeof(tv));
  tv.tv_sec = (ms / 1000);
  tv.tv_nsec = (ms % 1000) * 1000000;
  ::nanosleep(&tv, nullptr);
}

static event::Manager make_manager() {
  event::Manager m;
  event::ManagerOptions mo;
  mo.set_num_pollers(0, 1);
  mo.dispatcher().set_type(event::DispatcherType::async_dispatcher);
  event::new_manager(&m, mo).assert_ok();
  return m;
}

TEST(Copy, StringReaderStringWriter) {
  std::string in = "0123456789ab";
  std::string out;
  io::Reader r = io::stringreader(in);
  io::Writer w = io::stringwriter(&out);

  event::Task task;
  std::size_t n;
  io::copy(&task, &n, w, r);
  event::wait_all({w.manager(), r.manager()}, {&task});
  EXPECT_OK(task.result());
  EXPECT_EQ(12U, n);
  EXPECT_EQ(out, in);

  base::log_flush();
}

static void TestFileFileCopy(io::Options o) {
  std::string srcpath, dstpath;
  base::FD srcfd, dstfd;

  ASSERT_OK(base::make_tempfile(&srcpath, &srcfd, "mojo-io-util-test.XXXXXX"));
  auto cleanup0 = base::cleanup([srcpath] { ::unlink(srcpath.c_str()); });

  ASSERT_OK(base::make_tempfile(&dstpath, &dstfd, "mojo-io-util-test.XXXXXX"));
  auto cleanup1 = base::cleanup([dstpath] { ::unlink(dstpath.c_str()); });

  constexpr std::size_t N = 4096;

  std::vector<char> in;
  in.assign(N, 'A');
  in.resize(N, 'B');

  io::Reader r;
  io::Writer w;
  event::Task task;
  std::size_t n = 42;

  w = io::fdwriter(srcfd, o);
  w.write(&task, &n, in.data(), N);
  while (!task.is_finished()) {
    EXPECT_OK(o.manager().donate(false));
  }
  EXPECT_OK(task.result());
  EXPECT_EQ(N, n);

  {
    auto pair = srcfd->acquire_fd();
    EXPECT_EQ(0, ::lseek(pair.first, 0, SEEK_SET));
  }

  r = io::fdreader(srcfd, o);
  w = io::fdwriter(dstfd, o);
  task.reset();
  io::copy_n(&task, &n, N, w, r);
  while (!task.is_finished()) {
    EXPECT_OK(o.manager().donate(false));
  }
  EXPECT_OK(task.result());
  EXPECT_EQ(N, n);

  {
    auto pair = dstfd->acquire_fd();
    EXPECT_EQ(0, ::lseek(pair.first, 0, SEEK_SET));
  }

  std::vector<char> out;
  out.resize(2 * N);
  r = io::fdreader(dstfd, o);
  task.reset();
  r.read(&task, out.data(), &n, 1, out.size());
  while (!task.is_finished()) {
    EXPECT_OK(o.manager().donate(false));
  }
  EXPECT_OK(task.result());
  EXPECT_EQ(N, n);
  out.resize(n);

  EXPECT_EQ(std::string(in.begin(), in.end()),
            std::string(out.begin(), out.end()));

  base::log_flush();
}

TEST(Copy, FileFileLoop512) {
  io::Options o;
  o.set_manager(make_manager());
  o.set_block_size(512);
  o.set_transfer_mode(io::TransferMode::read_write);
  TestFileFileCopy(o);
}

TEST(Copy, FileFileLoop4K) {
  io::Options o;
  o.set_manager(make_manager());
  o.set_block_size(4096);
  o.set_transfer_mode(io::TransferMode::read_write);
  TestFileFileCopy(o);
}

TEST(Copy, FileFileSendfile) {
  io::Options o;
  o.set_manager(make_manager());
  o.set_transfer_mode(io::TransferMode::sendfile);
  TestFileFileCopy(o);
}

TEST(Copy, FileFileSplice) {
  io::Options o;
  o.set_manager(make_manager());
  o.set_transfer_mode(io::TransferMode::splice);
  TestFileFileCopy(o);
}

TEST(Copy, HeterogenousManagers) {
  event::Manager rm = make_manager();
  event::Manager wm = make_manager();
  io::Options ro, wo;
  ro.set_manager(rm);
  ro.set_block_size(4096);
  ro.set_transfer_mode(io::TransferMode::read_write);
  wo.set_manager(wm);
  wo.set_block_size(4096);
  wo.set_transfer_mode(io::TransferMode::read_write);

  base::SocketPair rdpair, wrpair;
  ASSERT_OK(base::make_socketpair(&rdpair, AF_UNIX, SOCK_STREAM, 0));
  ASSERT_OK(base::make_socketpair(&wrpair, AF_UNIX, SOCK_STREAM, 0));
  ASSERT_OK(base::set_blocking(rdpair.left, true));
  ASSERT_OK(base::set_blocking(wrpair.right, true));
  ASSERT_OK(base::shutdown(rdpair.right, SHUT_WR));
  ASSERT_OK(base::shutdown(wrpair.right, SHUT_WR));

  LOG(INFO) << "sockets are ready";

  std::mutex mu;
  std::size_t blocks_transferred = 0;

  std::thread t1([&rdpair] {
    std::vector<char> buf;
    buf.assign(4096, 'A');
    for (std::size_t i = 0; i < 16; ++i) {
      sleep_ms(1);
      LOG(INFO) << "writing block #" << i;
      auto r =
          base::write_exactly(rdpair.left, buf.data(), buf.size(), "rdpair");
      EXPECT_OK(r);
      if (!r) break;
    }
    sleep_ms(1);
    LOG(INFO) << "sending EOF on rdpair";
    EXPECT_OK(rdpair.left->close());
  });

  std::thread t2([&wrpair, &mu, &blocks_transferred] {
    std::vector<char> buf;
    buf.resize(1024);
    std::size_t i = 0;
    base::Result r;
    while (true) {
      sleep_ms(1);
      LOG(INFO) << "reading block #" << (i / 4) << "." << ((i % 4) * 25);
      r = base::read_exactly(wrpair.right, buf.data(), buf.size(), "wrpair");
      if (!r) break;
      auto lock = acquire_lock(mu);
      ++blocks_transferred;
      ++i;
    }
    EXPECT_EOF(r);
    LOG(INFO) << "got EOF on wrpair";
  });

  io::Reader r = io::fdreader(rdpair.right, ro);
  io::Writer w = io::fdwriter(wrpair.left, wo);

  event::Task task;
  std::size_t n;
  LOG(INFO) << "starting copy";
  io::copy(&task, &n, w, r);
  LOG(INFO) << "waiting on copy";
  event::wait_all({rm, wm}, {&task});
  LOG(INFO) << "copy complete";
  EXPECT_OK(task.result());
  LOG(INFO) << "sending EOF on wrpair";
  EXPECT_OK(wrpair.left->close());

  LOG(INFO) << "joining threads";
  t1.join();
  t2.join();
  EXPECT_EQ(4U * 16U, blocks_transferred);

  base::log_flush();
}

static void init() __attribute__((constructor));
static void init() { base::log_stderr_set_level(VLOG_LEVEL(6)); }
<|endoftext|>


<commit_before>#include "plugin.h"

#include <algorithm>
#include <cstring>

#include "npapi-headers/npfunctions.h"

NPNetscapeFuncs g_browser_functions = { 0 };

namespace {

NPError initializeBrowserFuncs(NPNetscapeFuncs* bFuncs)
{
    if (!bFuncs)
        return NPERR_INVALID_FUNCTABLE_ERROR;

    int major_version = bFuncs->version >> 8;
    int minor_version = bFuncs->version & 0xff;

    if (major_version != NP_VERSION_MAJOR)
        return NPERR_INCOMPATIBLE_VERSION_ERROR;

    // TODO: Assert support in minor_version for features we care
    // about.

    memcpy(&g_browser_functions, bFuncs,
           std::min(sizeof(g_browser_functions), (size_t)bFuncs->size));

    return NPERR_NO_ERROR;
}

}  // namespace

NP_EXPORT(NPError) OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs)
{
    pFuncs->size = sizeof(*pFuncs);
    pFuncs->version = 0; // My version?
    // TODO: implement the functions I need.
    return NPERR_NO_ERROR;
}

NP_EXPORT(NPError) OSCALL NP_Initialize(NPNetscapeFuncs* bFuncs,
					NPPluginFuncs* pFuncs)
{
    NPError err = initializeBrowserFuncs(bFuncs);
    if (err != NPERR_NO_ERROR)
        return err;

    // On Unix, it looks like NP_GetEntryPoints isn't called directly?
    // The prototype for NP_Initialize is different from everyone
    // else. Meh.
    return NP_GetEntryPoints(pFuncs);
}

NP_EXPORT(NPError) OSCALL NP_GetValue(void *instance,
                                      NPPVariable variable,
                                      void *value)
{
    NPError err = NPERR_NO_ERROR;
    switch (variable) {
        case NPPVpluginNameString:
            *reinterpret_cast<const char **>(value) = "Embedded Emacs";
            break;
        case NPPVpluginDescriptionString:
            *reinterpret_cast<const char **>(value) =
                    "Embeds emacs into your browser window with XEmbed.";
            break;
        default:
            err = NPERR_GENERIC_ERROR;
    }
    return err;
}

// This probably should be const char *, but NPAPI messed up.
NP_EXPORT(char*) OSCALL NP_GetMIMEDescription(void)
{
    return const_cast<char*>("application/x-emacs-npapi::Embed emacs with NPAPI");
}
<commit_msg>Add some stubs for NPP_New and NPP_Destroy<commit_after>#include "plugin.h"

#include <algorithm>
#include <cstring>

#include "emacsinstance.h"
#include "npapi-headers/npfunctions.h"

NPNetscapeFuncs g_browser_functions = { 0 };

namespace {

NPError initializeBrowserFuncs(NPNetscapeFuncs* bFuncs)
{
    if (!bFuncs)
        return NPERR_INVALID_FUNCTABLE_ERROR;

    int major_version = bFuncs->version >> 8;
    int minor_version = bFuncs->version & 0xff;

    if (major_version != NP_VERSION_MAJOR)
        return NPERR_INCOMPATIBLE_VERSION_ERROR;

    // TODO: Assert support in minor_version for features we care
    // about.

    memcpy(&g_browser_functions, bFuncs,
           std::min(sizeof(g_browser_functions), (size_t)bFuncs->size));

    return NPERR_NO_ERROR;
}

}  // namespace

NP_EXPORT(NPError) OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs)
{
    pFuncs->size = sizeof(*pFuncs);
    pFuncs->version = 0; // My version?
    pFuncs->newp = NPP_New;
    pFuncs->destroy = NPP_Destroy;
    // TODO: implement the functions I need.
    return NPERR_NO_ERROR;
}

NP_EXPORT(NPError) OSCALL NP_Initialize(NPNetscapeFuncs* bFuncs,
					NPPluginFuncs* pFuncs)
{
    NPError err = initializeBrowserFuncs(bFuncs);
    if (err != NPERR_NO_ERROR)
        return err;

    // On Unix, it looks like NP_GetEntryPoints isn't called directly?
    // The prototype for NP_Initialize is different from everyone
    // else. Meh.
    return NP_GetEntryPoints(pFuncs);
}

NP_EXPORT(NPError) OSCALL NP_GetValue(void *instance,
                                      NPPVariable variable,
                                      void *value)
{
    NPError err = NPERR_NO_ERROR;
    switch (variable) {
        case NPPVpluginNameString:
            *reinterpret_cast<const char **>(value) = "Embedded Emacs";
            break;
        case NPPVpluginDescriptionString:
            *reinterpret_cast<const char **>(value) =
                    "Embeds emacs into your browser window with XEmbed.";
            break;
        default:
            err = NPERR_GENERIC_ERROR;
    }
    return err;
}

// This probably should be const char *, but NPAPI messed up.
NP_EXPORT(char*) OSCALL NP_GetMIMEDescription(void)
{
    return const_cast<char*>("application/x-emacs-npapi::Embed emacs with NPAPI");
}

/*****************************/
/* NPP Functions             */
/*****************************/

NPError NPP_New(NPMIMEType pluginType, NPP instance,
                uint16_t mode, int16_t argc, char* argn[],
                char* argv[], NPSavedData* saved)
{
    return NPERR_GENERIC_ERROR;
}

NPError NPP_Destroy(NPP instance, NPSavedData** save)
{
    return NPERR_GENERIC_ERROR;
}
<|endoftext|>