<commit_before>//
//  SimpleDFPathTracer.cpp
//  Renderer
//
//  Created by Kristofer Schlachter on 6/19/14.
//
//

#include <memory>
#include "SimpleDFPathTracer.h"
#include "mesh.h"
#include "Texture.h"
#include "ImageUtils.h"
#include "RenderManager.h"
#include "GLFWTime.h"
#include "ObjectIDGenerator.h"
#include "TriangleMesh.h"
#include "UniformGrid.h"
#include "UniformHGrid.h"

//Get rid of this once we have a texture class working
#include "OpenGLHelper.h"

namespace renderlib{

SimpleDFPathTracer::SimpleDFPathTracer()
{
  _renderSortValue = IRenderable::DefaultSortValue;
  _id = ObjectIDGenerator::getInstance().getNextID();
  _useScreenResolution = false;
  _saveFrame = false;
  _elevationIDX = 0;
  _azimuthIDX = 0;
  _lightingIDX = 0;
  _renderTexture0 = new RenderTexture();
  _renderTexture1 = new RenderTexture();
  _downsampledTexture = new RenderTexture();
  setLightingArrays(_lightingIDX);

}

SimpleDFPathTracer::~SimpleDFPathTracer()
{
  delete _renderTexture0;
  delete _renderTexture1;
  delete _downsampledTexture;
  delete _shader;
  delete _filterShader;
}

void SimpleDFPathTracer::resetFBOs()
{
  _renderTexture0->clear();
  _renderTexture1->clear();

}

void SimpleDFPathTracer::resize()
{
  vmath::Vector2 sd =  RenderManager::getInstance().getFramebufferSize();
  _screenDim.x = sd.x;
  _screenDim.y = sd.y;
  _useScreenResolution ? _currentResolution = _screenDim : _currentResolution = _imageDim;
  _renderTexture0->setFBOSize(_currentResolution.x, _currentResolution.y);
  _renderTexture1->setFBOSize(_currentResolution.x, _currentResolution.y);
  resetFBOs();

}

void SimpleDFPathTracer::init()
{
  resize();
  _mvp.identity();
  //For now just hard code some stuff in here to see if everything else works!
  _shader = new Shader();
  _shader->registerShader("shaders/passThroughVS.glsl", ShaderType::VERTEX);
  _shader->registerShader("shaders/pathTracingSphereTracer.glsl", ShaderType::FRAGMENT);
  _shader->compileShaders();
  _shader->linkShaders();

  _mesh = new Mesh();
  _mesh->createScreenQuad(Vector2(-1.0f, -1.0f), Vector2(1.0f, 1.0f));
  _mesh->bindAttributesToVAO(*_shader);

  _fullscreenMesh = new Mesh();
  _fullscreenMesh->createScreenQuad(Vector2(-1.0f, -1.f), Vector2(1.0f, 1.0f));
  _filterShader = new Shader();
  _filterShader->registerShader("shaders/lanczosFilter.vert", ShaderType::VERTEX);
  _filterShader->registerShader("shaders/lanczosFilter.frag", ShaderType::FRAGMENT);
  //_filterShader->registerShader("shaders/bicubicFilter.vert", ShaderType::VERTEX);
  //_filterShader->registerShader("shaders/bicubicFilter.frag", ShaderType::FRAGMENT);
  _filterShader->compileShaders();
  _filterShader->linkShaders();
  _fullscreenMesh->bindAttributesToVAO(*_filterShader);

  //FIXME: There is a problem with the vertex format binding... UVs are invalid!
  //FIXME: There is a problem with the vertex format binding... UVs are invalid!

  const int DFRESOLUTION = 256;
  _imageDim = glm::vec2(96, 96);
  _currentResolution = _imageDim;
  _gridResolution = DFRESOLUTION;
  char outputName[256];
  char inputName[256];
  const char* modelname ="Humvee200k";
  sprintf(inputName, "assets/models/%s.obj", modelname);
  sprintf(outputName, "assets/%s%d.bin", modelname, DFRESOLUTION);

  Mesh normalMesh;
  //normalMesh.createSphereMeshData(8, 8);
  //normalMesh.createCube(Vector3(0.5f), Vector3(1.0f));
  //normalMesh.createTriangle();

  _model.loadModelFromFile(inputName, true, true);
  std::vector<Material> materials;
  _model.collapseMeshes(normalMesh, materials);

  normalMesh.fitIntoUnitCube(_trans, _min, _max);
  //normalMesh.movePivotToBottomMiddle();

  TriangleMesh triMesh;
  std::shared_ptr<UniformHGrid> grid = std::make_shared<UniformHGrid>(DFRESOLUTION, glm::vec3(0));
  normalMesh.convertToTriangleMesh(triMesh, grid);

  //_texture.createDistanceFieldFromMesh(DFRESOLUTION, triMesh, true, outputName);
  _texture.loadDistanceFieldFromDisk(outputName);

  printf("trans: %3.6f %3.6f %3.6f\n", _trans.x, _trans.y, _trans.z);
  printf("min: %3.6f %3.6f %3.6f\n", _min.x, _min.y, _min.z);
  printf("max: %3.6f %3.6f %3.6f\n", _max.x, _max.y, _max.z);
  _targetHeight = _max.y * 0.5f;

  _texture.setupDebugData(Vector2(0.0f, 0.0f), Vector2(1.0f, 1.0f));

  _renderTexture0->setupFBO(_imageDim.x, _imageDim.y, true, GL_RGBA32F, GL_RGBA, TextureDataType::TDT_FLOAT, 1);
  _renderTexture0->setupFullscreenData();

  _renderTexture1->setupFBO(_imageDim.x, _imageDim.y, true, GL_RGBA32F, GL_RGBA, TextureDataType::TDT_FLOAT, 1);
  _renderTexture1->setupFullscreenData();
  _downsampledTexture->setupFBO(_imageDim.x, _imageDim.y, true, GL_RGBA32F, GL_RGBA, TextureDataType::TDT_FLOAT, 1);
  _downsampledTexture->setupDebugData(Vector2(-1.0, -1.0), Vector2(1.0, 1.0));
}

void SimpleDFPathTracer::update(float time)
{

  // Conversion from Euler angles (in radians) to Quaternion
  float azimuth = _azimuths[_azimuthIDX];
  float azimuthRadians = glm::radians(azimuth);
  vec3 EulerAngles(0,azimuthRadians,  0);
  quat q = quat(EulerAngles);
  _m = glm::mat4_cast(q);
  _mInverse = glm::inverse(_m);

  float elevation = -_elevations[_elevationIDX];
  float elevationRadians = glm::radians(elevation);
  vec3 EulerAnglesElevation(elevationRadians, 0,  0);
  quat qElevation = quat(EulerAnglesElevation);
  glm::mat4 elevationMatrix = glm::mat4_cast(qElevation);

  glm::vec3 camPos(0.0f, 0.0f, 2.0f);
  _cameraPosition = mat3(elevationMatrix) * camPos;
  _cameraMatrix = elevationMatrix;

  Matrix4 ModelviewMatrix = Matrix4::identity();

  float n = 1.0f;
  float f = 100.0f;
  float fieldOfView = 0.7f;

  Matrix4 ProjectionMatrix = Matrix4::perspective(fieldOfView, 1, n, f);
  _mvp = ProjectionMatrix * ModelviewMatrix;

}

void SimpleDFPathTracer::preRender()
{

}

void SimpleDFPathTracer::draw()
{

  _renderTexture0->bindFBO();
  glDisable(GL_DEPTH_TEST);
  _shader->bind();
  _texture.bindToChannel(0);
  _renderTexture1->bindTargetToChannel(1);
  _shader->setUniform("uPreviousFrameTexture",1);
  _shader->setUniform("iGlobalTime", GLFWTime::getCurrentTime());
  _shader->setUniform("iResolution", Vector2(_currentResolution.x,_currentResolution.y));
  _shader->setUniform("iMouse", Vector2(_mousePos.x,_mousePos.y));
  _shader->setUniform("Density", 0);
  _shader->setUniform("uModelMatrix", _m);
  _shader->setUniform("uNormalMatrix", glm::inverseTranspose( _m));
  _shader->setUniform("uModelInverseMatrix", _mInverse);
  _shader->setUniform("uGridResolution", _gridResolution);
  _shader->setUniform("uCameraPosition", _cameraPosition);
  _shader->setUniform("uObjectOffset", _trans);
  _shader->setUniform("uCameraMatrix", _cameraMatrix);
  //_shader->setUniform("lightSwitches", _lightSwitching[_lightingIDX],6);
  _shader->setUniform("lights", _currLights,2);
  _shader->setUniform("lightColors", _currLightColors,2);
  _shader->setUniform("uTargetHeight", _targetHeight);

  _mesh->drawBuffers();
  _shader->unbind();

// FIXME:  Create signed dist field.
  //FIXME: Create test cases for sphere
  if (_saveFrame)
  {
    //saveScreenShotTGA("screencap.tga", _currentResolution.x, _currentResolution.y);
	  saveScreenShotPNG("screencap.png", _currentResolution.x, _currentResolution.y);
	  _saveFrame = false || _drawDownsampled;
  }

  _renderTexture0->unbindFBO();

  _renderTexture0->drawFullscreen();

  if(_drawDownsampled)
  {
    //Now create and draw a downsampled image
    //draw the full res texture to a 96x96 FBO using a filtering shader
    _filterShader->bind();
    _downsampledTexture->bindFBO();
    _renderTexture0->bindTargetToChannel(0);
    _filterShader->setUniform("inputImageTexture",0);
    _filterShader->setUniform("textureWidth",_imageDim.x);
    _filterShader->setUniform("textureHeight",_imageDim.y);
    _fullscreenMesh->drawBuffers();
    _filterShader->unbind();

    if (_saveFrame)
    {
      saveScreenShotPNG("screencap96x96.png", _imageDim.x, _imageDim.y);
      _saveFrame = false;
    }
    _downsampledTexture->unbindFBO();

    if(!_stretchImage)
    {
      //Now draw the downsampled texture to the screen's upper right corner
      float imageScale = (int)_screenDim.x /(96*3);
      if(imageScale < 1.0f) imageScale = 1.0f;

      int dim = 96*imageScale;
      _downsampledTexture->debugDraw(glm::ivec4(_screenDim.x-dim,_screenDim.y-dim,dim,dim));
    }
    else{
      _downsampledTexture->debugDraw(glm::ivec4(0,0,_screenDim.x,_screenDim.y));
    }
  }

  glEnable(GL_DEPTH_TEST);

  //swap the render targets
  RenderTexture* tmp = _renderTexture0;
  _renderTexture0 = _renderTexture1;
  _renderTexture1 = tmp;

}

void SimpleDFPathTracer::postRender()
{

}
void SimpleDFPathTracer::handleKey(KeyInfo& key)
{

  if(key.key >= '0' && key.key <= '9' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    //char idx = key.key - '0';
  }
  if(key.key == 'S' && key.action == KeyInfo::KeyAction::RELEASE)
  {
	  _saveFrame = true;
  }
  if(key.key == 'I' && key.action == KeyInfo::KeyAction::RELEASE)
  {
	  _useScreenResolution = !_useScreenResolution;
	  resize();
  }
  if(key.key == 'D' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    if (false == InputManager::getShiftDown())
    {
      _stretchImage = ! _stretchImage;
    }
    else
    {
      _drawDownsampled = !_drawDownsampled;
    }
  }

  if(key.key == 'Z' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    if (false == InputManager::getShiftDown())
	  {
		  if (++_elevationIDX >= (unsigned)_elevations.size())
		  {
			  _elevationIDX = 0;
		  }
	  }
	  else
	  {
		  if (--_elevationIDX <  0)
		  {
			  _elevationIDX = (unsigned)_elevations.size() - 1;
		  }
	  }
    resetFBOs();
	  printf("Elevation[%d]: %d\n", (int)_elevationIDX,(int)_elevations[_elevationIDX]);
  }
  if(key.key == 'X' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    if (false == InputManager::getShiftDown())
	  {
		  if (++_azimuthIDX >= (unsigned)_azimuths.size())
		  {
			  _azimuthIDX = 0;
		  }
	  }
	  else
	  {
		  if (--_azimuthIDX <  0)
		  {
			  _azimuthIDX = (unsigned)_azimuths.size() - 1;
		  }
	  }
    resetFBOs();
  }
  if(key.key == 'C' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    if (false == InputManager::getShiftDown())
	  {
		  if (++_lightingIDX >= (int)_lighting.size())
		  {
			  _lightingIDX = 0;
		  }
	  }
	  else
	  {
		  if (--_lightingIDX <  0)
		  {
			  _lightingIDX = (int)_lighting.size() - 1;
		  }

	  }
    setLightingArrays(_lightingIDX);
	printf("%d, %d\n",
		_lightSwitching[_lightingIDX][0],
		_lightSwitching[_lightingIDX][1]
		);
    resetFBOs();
  }
  if(key.key == ' ' && key.action == KeyInfo::KeyAction::PRESS)
  {
  }

  if(key.action == KeyInfo::KeyAction::PRESS)
  {
    switch (key.key)
    {
      case ',':
      case '<':
        break;
      case '.':
      case '>':
        break;
      case '/':
      case '?':
        break;
    }
  }
}

void SimpleDFPathTracer::handlePointer(std::vector<PointerInfo>& pointers)
{
  PointerInfo& pi = pointers[0];
  _mousePos = pi.pos;
  if(pi.down)
  {
    _currLights[0].x += pi.velocity.x;
    _currLights[0].y += pi.velocity.y;
    printf("light0: %2.4f, %2.4f, %2.4f, %2.4f   brightness: %2.4f\n",
           _currLights[0].x,
           _currLights[0].y,
           _currLights[0].z,
           _currLights[0].w,
           _currLightColors[0].x);
    resetFBOs();
  }
  if(pi.rdown)
  {
    _currLights[0].z += pi.velocity.x;
    _currLights[0].w += pi.velocity.y;

    printf("light0: %2.4f, %2.4f, %2.4f, %2.4f   brightness: %2.4f\n",
           _currLights[0].x,
           _currLights[0].y,
           _currLights[0].z,
           _currLights[0].w,
           _currLightColors[0].x);
    resetFBOs();
  }
  if(pi.wdown)
  {
    float c = _currLightColors[0].x;
    _currLightColors[0] = vec3(pi.velocity.y+c);

    printf("light0: %2.4f, %2.4f, %2.4f, %2.4f   brightness: %2.4f\n",
           _currLights[0].x,
           _currLights[0].y,
           _currLights[0].z,
           _currLights[0].w,
           _currLightColors[0].x);
    resetFBOs();
  }

}

}// namespace renderlib
<commit_msg>Can control each light property seperately with shift key<commit_after>//
//  SimpleDFPathTracer.cpp
//  Renderer
//
//  Created by Kristofer Schlachter on 6/19/14.
//
//

#include <memory>
#include "SimpleDFPathTracer.h"
#include "mesh.h"
#include "Texture.h"
#include "ImageUtils.h"
#include "RenderManager.h"
#include "GLFWTime.h"
#include "ObjectIDGenerator.h"
#include "TriangleMesh.h"
#include "UniformGrid.h"
#include "UniformHGrid.h"

//Get rid of this once we have a texture class working
#include "OpenGLHelper.h"

namespace renderlib{

SimpleDFPathTracer::SimpleDFPathTracer()
{
  _renderSortValue = IRenderable::DefaultSortValue;
  _id = ObjectIDGenerator::getInstance().getNextID();
  _useScreenResolution = false;
  _saveFrame = false;
  _elevationIDX = 0;
  _azimuthIDX = 0;
  _lightingIDX = 0;
  _renderTexture0 = new RenderTexture();
  _renderTexture1 = new RenderTexture();
  _downsampledTexture = new RenderTexture();
  setLightingArrays(_lightingIDX);

}

SimpleDFPathTracer::~SimpleDFPathTracer()
{
  delete _renderTexture0;
  delete _renderTexture1;
  delete _downsampledTexture;
  delete _shader;
  delete _filterShader;
}

void SimpleDFPathTracer::resetFBOs()
{
  _renderTexture0->clear();
  _renderTexture1->clear();

}

void SimpleDFPathTracer::resize()
{
  vmath::Vector2 sd =  RenderManager::getInstance().getFramebufferSize();
  _screenDim.x = sd.x;
  _screenDim.y = sd.y;
  _useScreenResolution ? _currentResolution = _screenDim : _currentResolution = _imageDim;
  _renderTexture0->setFBOSize(_currentResolution.x, _currentResolution.y);
  _renderTexture1->setFBOSize(_currentResolution.x, _currentResolution.y);
  resetFBOs();

}

void SimpleDFPathTracer::init()
{
  resize();
  _mvp.identity();
  //For now just hard code some stuff in here to see if everything else works!
  _shader = new Shader();
  _shader->registerShader("shaders/passThroughVS.glsl", ShaderType::VERTEX);
  _shader->registerShader("shaders/pathTracingSphereTracer.glsl", ShaderType::FRAGMENT);
  _shader->compileShaders();
  _shader->linkShaders();

  _mesh = new Mesh();
  _mesh->createScreenQuad(Vector2(-1.0f, -1.0f), Vector2(1.0f, 1.0f));
  _mesh->bindAttributesToVAO(*_shader);

  _fullscreenMesh = new Mesh();
  _fullscreenMesh->createScreenQuad(Vector2(-1.0f, -1.f), Vector2(1.0f, 1.0f));
  _filterShader = new Shader();
  _filterShader->registerShader("shaders/lanczosFilter.vert", ShaderType::VERTEX);
  _filterShader->registerShader("shaders/lanczosFilter.frag", ShaderType::FRAGMENT);
  //_filterShader->registerShader("shaders/bicubicFilter.vert", ShaderType::VERTEX);
  //_filterShader->registerShader("shaders/bicubicFilter.frag", ShaderType::FRAGMENT);
  _filterShader->compileShaders();
  _filterShader->linkShaders();
  _fullscreenMesh->bindAttributesToVAO(*_filterShader);

  //FIXME: There is a problem with the vertex format binding... UVs are invalid!
  //FIXME: There is a problem with the vertex format binding... UVs are invalid!

  const int DFRESOLUTION = 256;
  _imageDim = glm::vec2(96, 96);
  _currentResolution = _imageDim;
  _gridResolution = DFRESOLUTION;
  char outputName[256];
  char inputName[256];
  const char* modelname ="Humvee200k";
  sprintf(inputName, "assets/models/%s.obj", modelname);
  sprintf(outputName, "assets/%s%d.bin", modelname, DFRESOLUTION);

  Mesh normalMesh;
  //normalMesh.createSphereMeshData(8, 8);
  //normalMesh.createCube(Vector3(0.5f), Vector3(1.0f));
  //normalMesh.createTriangle();

  _model.loadModelFromFile(inputName, true, true);
  std::vector<Material> materials;
  _model.collapseMeshes(normalMesh, materials);

  normalMesh.fitIntoUnitCube(_trans, _min, _max);
  //normalMesh.movePivotToBottomMiddle();

  TriangleMesh triMesh;
  std::shared_ptr<UniformHGrid> grid = std::make_shared<UniformHGrid>(DFRESOLUTION, glm::vec3(0));
  normalMesh.convertToTriangleMesh(triMesh, grid);

  //_texture.createDistanceFieldFromMesh(DFRESOLUTION, triMesh, true, outputName);
  _texture.loadDistanceFieldFromDisk(outputName);

  printf("trans: %3.6f %3.6f %3.6f\n", _trans.x, _trans.y, _trans.z);
  printf("min: %3.6f %3.6f %3.6f\n", _min.x, _min.y, _min.z);
  printf("max: %3.6f %3.6f %3.6f\n", _max.x, _max.y, _max.z);
  _targetHeight = _max.y * 0.5f;

  _texture.setupDebugData(Vector2(0.0f, 0.0f), Vector2(1.0f, 1.0f));

  _renderTexture0->setupFBO(_imageDim.x, _imageDim.y, true, GL_RGBA32F, GL_RGBA, TextureDataType::TDT_FLOAT, 1);
  _renderTexture0->setupFullscreenData();

  _renderTexture1->setupFBO(_imageDim.x, _imageDim.y, true, GL_RGBA32F, GL_RGBA, TextureDataType::TDT_FLOAT, 1);
  _renderTexture1->setupFullscreenData();
  _downsampledTexture->setupFBO(_imageDim.x, _imageDim.y, true, GL_RGBA32F, GL_RGBA, TextureDataType::TDT_FLOAT, 1);
  _downsampledTexture->setupDebugData(Vector2(-1.0, -1.0), Vector2(1.0, 1.0));
}

void SimpleDFPathTracer::update(float time)
{

  // Conversion from Euler angles (in radians) to Quaternion
  float azimuth = _azimuths[_azimuthIDX];
  float azimuthRadians = glm::radians(azimuth);
  vec3 EulerAngles(0,azimuthRadians,  0);
  quat q = quat(EulerAngles);
  _m = glm::mat4_cast(q);
  _mInverse = glm::inverse(_m);

  float elevation = -_elevations[_elevationIDX];
  float elevationRadians = glm::radians(elevation);
  vec3 EulerAnglesElevation(elevationRadians, 0,  0);
  quat qElevation = quat(EulerAnglesElevation);
  glm::mat4 elevationMatrix = glm::mat4_cast(qElevation);

  glm::vec3 camPos(0.0f, 0.0f, 2.0f);
  _cameraPosition = mat3(elevationMatrix) * camPos;
  _cameraMatrix = elevationMatrix;

  Matrix4 ModelviewMatrix = Matrix4::identity();

  float n = 1.0f;
  float f = 100.0f;
  float fieldOfView = 0.7f;

  Matrix4 ProjectionMatrix = Matrix4::perspective(fieldOfView, 1, n, f);
  _mvp = ProjectionMatrix * ModelviewMatrix;

}

void SimpleDFPathTracer::preRender()
{

}

void SimpleDFPathTracer::draw()
{

  _renderTexture0->bindFBO();
  glDisable(GL_DEPTH_TEST);
  _shader->bind();
  _texture.bindToChannel(0);
  _renderTexture1->bindTargetToChannel(1);
  _shader->setUniform("uPreviousFrameTexture",1);
  _shader->setUniform("iGlobalTime", GLFWTime::getCurrentTime());
  _shader->setUniform("iResolution", Vector2(_currentResolution.x,_currentResolution.y));
  _shader->setUniform("iMouse", Vector2(_mousePos.x,_mousePos.y));
  _shader->setUniform("Density", 0);
  _shader->setUniform("uModelMatrix", _m);
  _shader->setUniform("uNormalMatrix", glm::inverseTranspose( _m));
  _shader->setUniform("uModelInverseMatrix", _mInverse);
  _shader->setUniform("uGridResolution", _gridResolution);
  _shader->setUniform("uCameraPosition", _cameraPosition);
  _shader->setUniform("uObjectOffset", _trans);
  _shader->setUniform("uCameraMatrix", _cameraMatrix);
  //_shader->setUniform("lightSwitches", _lightSwitching[_lightingIDX],6);
  _shader->setUniform("lights", _currLights,2);
  _shader->setUniform("lightColors", _currLightColors,2);
  _shader->setUniform("uTargetHeight", _targetHeight);

  _mesh->drawBuffers();
  _shader->unbind();

// FIXME:  Create signed dist field.
  //FIXME: Create test cases for sphere
  if (_saveFrame)
  {
    //saveScreenShotTGA("screencap.tga", _currentResolution.x, _currentResolution.y);
	  saveScreenShotPNG("screencap.png", _currentResolution.x, _currentResolution.y);
	  _saveFrame = false || _drawDownsampled;
  }

  _renderTexture0->unbindFBO();

  _renderTexture0->drawFullscreen();

  if(_drawDownsampled)
  {
    //Now create and draw a downsampled image
    //draw the full res texture to a 96x96 FBO using a filtering shader
    _filterShader->bind();
    _downsampledTexture->bindFBO();
    _renderTexture0->bindTargetToChannel(0);
    _filterShader->setUniform("inputImageTexture",0);
    _filterShader->setUniform("textureWidth",_imageDim.x);
    _filterShader->setUniform("textureHeight",_imageDim.y);
    _fullscreenMesh->drawBuffers();
    _filterShader->unbind();

    if (_saveFrame)
    {
      saveScreenShotPNG("screencap96x96.png", _imageDim.x, _imageDim.y);
      _saveFrame = false;
    }
    _downsampledTexture->unbindFBO();

    if(!_stretchImage)
    {
      //Now draw the downsampled texture to the screen's upper right corner
      float imageScale = (int)_screenDim.x /(96*3);
      if(imageScale < 1.0f) imageScale = 1.0f;

      int dim = 96*imageScale;
      _downsampledTexture->debugDraw(glm::ivec4(_screenDim.x-dim,_screenDim.y-dim,dim,dim));
    }
    else{
      _downsampledTexture->debugDraw(glm::ivec4(0,0,_screenDim.x,_screenDim.y));
    }
  }

  glEnable(GL_DEPTH_TEST);

  //swap the render targets
  RenderTexture* tmp = _renderTexture0;
  _renderTexture0 = _renderTexture1;
  _renderTexture1 = tmp;

}

void SimpleDFPathTracer::postRender()
{

}
void SimpleDFPathTracer::handleKey(KeyInfo& key)
{

  if(key.key >= '0' && key.key <= '9' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    //char idx = key.key - '0';
  }
  if(key.key == 'S' && key.action == KeyInfo::KeyAction::RELEASE)
  {
	  _saveFrame = true;
  }
  if(key.key == 'I' && key.action == KeyInfo::KeyAction::RELEASE)
  {
	  _useScreenResolution = !_useScreenResolution;
	  resize();
  }
  if(key.key == 'D' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    if (false == InputManager::getShiftDown())
    {
      _stretchImage = ! _stretchImage;
    }
    else
    {
      _drawDownsampled = !_drawDownsampled;
    }
  }

  if(key.key == 'Z' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    if (false == InputManager::getShiftDown())
	  {
		  if (++_elevationIDX >= (unsigned)_elevations.size())
		  {
			  _elevationIDX = 0;
		  }
	  }
	  else
	  {
		  if (--_elevationIDX <  0)
		  {
			  _elevationIDX = (unsigned)_elevations.size() - 1;
		  }
	  }
    resetFBOs();
	  printf("Elevation[%d]: %d\n", (int)_elevationIDX,(int)_elevations[_elevationIDX]);
  }
  if(key.key == 'X' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    if (false == InputManager::getShiftDown())
	  {
		  if (++_azimuthIDX >= (unsigned)_azimuths.size())
		  {
			  _azimuthIDX = 0;
		  }
	  }
	  else
	  {
		  if (--_azimuthIDX <  0)
		  {
			  _azimuthIDX = (unsigned)_azimuths.size() - 1;
		  }
	  }
    resetFBOs();
  }
  if(key.key == 'C' && key.action == KeyInfo::KeyAction::RELEASE)
  {
    if (false == InputManager::getShiftDown())
	  {
		  if (++_lightingIDX >= (int)_lighting.size())
		  {
			  _lightingIDX = 0;
		  }
	  }
	  else
	  {
		  if (--_lightingIDX <  0)
		  {
			  _lightingIDX = (int)_lighting.size() - 1;
		  }

	  }
    setLightingArrays(_lightingIDX);
	printf("%d, %d\n",
		_lightSwitching[_lightingIDX][0],
		_lightSwitching[_lightingIDX][1]
		);
    resetFBOs();
  }
  if(key.key == ' ' && key.action == KeyInfo::KeyAction::PRESS)
  {
  }

  if(key.action == KeyInfo::KeyAction::PRESS)
  {
    switch (key.key)
    {
      case ',':
      case '<':
        break;
      case '.':
      case '>':
        break;
      case '/':
      case '?':
        break;
    }
  }
}

void SimpleDFPathTracer::handlePointer(std::vector<PointerInfo>& pointers)
{
  PointerInfo& pi = pointers[0];
  _mousePos = pi.pos;
  int lidx = 0;
  if(InputManager::getShiftDown())
    lidx = 1;
  if(pi.down && pi.dragging)
  {
    _currLights[lidx].x += pi.velocity.x;
    _currLights[lidx].z += pi.velocity.y;
    printf("light0: %2.4f, %2.4f, %2.4f, %2.4f   brightness: %2.4f\n",
           _currLights[lidx].x,
           _currLights[lidx].y,
           _currLights[lidx].z,
           _currLights[lidx].w,
           _currLightColors[lidx].x);
    resetFBOs();
  }
  if(pi.rdown && pi.dragging)
  {
    //Light height = right drag vertical
    _currLights[lidx].y += pi.velocity.x;

    printf("light0: %2.4f, %2.4f, %2.4f, %2.4f   brightness: %2.4f\n",
           _currLights[lidx].x,
           _currLights[lidx].y,
           _currLights[lidx].z,
           _currLights[lidx].w,
           _currLightColors[lidx].x);
    resetFBOs();
  }
  if(pi.wdown && pi.dragging)
  {
    //Intensity = middle drag horizontal
    float c = _currLightColors[lidx].x;
    _currLightColors[lidx] = vec3(pi.velocity.x+c);

    //Size = middle drag vertical
    _currLights[lidx].w += pi.velocity.y;

    printf("light0: %2.4f, %2.4f, %2.4f, %2.4f   brightness: %2.4f\n",
           _currLights[lidx].x,
           _currLights[lidx].y,
           _currLights[lidx].z,
           _currLights[lidx].w,
           _currLightColors[lidx].x);
    resetFBOs();
  }

}

}// namespace renderlib
<|endoftext|>


<commit_before>/**
 * \file
 * \brief RawFifoQueue class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-12-31
 */

#include "distortos/RawFifoQueue.hpp"

#include "distortos/scheduler/SemaphoreWaitFunctor.hpp"

#include <cstring>
#include <cerrno>

namespace distortos
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// PushFunctor is a functor used for pushing of data to the RawFifoQueue
class PushFunctor : public scheduler::FifoQueueBase::Functor
{
public:

	/**
	 * \brief PushFunctor's constructor
	 *
	 * \param [in] data is a pointer to data that will be pushed to RawFifoQueue
	 * \param [in] size is the size of \a data, bytes
	 */

	constexpr PushFunctor(const void* const data, const size_t size) :
			data_{data},
			size_{size}
	{

	}

	/**
	 * \brief Copies the data to RawFifoQueue's storage (with memcpy()) and increments the storage pointer to next
	 * position (using the size of element).
	 *
	 * \param [in,out] storage is a reference to pointer to queue's storage
	 */

	virtual void operator()(void*& storage) const override
	{
		memcpy(storage, data_, size_);
		storage = static_cast<uint8_t*>(storage) + size_;
	}

private:

	/// pointer to data that will be pushed to RawFifoQueue
	const void* const data_;

	/// size of \a data_, bytes
	const size_t size_;
};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

RawFifoQueue::RawFifoQueue(void* const storage, const size_t elementSize, const size_t maxElements) :
		fifoQueueBase_{storage, static_cast<uint8_t*>(storage) + elementSize * maxElements, maxElements},
		elementSize_{elementSize}
{

}

int RawFifoQueue::push(const void* const data, const size_t size)
{
	const scheduler::SemaphoreWaitFunctor semaphoreWaitFunctor;
	return pushInternal(semaphoreWaitFunctor, data, size);
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

int RawFifoQueue::pushInternal(const scheduler::SemaphoreFunctor& waitSemaphoreFunctor, const void* const data,
		const size_t size)
{
	if (size != elementSize_)
		return EMSGSIZE;

	const PushFunctor pushFunctor {data, size};
	return fifoQueueBase_.push(waitSemaphoreFunctor, pushFunctor);
}

}	// namespace distortos
<commit_msg>RawFifoQueue: add local PopFunctor class<commit_after>/**
 * \file
 * \brief RawFifoQueue class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-01-01
 */

#include "distortos/RawFifoQueue.hpp"

#include "distortos/scheduler/SemaphoreWaitFunctor.hpp"

#include <cstring>
#include <cerrno>

namespace distortos
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// PopFunctor is a functor used for popping of data from the RawFifoQueue
class PopFunctor : public scheduler::FifoQueueBase::Functor
{
public:

	/**
	 * \brief PopFunctor's constructor
	 *
	 * \param [out] buffer is a pointer to buffer for popped element
	 * \param [in] size is the size of \a buffer, bytes
	 */

	constexpr PopFunctor(void* const buffer, const size_t size) :
			buffer_{buffer},
			size_{size}
	{

	}

	/**
	 * \brief Copies the data from RawFifoQueue's storage (with memcpy()) and increments the storage pointer to next
	 * position (using the size of element).
	 *
	 * \param [in,out] storage is a reference to pointer to queue's storage
	 */

	virtual void operator()(void*& storage) const override
	{
		memcpy(buffer_, storage, size_);
		storage = static_cast<uint8_t*>(storage) + size_;
	}

private:

	/// pointer to buffer for popped element
	void* const buffer_;

	/// size of \a buffer_, bytes
	const size_t size_;
};

/// PushFunctor is a functor used for pushing of data to the RawFifoQueue
class PushFunctor : public scheduler::FifoQueueBase::Functor
{
public:

	/**
	 * \brief PushFunctor's constructor
	 *
	 * \param [in] data is a pointer to data that will be pushed to RawFifoQueue
	 * \param [in] size is the size of \a data, bytes
	 */

	constexpr PushFunctor(const void* const data, const size_t size) :
			data_{data},
			size_{size}
	{

	}

	/**
	 * \brief Copies the data to RawFifoQueue's storage (with memcpy()) and increments the storage pointer to next
	 * position (using the size of element).
	 *
	 * \param [in,out] storage is a reference to pointer to queue's storage
	 */

	virtual void operator()(void*& storage) const override
	{
		memcpy(storage, data_, size_);
		storage = static_cast<uint8_t*>(storage) + size_;
	}

private:

	/// pointer to data that will be pushed to RawFifoQueue
	const void* const data_;

	/// size of \a data_, bytes
	const size_t size_;
};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

RawFifoQueue::RawFifoQueue(void* const storage, const size_t elementSize, const size_t maxElements) :
		fifoQueueBase_{storage, static_cast<uint8_t*>(storage) + elementSize * maxElements, maxElements},
		elementSize_{elementSize}
{

}

int RawFifoQueue::push(const void* const data, const size_t size)
{
	const scheduler::SemaphoreWaitFunctor semaphoreWaitFunctor;
	return pushInternal(semaphoreWaitFunctor, data, size);
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

int RawFifoQueue::pushInternal(const scheduler::SemaphoreFunctor& waitSemaphoreFunctor, const void* const data,
		const size_t size)
{
	if (size != elementSize_)
		return EMSGSIZE;

	const PushFunctor pushFunctor {data, size};
	return fifoQueueBase_.push(waitSemaphoreFunctor, pushFunctor);
}

}	// namespace distortos
<|endoftext|>


<commit_before>
#include "class_tensors_finite.h"
#include <config/nmspc_settings.h>
#include <math/num.h>
#include <tensors/edges/class_edges_finite.h>
#include <tensors/model/class_model_finite.h>
#include <tensors/state/class_state_finite.h>
#include <tools/common/log.h>
#include <tools/finite/env.h>
#include <tools/finite/measure.h>
#include <tools/finite/mps.h>
#include <tools/finite/multisite.h>
#include <tools/finite/ops.h>

class_tensors_finite::class_tensors_finite()
    : state(std::make_unique<class_state_finite>()), model(std::make_unique<class_model_finite>()), edges(std::make_unique<class_edges_finite>()) {
    tools::log->trace("Constructing class_tensors_finite");
}

// We need to define the destructor and other special functions
// because we enclose data in unique_ptr for this pimpl idiom.
// Otherwise unique_ptr will forcibly inline its own default deleter.
// Here we follow "rule of five", so we must also define
// our own copy/move ctor and copy/move assignments
// This has the side effect that we must define our own
// operator= and copy assignment constructor.
// Read more: https://stackoverflow.com/questions/33212686/how-to-use-unique-ptr-with-forward-declared-type
// And here:  https://stackoverflow.com/questions/6012157/is-stdunique-ptrt-required-to-know-the-full-definition-of-t
class_tensors_finite::~class_tensors_finite()                            = default;            // default dtor
class_tensors_finite::class_tensors_finite(class_tensors_finite &&other) = default;            // default move ctor
class_tensors_finite &class_tensors_finite::operator=(class_tensors_finite &&other) = default; // default move assign

class_tensors_finite::class_tensors_finite(const class_tensors_finite &other)
    : state(std::make_unique<class_state_finite>(*other.state)), model(std::make_unique<class_model_finite>(*other.model)),
      edges(std::make_unique<class_edges_finite>(*other.edges)), active_sites(other.active_sites), measurements(other.measurements) {}
class_tensors_finite &class_tensors_finite::operator=(const class_tensors_finite &other) {
    // check for self-assignment
    if(this != &other) {
        state        = std::make_unique<class_state_finite>(*other.state);
        model        = std::make_unique<class_model_finite>(*other.model);
        edges        = std::make_unique<class_edges_finite>(*other.edges);
        active_sites = other.active_sites;
        measurements = other.measurements;
    }
    return *this;
}

void class_tensors_finite::initialize(ModelType model_type, size_t model_size, size_t position) {
    state->initialize(model_type, model_size, position);
    model->initialize(model_type, model_size);
    edges->initialize(model_size);
}

void class_tensors_finite::randomize_model() {
    model->randomize();
    model->rebuild_mpo_squared();
    eject_all_edges();
    rebuild_edges();
}

void class_tensors_finite::randomize_state(StateType state_type, const std::string &sector, long chi_lim, bool use_eigenspinors, std::optional<long> bitfield,
                                           std::optional<double> svd_threshold) {
    state->clear_measurements();
    tools::finite::mps::randomize_state(*state, sector, state_type, chi_lim, use_eigenspinors, bitfield);
    if(state_type == StateType::RANDOMIZE_PREVIOUS_STATE or state_type == StateType::RANDOM_ENTANGLED_STATE)
        project_to_nearest_sector(sector, chi_lim, svd_threshold); // Normalization happens during projection anyway
    else
        normalize_state(chi_lim, svd_threshold, NormPolicy::ALWAYS);
}

void class_tensors_finite::normalize_state(long chi_lim, std::optional<double> svd_threshold, NormPolicy norm_policy) {
    // Normalize if unity was lost for some reason (numerical error buildup)
    auto has_normalized = tools::finite::mps::normalize_state(*state, chi_lim, svd_threshold, norm_policy);
    if(has_normalized) {
        state->clear_cache();
        clear_measurements();
        eject_all_edges();
        rebuild_edges();
        assert_validity();
    }
}

void class_tensors_finite::project_to_nearest_sector(const std::string &sector, std::optional<long> chi_lim, std::optional<double> svd_threshold) {
    clear_measurements();
    if(not chi_lim) chi_lim = state->find_largest_chi();
    tools::finite::mps::normalize_state(*state, chi_lim.value(), svd_threshold, NormPolicy::IFNEEDED);
    tools::finite::ops::project_to_nearest_sector(*state, sector);
    normalize_state(chi_lim.value(), svd_threshold, NormPolicy::ALWAYS);
}

void class_tensors_finite::perturb_model_params(double coupling_ptb, double field_ptb, PerturbMode perturbMode) {
    measurements = tensors_measure_finite(); // State measurements can remain
    model->clear_cache();
    model->perturb_hamiltonian(coupling_ptb, field_ptb, perturbMode);
    model->rebuild_mpo_squared();
    model->assert_validity();
    eject_all_edges();
    rebuild_edges();
}

void class_tensors_finite::reduce_mpo_energy(std::optional<double> site_energy) {
    [[maybe_unused]] double var_bef_reset     = 1.0;
    [[maybe_unused]] double var_aft_compr     = 1.0;
    [[maybe_unused]] double ene_bef_reset     = 1.0;
    [[maybe_unused]] double ene_aft_compr_lap = 1.0;

    if constexpr(settings::debug) {
        ene_bef_reset = tools::finite::measure::energy_per_site(*state, *model, *edges);
        var_bef_reset = tools::finite::measure::energy_variance_per_site(*state, *model, *edges);
        clear_cache();
        clear_measurements();
    }

    if(not site_energy) site_energy = tools::finite::measure::energy_per_site(*state, *model, *edges);
    if(site_energy.value() == model->get_energy_per_site_reduced()) return;

    measurements = tensors_measure_finite(); // Resets model-related measurements but not state measurements, which can remain
    model->clear_cache();
    model->set_reduced_energy_per_site(site_energy.value());
    model->rebuild_mpo_squared();
    model->assert_validity();
    eject_all_edges();
    rebuild_edges();

    if constexpr(settings::debug) {
        ene_aft_compr_lap = tools::finite::measure::energy_per_site(*state, *model, *edges);
        var_aft_compr     = tools::finite::measure::energy_variance_per_site(*state, *model, *edges);
        clear_cache();
        clear_measurements();
    }

    if constexpr(settings::debug) {
        tools::log->debug("Energy   before mpo reset       {:>20.16f}", ene_bef_reset);
        tools::log->debug("Energy   after  mpo compression {:>20.16f}", ene_aft_compr_lap);
        tools::log->debug("Variance before mpo reset       {:>20.16f}", std::log10(var_bef_reset));
        tools::log->debug("Variance after  mpo compression {:>20.16f}", std::log10(var_aft_compr));
        double critical_cancellation_max_decimals = 15.0 - std::log10(std::pow(static_cast<double>(get_length()) * site_energy.value(), 2));
        double critical_cancellation_error        = std::pow(10, -critical_cancellation_max_decimals);
        double variance_change                    = std::abs(var_bef_reset - var_aft_compr);
        if(variance_change > 100 * critical_cancellation_error) {
            tools::log->warn("Energy reduction changed the variance significantly: {:.4f}%", variance_change / var_bef_reset * 100);
            throw std::runtime_error("Energy reduction changed the variance significantly");
        }
    }
}

void class_tensors_finite::rebuild_mpo_squared(std::optional<SVDMode> svdMode) {
    measurements = tensors_measure_finite(); // Resets model-related measurements but not state measurements, which can remain
    model->clear_cache();
    model->rebuild_mpo_squared(svdMode);
    model->assert_validity();
    eject_all_edges();
    rebuild_edges();
}

void class_tensors_finite::damp_model_disorder(double coupling_damp, double field_damp) {
    measurements = tensors_measure_finite(); // Resets model-related measurements but not state measurements, which can remain
    model->clear_cache();
    model->damp_model_disorder(coupling_damp, field_damp);
    model->rebuild_mpo_squared();
    model->assert_validity();
    eject_all_edges();
    rebuild_edges();
}

// Active sites
void class_tensors_finite::sync_active_sites() {
    active_sites        = state->active_sites;
    model->active_sites = state->active_sites;
    edges->active_sites = state->active_sites;
    rebuild_edges();
}

void class_tensors_finite::activate_sites(long threshold, size_t max_sites, size_t min_sites) {
    active_sites        = tools::finite::multisite::generate_site_list(*state, threshold, max_sites, min_sites);
    state->active_sites = active_sites;
    model->active_sites = active_sites;
    edges->active_sites = active_sites;
    rebuild_edges();
    clear_cache();
    clear_measurements();
}

void class_tensors_finite::activate_sites(const std::vector<size_t> &sites) {
    if(active_sites == sites) {
        state->active_sites = active_sites;
        model->active_sites = active_sites;
        edges->active_sites = active_sites;
        return;
    }

    active_sites        = sites;
    state->active_sites = active_sites;
    model->active_sites = active_sites;
    edges->active_sites = active_sites;
    rebuild_edges();
    clear_cache();
    clear_measurements();
}

void class_tensors_finite::activate_truncated_sites(long threshold, long chi_lim, size_t max_sites, size_t min_sites) {
    active_sites        = tools::finite::multisite::generate_truncated_site_list(*state, threshold, chi_lim, max_sites, min_sites);
    state->active_sites = active_sites;
    model->active_sites = active_sites;
    edges->active_sites = active_sites;
    rebuild_edges(); // will only produce missing edges
    clear_cache();   // clear multisite tensors and mpos which become invalid
    clear_measurements();
    // TODO: Should measurements be cleared here? Probably not
    //    clear_measurements();
}

long class_tensors_finite::active_problem_size() const { return tools::finite::multisite::get_problem_size(*state, active_sites); }
bool class_tensors_finite::is_real() const { return state->is_real() and model->is_real() and edges->is_real(); }
bool class_tensors_finite::has_nan() const { return state->has_nan() or model->has_nan() or edges->has_nan(); }
void class_tensors_finite::assert_validity() const {
    state->assert_validity();
    model->assert_validity();
    edges->assert_validity();
}

size_t class_tensors_finite::get_length() const {
    if(not num::all_equal(state->get_length(), model->get_length(), edges->get_length()))
        throw std::runtime_error("All lengths are not equal: state {} | model {} | edges {}");
    return state->get_length();
}

size_t class_tensors_finite::get_position() const { return state->get_position(); }

bool class_tensors_finite::position_is_the_middle() const { return state->position_is_the_middle(); }
bool class_tensors_finite::position_is_the_middle_any_direction() const { return state->position_is_the_middle_any_direction(); }
bool class_tensors_finite::position_is_left_edge() const { return state->position_is_left_edge(); }
bool class_tensors_finite::position_is_right_edge() const { return state->position_is_right_edge(); }
bool class_tensors_finite::position_is_any_edge() const { return state->position_is_any_edge(); }
bool class_tensors_finite::position_is_at(size_t pos) const { return state->position_is_at(pos); }
void class_tensors_finite::move_center_point(long chi_lim, std::optional<double> svd_threshold) {
    tools::finite::mps::move_center_point(*state, chi_lim, svd_threshold);
}
void class_tensors_finite::merge_multisite_tensor(const Eigen::Tensor<Scalar, 3> &multisite_tensor, long chi_lim, std::optional<double> svd_threshold) {
    // Make sure the active sites are the same everywhere
    if(not num::all_equal(active_sites, state->active_sites, model->active_sites, edges->active_sites))
        throw std::runtime_error("All active sites are not equal: tensors {} | state {} | model {} | edges {}");
    clear_measurements();
    tools::finite::mps::merge_multisite_tensor(*state, multisite_tensor, active_sites, get_position(), chi_lim);
    normalize_state(chi_lim, svd_threshold, NormPolicy::IFNEEDED);
}

void class_tensors_finite::rebuild_edges() { tools::finite::env::rebuild_edges(*state, *model, *edges); }
void class_tensors_finite::eject_all_edges() { edges->eject_edges_all(); }
void class_tensors_finite::eject_inactive_edges() { edges->eject_edges_inactive(); }
void class_tensors_finite::do_all_measurements() const { tools::finite::measure::do_all_measurements(*this); }
void class_tensors_finite::clear_measurements() const {
    measurements = tensors_measure_finite();
    state->clear_measurements();
}

void class_tensors_finite::clear_cache() const {
    model->clear_cache();
    state->clear_cache();
}<commit_msg>Ease abort criterion for variance shift on energy reduction operation<commit_after>
#include "class_tensors_finite.h"
#include <config/nmspc_settings.h>
#include <math/num.h>
#include <tensors/edges/class_edges_finite.h>
#include <tensors/model/class_model_finite.h>
#include <tensors/state/class_state_finite.h>
#include <tools/common/log.h>
#include <tools/finite/env.h>
#include <tools/finite/measure.h>
#include <tools/finite/mps.h>
#include <tools/finite/multisite.h>
#include <tools/finite/ops.h>

class_tensors_finite::class_tensors_finite()
    : state(std::make_unique<class_state_finite>()), model(std::make_unique<class_model_finite>()), edges(std::make_unique<class_edges_finite>()) {
    tools::log->trace("Constructing class_tensors_finite");
}

// We need to define the destructor and other special functions
// because we enclose data in unique_ptr for this pimpl idiom.
// Otherwise unique_ptr will forcibly inline its own default deleter.
// Here we follow "rule of five", so we must also define
// our own copy/move ctor and copy/move assignments
// This has the side effect that we must define our own
// operator= and copy assignment constructor.
// Read more: https://stackoverflow.com/questions/33212686/how-to-use-unique-ptr-with-forward-declared-type
// And here:  https://stackoverflow.com/questions/6012157/is-stdunique-ptrt-required-to-know-the-full-definition-of-t
class_tensors_finite::~class_tensors_finite()                            = default;            // default dtor
class_tensors_finite::class_tensors_finite(class_tensors_finite &&other) = default;            // default move ctor
class_tensors_finite &class_tensors_finite::operator=(class_tensors_finite &&other) = default; // default move assign

class_tensors_finite::class_tensors_finite(const class_tensors_finite &other)
    : state(std::make_unique<class_state_finite>(*other.state)), model(std::make_unique<class_model_finite>(*other.model)),
      edges(std::make_unique<class_edges_finite>(*other.edges)), active_sites(other.active_sites), measurements(other.measurements) {}
class_tensors_finite &class_tensors_finite::operator=(const class_tensors_finite &other) {
    // check for self-assignment
    if(this != &other) {
        state        = std::make_unique<class_state_finite>(*other.state);
        model        = std::make_unique<class_model_finite>(*other.model);
        edges        = std::make_unique<class_edges_finite>(*other.edges);
        active_sites = other.active_sites;
        measurements = other.measurements;
    }
    return *this;
}

void class_tensors_finite::initialize(ModelType model_type, size_t model_size, size_t position) {
    state->initialize(model_type, model_size, position);
    model->initialize(model_type, model_size);
    edges->initialize(model_size);
}

void class_tensors_finite::randomize_model() {
    model->randomize();
    model->rebuild_mpo_squared();
    eject_all_edges();
    rebuild_edges();
}

void class_tensors_finite::randomize_state(StateType state_type, const std::string &sector, long chi_lim, bool use_eigenspinors, std::optional<long> bitfield,
                                           std::optional<double> svd_threshold) {
    state->clear_measurements();
    tools::finite::mps::randomize_state(*state, sector, state_type, chi_lim, use_eigenspinors, bitfield);
    if(state_type == StateType::RANDOMIZE_PREVIOUS_STATE or state_type == StateType::RANDOM_ENTANGLED_STATE)
        project_to_nearest_sector(sector, chi_lim, svd_threshold); // Normalization happens during projection anyway
    else
        normalize_state(chi_lim, svd_threshold, NormPolicy::ALWAYS);
}

void class_tensors_finite::normalize_state(long chi_lim, std::optional<double> svd_threshold, NormPolicy norm_policy) {
    // Normalize if unity was lost for some reason (numerical error buildup)
    auto has_normalized = tools::finite::mps::normalize_state(*state, chi_lim, svd_threshold, norm_policy);
    if(has_normalized) {
        state->clear_cache();
        clear_measurements();
        eject_all_edges();
        rebuild_edges();
        assert_validity();
    }
}

void class_tensors_finite::project_to_nearest_sector(const std::string &sector, std::optional<long> chi_lim, std::optional<double> svd_threshold) {
    clear_measurements();
    if(not chi_lim) chi_lim = state->find_largest_chi();
    tools::finite::mps::normalize_state(*state, chi_lim.value(), svd_threshold, NormPolicy::IFNEEDED);
    tools::finite::ops::project_to_nearest_sector(*state, sector);
    normalize_state(chi_lim.value(), svd_threshold, NormPolicy::ALWAYS);
}

void class_tensors_finite::perturb_model_params(double coupling_ptb, double field_ptb, PerturbMode perturbMode) {
    measurements = tensors_measure_finite(); // State measurements can remain
    model->clear_cache();
    model->perturb_hamiltonian(coupling_ptb, field_ptb, perturbMode);
    model->rebuild_mpo_squared();
    model->assert_validity();
    eject_all_edges();
    rebuild_edges();
}

void class_tensors_finite::reduce_mpo_energy(std::optional<double> site_energy) {
    [[maybe_unused]] double var_bef_reset     = 1.0;
    [[maybe_unused]] double var_aft_compr     = 1.0;
    [[maybe_unused]] double ene_bef_reset     = 1.0;
    [[maybe_unused]] double ene_aft_compr_lap = 1.0;

    if constexpr(settings::debug) {
        ene_bef_reset = tools::finite::measure::energy_per_site(*state, *model, *edges);
        var_bef_reset = tools::finite::measure::energy_variance_per_site(*state, *model, *edges);
        clear_cache();
        clear_measurements();
    }

    if(not site_energy) site_energy = tools::finite::measure::energy_per_site(*state, *model, *edges);
    if(site_energy.value() == model->get_energy_per_site_reduced()) return;

    measurements = tensors_measure_finite(); // Resets model-related measurements but not state measurements, which can remain
    model->clear_cache();
    model->set_reduced_energy_per_site(site_energy.value());
    model->rebuild_mpo_squared();
    model->assert_validity();
    eject_all_edges();
    rebuild_edges();

    if constexpr(settings::debug) {
        ene_aft_compr_lap = tools::finite::measure::energy_per_site(*state, *model, *edges);
        var_aft_compr     = tools::finite::measure::energy_variance_per_site(*state, *model, *edges);
        clear_cache();
        clear_measurements();
    }

    if constexpr(settings::debug) {
        tools::log->debug("Energy   before mpo reset       {:>20.16f}", ene_bef_reset);
        tools::log->debug("Energy   after  mpo compression {:>20.16f}", ene_aft_compr_lap);
        tools::log->debug("Variance before mpo reset       {:>20.16f}", std::log10(var_bef_reset));
        tools::log->debug("Variance after  mpo compression {:>20.16f}", std::log10(var_aft_compr));
        double critical_cancellation_max_decimals = 15.0 - std::log10(std::pow(static_cast<double>(get_length()) * site_energy.value(), 2));
        double critical_cancellation_error        = std::pow(10, -critical_cancellation_max_decimals);
        double variance_change                    = std::abs(var_bef_reset - var_aft_compr);
        if(variance_change > 100 * critical_cancellation_error){
            tools::log->warn("Energy reduction changed the variance significantly: {:.16f}%", variance_change / var_bef_reset * 100);
        }
        if(variance_change > 1e6 * critical_cancellation_error) {
            throw std::runtime_error("Energy reduction destroyed variance precision");
        }
    }
}

void class_tensors_finite::rebuild_mpo_squared(std::optional<SVDMode> svdMode) {
    measurements = tensors_measure_finite(); // Resets model-related measurements but not state measurements, which can remain
    model->clear_cache();
    model->rebuild_mpo_squared(svdMode);
    model->assert_validity();
    eject_all_edges();
    rebuild_edges();
}

void class_tensors_finite::damp_model_disorder(double coupling_damp, double field_damp) {
    measurements = tensors_measure_finite(); // Resets model-related measurements but not state measurements, which can remain
    model->clear_cache();
    model->damp_model_disorder(coupling_damp, field_damp);
    model->rebuild_mpo_squared();
    model->assert_validity();
    eject_all_edges();
    rebuild_edges();
}

// Active sites
void class_tensors_finite::sync_active_sites() {
    active_sites        = state->active_sites;
    model->active_sites = state->active_sites;
    edges->active_sites = state->active_sites;
    rebuild_edges();
}

void class_tensors_finite::activate_sites(long threshold, size_t max_sites, size_t min_sites) {
    active_sites        = tools::finite::multisite::generate_site_list(*state, threshold, max_sites, min_sites);
    state->active_sites = active_sites;
    model->active_sites = active_sites;
    edges->active_sites = active_sites;
    rebuild_edges();
    clear_cache();
    clear_measurements();
}

void class_tensors_finite::activate_sites(const std::vector<size_t> &sites) {
    if(active_sites == sites) {
        state->active_sites = active_sites;
        model->active_sites = active_sites;
        edges->active_sites = active_sites;
        return;
    }

    active_sites        = sites;
    state->active_sites = active_sites;
    model->active_sites = active_sites;
    edges->active_sites = active_sites;
    rebuild_edges();
    clear_cache();
    clear_measurements();
}

void class_tensors_finite::activate_truncated_sites(long threshold, long chi_lim, size_t max_sites, size_t min_sites) {
    active_sites        = tools::finite::multisite::generate_truncated_site_list(*state, threshold, chi_lim, max_sites, min_sites);
    state->active_sites = active_sites;
    model->active_sites = active_sites;
    edges->active_sites = active_sites;
    rebuild_edges(); // will only produce missing edges
    clear_cache();   // clear multisite tensors and mpos which become invalid
    clear_measurements();
    // TODO: Should measurements be cleared here? Probably not
    //    clear_measurements();
}

long class_tensors_finite::active_problem_size() const { return tools::finite::multisite::get_problem_size(*state, active_sites); }
bool class_tensors_finite::is_real() const { return state->is_real() and model->is_real() and edges->is_real(); }
bool class_tensors_finite::has_nan() const { return state->has_nan() or model->has_nan() or edges->has_nan(); }
void class_tensors_finite::assert_validity() const {
    state->assert_validity();
    model->assert_validity();
    edges->assert_validity();
}

size_t class_tensors_finite::get_length() const {
    if(not num::all_equal(state->get_length(), model->get_length(), edges->get_length()))
        throw std::runtime_error("All lengths are not equal: state {} | model {} | edges {}");
    return state->get_length();
}

size_t class_tensors_finite::get_position() const { return state->get_position(); }

bool class_tensors_finite::position_is_the_middle() const { return state->position_is_the_middle(); }
bool class_tensors_finite::position_is_the_middle_any_direction() const { return state->position_is_the_middle_any_direction(); }
bool class_tensors_finite::position_is_left_edge() const { return state->position_is_left_edge(); }
bool class_tensors_finite::position_is_right_edge() const { return state->position_is_right_edge(); }
bool class_tensors_finite::position_is_any_edge() const { return state->position_is_any_edge(); }
bool class_tensors_finite::position_is_at(size_t pos) const { return state->position_is_at(pos); }
void class_tensors_finite::move_center_point(long chi_lim, std::optional<double> svd_threshold) {
    tools::finite::mps::move_center_point(*state, chi_lim, svd_threshold);
}
void class_tensors_finite::merge_multisite_tensor(const Eigen::Tensor<Scalar, 3> &multisite_tensor, long chi_lim, std::optional<double> svd_threshold) {
    // Make sure the active sites are the same everywhere
    if(not num::all_equal(active_sites, state->active_sites, model->active_sites, edges->active_sites))
        throw std::runtime_error("All active sites are not equal: tensors {} | state {} | model {} | edges {}");
    clear_measurements();
    tools::finite::mps::merge_multisite_tensor(*state, multisite_tensor, active_sites, get_position(), chi_lim);
    normalize_state(chi_lim, svd_threshold, NormPolicy::IFNEEDED);
}

void class_tensors_finite::rebuild_edges() { tools::finite::env::rebuild_edges(*state, *model, *edges); }
void class_tensors_finite::eject_all_edges() { edges->eject_edges_all(); }
void class_tensors_finite::eject_inactive_edges() { edges->eject_edges_inactive(); }
void class_tensors_finite::do_all_measurements() const { tools::finite::measure::do_all_measurements(*this); }
void class_tensors_finite::clear_measurements() const {
    measurements = tensors_measure_finite();
    state->clear_measurements();
}

void class_tensors_finite::clear_cache() const {
    model->clear_cache();
    state->clear_cache();
}<|endoftext|>


<commit_before>/*  This file is part of the KDE project.

    Copyright (C) 2011 Trever Fischer <tdfischer@fedoraproject.org>

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2.1 or 3 of the License.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "plugininstaller.h"
#include <gst/gst.h>
#include <QtCore/QCoreApplication>
#include <QtGui/QApplication>
#include <QtGui/QWidget>
#include <QtCore/QLibrary>
#include <QtCore/QPointer>
#include <QtCore/QMetaType>
#include <QtCore/QDebug>

#ifdef PLUGIN_INSTALL_API
#include <gst/pbutils/pbutils.h>
#endif

QT_BEGIN_NAMESPACE

namespace Phonon
{
namespace Gstreamer
{
bool PluginInstaller::s_ready = false;

bool PluginInstaller::init()
{
    if (!s_ready) {
#ifdef PLUGIN_INSTALL_API
        gst_pb_utils_init();
        s_ready = true;
#endif
    }
    return s_ready;
}

QString PluginInstaller::description(const GstCaps *caps, PluginType type)
{
#ifdef PLUGIN_INSTALL_API
    if (init()) {
        QString pluginStr;
        gchar *pluginDesc = NULL;
        switch(type) {
            case Decoder:
                pluginDesc = gst_pb_utils_get_decoder_description(caps);
                break;
            case Encoder:
                pluginDesc = gst_pb_utils_get_encoder_description(caps);
                break;
            case Codec:
                pluginDesc = gst_pb_utils_get_codec_description(caps);
                break;
            default:
                return 0;
        }
        pluginStr = QString::fromUtf8(pluginDesc);
        g_free (pluginDesc);
        return pluginStr;
    }
#endif
    return getCapType(caps);
}

QString PluginInstaller::description(const gchar *name, PluginType type)
{
#ifdef PLUGIN_INSTALL_API
    if (init()) {
        QString pluginStr;
        gchar *pluginDesc = NULL;
        switch(type) {
            case Source:
                pluginDesc = gst_pb_utils_get_source_description(name);
                break;
            case Sink:
                pluginDesc = gst_pb_utils_get_sink_description(name);
                break;
            case Element:
                pluginDesc = gst_pb_utils_get_element_description(name);
                break;
            default:
                return 0;
        }
        pluginStr = QString::fromUtf8(pluginDesc);
        g_free (pluginDesc);
        return pluginStr;
    }
#endif
    return name;
}

QString PluginInstaller::buildInstallationString(const GstCaps *caps, PluginType type)
{
    QString descType;
    switch(type) {
        case Decoder:
            descType = "decoder";
            break;
        case Encoder:
            descType = "encoder";
            break;
        case Codec:
            descType = "codec";
            break;
        default:
            return 0;
    }

    return QString("gstreamer|0.10|%0|%1|%2-%3")
        .arg( qApp->applicationName() )
        .arg( description(caps, type) )
        .arg( descType )
        .arg( getCapType(caps) );
}

QString PluginInstaller::buildInstallationString(const gchar *name, PluginType type)
{
    QString descType;
    switch(type) {
        case Element:
            descType = "element";
            break;
        default:
            return 0;
    }
    return QString("gstreamer|0.10|%0|%1|%2-%3")
        .arg( qApp->applicationName() )
        .arg( description(name, type) )
        .arg( descType )
        .arg( name );
}

PluginInstaller::PluginInstaller(QObject *parent)
    : QObject(parent)
{
}

void PluginInstaller::addPlugin(const QString &name, PluginType type)
{
    m_pluginList.insert(name, type);
}

void PluginInstaller::addPlugin(const GstCaps *caps, PluginType type)
{
    m_capList.insert(gst_caps_copy(caps), type);
}

#ifdef PLUGIN_INSTALL_API
void PluginInstaller::run()
{
    GstInstallPluginsContext *ctx = gst_install_plugins_context_new();
    QWidget *activeWindow = QApplication::activeWindow();
    if (activeWindow) {
        gst_install_plugins_context_set_xid(ctx, static_cast<int>(activeWindow->winId()));
    }
    gchar *details[m_pluginList.size()+m_capList.size()+1];
    int i = 0;
    foreach(QString plugin, m_pluginList.keys()) {
        details[i] = strdup(buildInstallationString(plugin.toLocal8Bit().data(), m_pluginList[plugin]).toLocal8Bit().data());
        i++;
    }
    foreach(GstCaps *caps, m_capList.keys()) {
        details[i] = strdup(buildInstallationString(caps, m_capList[caps]).toLocal8Bit().data());
        i++;
    }
    details[i] = NULL;

    GstInstallPluginsReturn status;
    status = gst_install_plugins_async(details, ctx, pluginInstallationDone, new QPointer<PluginInstaller>(this));
    gst_install_plugins_context_free(ctx);
    if (status != GST_INSTALL_PLUGINS_STARTED_OK) {
        if (status == GST_INSTALL_PLUGINS_HELPER_MISSING)
            emit failure(tr("Missing codec helper script assistant."));
        else
            emit failure(tr("Plugin codec installation failed."));
    } else {
        emit started();
    }
    for(i;i>0;i--)
        free(details[i]);
    reset();
}

void PluginInstaller::pluginInstallationDone(GstInstallPluginsReturn result, gpointer data)
{
    QPointer<PluginInstaller> *that = static_cast<QPointer<PluginInstaller>*>(data);
    if (*that) {
        qRegisterMetaType<GstInstallPluginsReturn>("GstInstallPluginsReturn");
        (*that)->pluginInstallationResult(result);
    }
}

void PluginInstaller::pluginInstallationResult(GstInstallPluginsReturn result)
{
    switch(result) {
        case GST_INSTALL_PLUGINS_INVALID:
            emit failure(tr("Phonon attempted to install an invalid codec name."));
            break;
        case GST_INSTALL_PLUGINS_CRASHED:
            emit failure(tr("The codec installer crashed."));
            break;
        case GST_INSTALL_PLUGINS_NOT_FOUND:
            emit failure(tr("The required codec could not be found for installation."));
            break;
        case GST_INSTALL_PLUGINS_ERROR:
            emit failure(tr("An unspecified error occurred during codec installation."));
            break;
        case GST_INSTALL_PLUGINS_PARTIAL_SUCCESS:
            emit failure(tr("Not all codecs could be installed."));
            break;
        case GST_INSTALL_PLUGINS_USER_ABORT:
            emit failure(tr("User aborted codec installation"));
            break;
        //These four should never ever be passed in.
        //If they have, gstreamer has probably imploded in on itself.
        case GST_INSTALL_PLUGINS_STARTED_OK:
        case GST_INSTALL_PLUGINS_INTERNAL_FAILURE:
        case GST_INSTALL_PLUGINS_HELPER_MISSING:
        case GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS:
        //But this one is OK.
        case GST_INSTALL_PLUGINS_SUCCESS:
            if (!gst_update_registry()) {
                emit failure(tr("Could not update plugin registry after update."));
            } else {
                emit success();
            }
            break;
    }
}
#endif

PluginInstaller::InstallStatus PluginInstaller::checkInstalledPlugins()
{
    bool allFound = true;
    foreach(QString plugin, m_pluginList.keys()) {
        if (!gst_default_registry_check_feature_version(plugin.toLocal8Bit().data(), 0, 10, 0)) {
            allFound = false;
            break;
        }
    }
    if (!allFound || m_capList.size() > 0) {
#ifdef PLUGIN_INSTALL_API
        run();
        return Installing;
#else
        return Missing;
#endif
    } else {
        return Installed;
    }
}

QString PluginInstaller::getCapType(const GstCaps *caps)
{
    GstStructure *str = gst_caps_get_structure (caps, 0);
    return QString::fromUtf8(gst_structure_get_name (str));;
}

void PluginInstaller::reset()
{
    foreach(GstCaps *caps, m_capList.keys()) {
        gst_caps_unref(caps);
    }
    m_capList.clear();
    m_pluginList.clear();
}

} // ns Gstreamer
} // ns Phonon

QT_END_NAMESPACE

#include "moc_plugininstaller.cpp"
<commit_msg>warning--<commit_after>/*  This file is part of the KDE project.

    Copyright (C) 2011 Trever Fischer <tdfischer@fedoraproject.org>

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2.1 or 3 of the License.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "plugininstaller.h"
#include <gst/gst.h>
#include <QtCore/QCoreApplication>
#include <QtGui/QApplication>
#include <QtGui/QWidget>
#include <QtCore/QLibrary>
#include <QtCore/QPointer>
#include <QtCore/QMetaType>
#include <QtCore/QDebug>

#ifdef PLUGIN_INSTALL_API
#include <gst/pbutils/pbutils.h>
#endif

QT_BEGIN_NAMESPACE

namespace Phonon
{
namespace Gstreamer
{
bool PluginInstaller::s_ready = false;

bool PluginInstaller::init()
{
    if (!s_ready) {
#ifdef PLUGIN_INSTALL_API
        gst_pb_utils_init();
        s_ready = true;
#endif
    }
    return s_ready;
}

QString PluginInstaller::description(const GstCaps *caps, PluginType type)
{
#ifdef PLUGIN_INSTALL_API
    if (init()) {
        QString pluginStr;
        gchar *pluginDesc = NULL;
        switch(type) {
            case Decoder:
                pluginDesc = gst_pb_utils_get_decoder_description(caps);
                break;
            case Encoder:
                pluginDesc = gst_pb_utils_get_encoder_description(caps);
                break;
            case Codec:
                pluginDesc = gst_pb_utils_get_codec_description(caps);
                break;
            default:
                return 0;
        }
        pluginStr = QString::fromUtf8(pluginDesc);
        g_free (pluginDesc);
        return pluginStr;
    }
#endif
    return getCapType(caps);
}

QString PluginInstaller::description(const gchar *name, PluginType type)
{
#ifdef PLUGIN_INSTALL_API
    if (init()) {
        QString pluginStr;
        gchar *pluginDesc = NULL;
        switch(type) {
            case Source:
                pluginDesc = gst_pb_utils_get_source_description(name);
                break;
            case Sink:
                pluginDesc = gst_pb_utils_get_sink_description(name);
                break;
            case Element:
                pluginDesc = gst_pb_utils_get_element_description(name);
                break;
            default:
                return 0;
        }
        pluginStr = QString::fromUtf8(pluginDesc);
        g_free (pluginDesc);
        return pluginStr;
    }
#endif
    return name;
}

QString PluginInstaller::buildInstallationString(const GstCaps *caps, PluginType type)
{
    QString descType;
    switch(type) {
        case Decoder:
            descType = "decoder";
            break;
        case Encoder:
            descType = "encoder";
            break;
        case Codec:
            descType = "codec";
            break;
        default:
            return 0;
    }

    return QString("gstreamer|0.10|%0|%1|%2-%3")
        .arg( qApp->applicationName() )
        .arg( description(caps, type) )
        .arg( descType )
        .arg( getCapType(caps) );
}

QString PluginInstaller::buildInstallationString(const gchar *name, PluginType type)
{
    QString descType;
    switch(type) {
        case Element:
            descType = "element";
            break;
        default:
            return 0;
    }
    return QString("gstreamer|0.10|%0|%1|%2-%3")
        .arg( qApp->applicationName() )
        .arg( description(name, type) )
        .arg( descType )
        .arg( name );
}

PluginInstaller::PluginInstaller(QObject *parent)
    : QObject(parent)
{
}

void PluginInstaller::addPlugin(const QString &name, PluginType type)
{
    m_pluginList.insert(name, type);
}

void PluginInstaller::addPlugin(const GstCaps *caps, PluginType type)
{
    m_capList.insert(gst_caps_copy(caps), type);
}

#ifdef PLUGIN_INSTALL_API
void PluginInstaller::run()
{
    GstInstallPluginsContext *ctx = gst_install_plugins_context_new();
    QWidget *activeWindow = QApplication::activeWindow();
    if (activeWindow) {
        gst_install_plugins_context_set_xid(ctx, static_cast<int>(activeWindow->winId()));
    }
    gchar *details[m_pluginList.size()+m_capList.size()+1];
    int i = 0;
    foreach(QString plugin, m_pluginList.keys()) {
        details[i] = strdup(buildInstallationString(plugin.toLocal8Bit().data(), m_pluginList[plugin]).toLocal8Bit().data());
        i++;
    }
    foreach(GstCaps *caps, m_capList.keys()) {
        details[i] = strdup(buildInstallationString(caps, m_capList[caps]).toLocal8Bit().data());
        i++;
    }
    details[i] = NULL;

    GstInstallPluginsReturn status;
    status = gst_install_plugins_async(details, ctx, pluginInstallationDone, new QPointer<PluginInstaller>(this));
    gst_install_plugins_context_free(ctx);
    if (status != GST_INSTALL_PLUGINS_STARTED_OK) {
        if (status == GST_INSTALL_PLUGINS_HELPER_MISSING)
            emit failure(tr("Missing codec helper script assistant."));
        else
            emit failure(tr("Plugin codec installation failed."));
    } else {
        emit started();
    }
    for(; i > 0; i--)
        free(details[i]);
    reset();
}

void PluginInstaller::pluginInstallationDone(GstInstallPluginsReturn result, gpointer data)
{
    QPointer<PluginInstaller> *that = static_cast<QPointer<PluginInstaller>*>(data);
    if (*that) {
        qRegisterMetaType<GstInstallPluginsReturn>("GstInstallPluginsReturn");
        (*that)->pluginInstallationResult(result);
    }
}

void PluginInstaller::pluginInstallationResult(GstInstallPluginsReturn result)
{
    switch(result) {
        case GST_INSTALL_PLUGINS_INVALID:
            emit failure(tr("Phonon attempted to install an invalid codec name."));
            break;
        case GST_INSTALL_PLUGINS_CRASHED:
            emit failure(tr("The codec installer crashed."));
            break;
        case GST_INSTALL_PLUGINS_NOT_FOUND:
            emit failure(tr("The required codec could not be found for installation."));
            break;
        case GST_INSTALL_PLUGINS_ERROR:
            emit failure(tr("An unspecified error occurred during codec installation."));
            break;
        case GST_INSTALL_PLUGINS_PARTIAL_SUCCESS:
            emit failure(tr("Not all codecs could be installed."));
            break;
        case GST_INSTALL_PLUGINS_USER_ABORT:
            emit failure(tr("User aborted codec installation"));
            break;
        //These four should never ever be passed in.
        //If they have, gstreamer has probably imploded in on itself.
        case GST_INSTALL_PLUGINS_STARTED_OK:
        case GST_INSTALL_PLUGINS_INTERNAL_FAILURE:
        case GST_INSTALL_PLUGINS_HELPER_MISSING:
        case GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS:
        //But this one is OK.
        case GST_INSTALL_PLUGINS_SUCCESS:
            if (!gst_update_registry()) {
                emit failure(tr("Could not update plugin registry after update."));
            } else {
                emit success();
            }
            break;
    }
}
#endif

PluginInstaller::InstallStatus PluginInstaller::checkInstalledPlugins()
{
    bool allFound = true;
    foreach(QString plugin, m_pluginList.keys()) {
        if (!gst_default_registry_check_feature_version(plugin.toLocal8Bit().data(), 0, 10, 0)) {
            allFound = false;
            break;
        }
    }
    if (!allFound || m_capList.size() > 0) {
#ifdef PLUGIN_INSTALL_API
        run();
        return Installing;
#else
        return Missing;
#endif
    } else {
        return Installed;
    }
}

QString PluginInstaller::getCapType(const GstCaps *caps)
{
    GstStructure *str = gst_caps_get_structure (caps, 0);
    return QString::fromUtf8(gst_structure_get_name (str));;
}

void PluginInstaller::reset()
{
    foreach(GstCaps *caps, m_capList.keys()) {
        gst_caps_unref(caps);
    }
    m_capList.clear();
    m_pluginList.clear();
}

} // ns Gstreamer
} // ns Phonon

QT_END_NAMESPACE

#include "moc_plugininstaller.cpp"
<|endoftext|>


<commit_before>/*  This file is part of the KDE project
    Copyright (C) 2010 Trever Fischer <tdfischer@fedoraproject.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) version 3, or any
    later version accepted by the membership of KDE e.V. (or its
    successor approved by the membership of KDE e.V.), Nokia Corporation
    (or its successors, if any) and the KDE Free Qt Foundation, which shall
    act as a proxy defined in Section 6 of version 3 of the license.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "videodataoutput.h"
#include <phonon/experimental/videoframe2.h>
#include "mediaobject.h"
#include "medianodeevent.h"

#include <gst/gstbin.h>
#include <gst/gstghostpad.h>
#include <gst/gstutils.h>

QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE

namespace Phonon
{
namespace Gstreamer
{

VideoDataOutput::VideoDataOutput(Backend *backend, QObject *parent)
    : QObject(parent),
    MediaNode(backend, VideoSink | VideoSource),
	m_frontend(0)
{
    static int count = 0;
    m_name = "VideoDataOutput" + QString::number(count++);

    m_queue = gst_bin_new(NULL);
    gst_object_ref(GST_OBJECT(m_queue));
    gst_object_sink(GST_OBJECT(m_queue));

    GstElement* tee = gst_element_factory_make("tee", NULL);
    GstElement* sink = gst_element_factory_make("fakesink", NULL);
    GstElement* queue = gst_element_factory_make("queue", NULL);
    GstElement* convert = gst_element_factory_make("ffmpegcolorspace", NULL);

    g_signal_connect(sink, "handoff", G_CALLBACK(processBuffer), this);
    g_object_set(G_OBJECT(sink), "signal-handoffs", true, (const char*)NULL);

        // Save ourselves a metric crapton of work by simply requesting
        // a format native to Qt.
    GstCaps *caps = gst_caps_new_simple("video/x-raw-rgb",
                                        "bpp", G_TYPE_INT, 24,
                                        "depth", G_TYPE_INT, 24,
                                        "endianess", G_TYPE_INT, G_BYTE_ORDER,
                                        NULL);

    gst_bin_add_many(GST_BIN(m_queue), tee, sink, convert, queue, NULL);
    gst_element_link(tee, queue);
    gst_element_link(queue, convert);
    gst_element_link_filtered(convert, sink, caps);
    gst_caps_unref(caps);

    GstPad *inputpad = gst_element_get_static_pad(tee, "sink");
    gst_element_add_pad(m_queue, gst_ghost_pad_new("sink", inputpad));
    gst_object_unref(inputpad);

    GstPad *outputpad = gst_element_get_request_pad(tee, "src%d");
    gst_element_add_pad(m_queue, gst_ghost_pad_new("src", outputpad));
    gst_object_unref(outputpad);
    g_object_set(G_OBJECT(sink), "sync", true, (const char*)NULL);

    m_isValid = true;
}

VideoDataOutput::~VideoDataOutput()
{
    gst_element_set_state(m_queue, GST_STATE_NULL);
    gst_object_unref(m_queue);
}

void VideoDataOutput::processBuffer(GstElement*, GstBuffer* buffer, GstPad*, gpointer gThat)
{
    VideoDataOutput *that = reinterpret_cast<VideoDataOutput*>(gThat);

    GstStructure* structure = gst_caps_get_structure(GST_BUFFER_CAPS(buffer), 0);
    int width;
    int height;
    double aspect;

    gst_structure_get_int(structure, "width", &width);
    gst_structure_get_int(structure, "height", &height);
    aspect = (double)width/height;
    const Experimental::VideoFrame2 f = {
        width,
        height,
        aspect,
        Experimental::VideoFrame2::Format_RGB888,
                // RGB888 Means the data is 8 bits o' red, 8 bits o' green, and 8 bits o' blue per pixel.
        QByteArray::fromRawData(reinterpret_cast<const char*>(GST_BUFFER_DATA(buffer)), 3*width*height),
        0,
        0
    };
    if (that->m_frontend)
        that->m_frontend->frameReady(f);
}

}} // namespace Phonon::Gstreamer

QT_END_NAMESPACE
QT_END_HEADER

#include "moc_videodataoutput.cpp"
// vim: sw=4 ts=4
<commit_msg>VideoDataOutput is no longer a source, just a sink<commit_after>/*  This file is part of the KDE project
    Copyright (C) 2010 Trever Fischer <tdfischer@fedoraproject.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) version 3, or any
    later version accepted by the membership of KDE e.V. (or its
    successor approved by the membership of KDE e.V.), Nokia Corporation
    (or its successors, if any) and the KDE Free Qt Foundation, which shall
    act as a proxy defined in Section 6 of version 3 of the license.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "videodataoutput.h"
#include <phonon/experimental/videoframe2.h>
#include "mediaobject.h"
#include "medianodeevent.h"

#include <gst/gstbin.h>
#include <gst/gstghostpad.h>
#include <gst/gstutils.h>

QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE

namespace Phonon
{
namespace Gstreamer
{

VideoDataOutput::VideoDataOutput(Backend *backend, QObject *parent)
    : QObject(parent),
    MediaNode(backend, VideoSink),
	m_frontend(0)
{
    static int count = 0;
    m_name = "VideoDataOutput" + QString::number(count++);

    m_queue = gst_bin_new(NULL);
    gst_object_ref(GST_OBJECT(m_queue));
    gst_object_sink(GST_OBJECT(m_queue));

    GstElement* sink = gst_element_factory_make("fakesink", NULL);
    GstElement* queue = gst_element_factory_make("queue", NULL);
    GstElement* convert = gst_element_factory_make("ffmpegcolorspace", NULL);

    g_signal_connect(sink, "handoff", G_CALLBACK(processBuffer), this);
    g_object_set(G_OBJECT(sink), "signal-handoffs", true, (const char*)NULL);

        // Save ourselves a metric crapton of work by simply requesting
        // a format native to Qt.
    GstCaps *caps = gst_caps_new_simple("video/x-raw-rgb",
                                        "bpp", G_TYPE_INT, 24,
                                        "depth", G_TYPE_INT, 24,
                                        "endianess", G_TYPE_INT, G_BYTE_ORDER,
                                        NULL);

    gst_bin_add_many(GST_BIN(m_queue), sink, convert, queue, NULL);
    gst_element_link(queue, convert);
    gst_element_link_filtered(convert, sink, caps);
    gst_caps_unref(caps);

    GstPad *inputpad = gst_element_get_static_pad(queue, "sink");
    gst_element_add_pad(m_queue, gst_ghost_pad_new("sink", inputpad));
    gst_object_unref(inputpad);

    g_object_set(G_OBJECT(sink), "sync", true, (const char*)NULL);

    m_isValid = true;
}

VideoDataOutput::~VideoDataOutput()
{
    gst_element_set_state(m_queue, GST_STATE_NULL);
    gst_object_unref(m_queue);
}

void VideoDataOutput::processBuffer(GstElement*, GstBuffer* buffer, GstPad*, gpointer gThat)
{
    VideoDataOutput *that = reinterpret_cast<VideoDataOutput*>(gThat);

    GstStructure* structure = gst_caps_get_structure(GST_BUFFER_CAPS(buffer), 0);
    int width;
    int height;
    double aspect;

    gst_structure_get_int(structure, "width", &width);
    gst_structure_get_int(structure, "height", &height);
    aspect = (double)width/height;
    const Experimental::VideoFrame2 f = {
        width,
        height,
        aspect,
        Experimental::VideoFrame2::Format_RGB888,
                // RGB888 Means the data is 8 bits o' red, 8 bits o' green, and 8 bits o' blue per pixel.
        QByteArray::fromRawData(reinterpret_cast<const char*>(GST_BUFFER_DATA(buffer)), 3*width*height),
        0,
        0
    };
    if (that->m_frontend)
        that->m_frontend->frameReady(f);
}

}} // namespace Phonon::Gstreamer

QT_END_NAMESPACE
QT_END_HEADER

#include "moc_videodataoutput.cpp"
// vim: sw=4 ts=4
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "Stroika/Frameworks/StroikaPreComp.h"

#include <iostream>

#include "Stroika/Foundation/Characters/String2Int.h"
#include "Stroika/Foundation/Characters/ToString.h"
#include "Stroika/Foundation/DataExchange/InternetMediaTypeRegistry.h"
#include "Stroika/Foundation/Execution/CommandLine.h"
#include "Stroika/Foundation/Execution/Module.h"
#include "Stroika/Foundation/Execution/SignalHandlers.h"
#include "Stroika/Foundation/Execution/TimeOutException.h"
#include "Stroika/Foundation/Execution/WaitableEvent.h"
#include "Stroika/Foundation/IO/Network/HTTP/Exception.h"
#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
#include "Stroika/Foundation/IO/Network/HTTP/Methods.h"
#include "Stroika/Foundation/Streams/TextReader.h"
#include "Stroika/Foundation/Time/Duration.h"

#include "Stroika/Frameworks/WebServer/ConnectionManager.h"
#include "Stroika/Frameworks/WebServer/FileSystemRequestHandler.h"
#include "Stroika/Frameworks/WebServer/Router.h"

#include "AppVersion.h"

using namespace std;

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Frameworks::WebServer;

using Characters::String;
using Memory::BLOB;
using Stroika::Frameworks::WebServer::FileSystemRequestHandler;
using Stroika::Frameworks::WebServer::Request;
using Stroika::Frameworks::WebServer::Response;
using Time::Duration;

namespace {
    Sequence<pair<RegularExpression, CacheControl>> kCacheControlSettings_{
#if __cpp_designated_initializers
        {RegularExpression{L".*\\.gif", CompareOptions::eCaseInsensitive}, CacheControl{.fMaxAge = Duration{24h}.As<int32_t> ()}}
#else
        {RegularExpression{L".*\\.gif", CompareOptions::eCaseInsensitive}, CacheControl{nullopt, Duration{24h}.As<int32_t> ()}}
#endif
    };

    FileSystemRequestHandler::Options kFSHandlersOptions_{L"Files"_k, Sequence<String>{L"index.html"_k}, nullopt, kCacheControlSettings_};

    /*
     *  It's often helpful to structure together, routes, special interceptors, with your connection manager, to package up
     *  all the logic /options for HTTP interface.
     *
     *  This particular organization also makes it easy to save instance variables with the webserver (like a pointer to a handler)
     *  and accesss them from the Route handler functions.
     */
    struct MyWebServer_ {
        const Sequence<Route> kRoutes_;
        ConnectionManager     fConnectionMgr_;
        MyWebServer_ (uint16_t portNumber)
            : kRoutes_
        {
            Route{L""_RegEx, DefaultPage_},
                Route{HTTP::MethodsRegEx::kPost, L"SetAppState"_RegEx, SetAppState_},
                Route{L"FRED"_RegEx, [] (Request*, Response* response) {
                          response->write (L"FRED");
                          response->contentType = DataExchange::InternetMediaTypes::kText_PLAIN;
                      }},
                Route
            {
                L"Files/.*"_RegEx, FileSystemRequestHandler { Execution::GetEXEDir () / L"html", kFSHandlersOptions_ }
            }
        }
#if __cpp_designated_initializers
        , fConnectionMgr_
        {
            SocketAddresses (InternetAddresses_Any (), portNumber), kRoutes_, ConnectionManager::Options { .fBindFlags = Socket::BindFlags{}, .fServerHeader = L"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString () }
        }
#else
        , fConnectionMgr_
        {
            SocketAddresses (InternetAddresses_Any (), portNumber), kRoutes_, ConnectionManager::Options { nullopt, nullopt, Socket::BindFlags{}, L"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString () }
        }
#endif
        {
        }
        // Can declare arguments as Request*,Response*
        static void DefaultPage_ (Request*, Response* response)
        {
            //constexpr bool kUseTransferCoding_ = true;
            constexpr bool kUseTransferCoding_ = false;
            if (kUseTransferCoding_) {
                response->rwHeaders ().transferEncoding = HTTP::TransferEncoding::eChunked;
            }
            response->contentType = DataExchange::InternetMediaTypes::kHTML;
            response->writeln (L"");
            response->writeln (L"
Hi Mom
");
            response->writeln (L"
");
            response->writeln (L"Run the service (under the debugger if you wish)");
            response->writeln (L"
curl http://localhost:8080/ OR
");
            response->writeln (L"
curl http://localhost:8080/FRED OR      (to see error handling)
");
            response->writeln (L"
curl -H \"Content-Type: application/json\" -X POST -d '{\"AppState\":\"Start\"}' http://localhost:8080/SetAppState
");
            response->writeln (L"
curl http://localhost:8080/Files/index.html -v
");
            response->writeln (L"
");
            response->writeln (L"");
        }
        // Can declare arguments as Message* message
        static void SetAppState_ (Message* message)
        {
            String argsAsString = Streams::TextReader::New (message->rwRequest ().GetBody ()).ReadAll ();
            message->rwResponse ().writeln (L"
Hi SetAppState (" + argsAsString.As<wstring> () + L")
");
            message->rwResponse ().contentType = DataExchange::InternetMediaTypes::kHTML;
        }
    };
}

int main (int argc, const char* argv[])
{
    Debug::TraceContextBumper                            ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"main", L"argv=%s", Characters::ToString (vector<const char*>{argv, argv + argc}).c_str ())};
    Execution::SignalHandlerRegistry::SafeSignalsManager safeSignals;
#if qPlatform_POSIX
    Execution::SignalHandlerRegistry::Get ().SetSignalHandlers (SIGPIPE, Execution::SignalHandlerRegistry::kIGNORED);
#endif
    uint16_t                  portNumber = 8080;
    Time::DurationSecondsType quitAfter  = numeric_limits<Time::DurationSecondsType>::max ();

    Sequence<String> args = Execution::ParseCommandLine (argc, argv);
    for (auto argi = args.begin (); argi != args.end (); ++argi) {
        if (Execution::MatchesCommandLineArgument (*argi, L"port")) {
            ++argi;
            if (argi != args.end ()) {
                portNumber = Characters::String2Int<uint16_t> (*argi);
            }
            else {
                cerr << "Expected arg to -port" << endl;
                return EXIT_FAILURE;
            }
        }
        else if (Execution::MatchesCommandLineArgument (*argi, L"quit-after")) {
            ++argi;
            if (argi != args.end ()) {
                quitAfter = Characters::String2Float<Time::DurationSecondsType> (*argi);
            }
            else {
                cerr << "Expected arg to -quit-after" << endl;
                return EXIT_FAILURE;
            }
        }
    }

    try {
        MyWebServer_ myWebServer{portNumber};        // listen and dispatch while this object exists
        Execution::WaitableEvent{}.Wait (quitAfter); // wait forever - til user hits ctrl-c
    }
    catch (const Execution::TimeOutException&) {
        cerr << "Timed out - so - exiting..." << endl;
        return EXIT_SUCCESS;
    }
    catch (...) {
        cerr << "Error encountered: " << Characters::ToString (current_exception ()).AsNarrowSDKString () << " - terminating..." << endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
<commit_msg>tweak webserver sample<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "Stroika/Frameworks/StroikaPreComp.h"

#include <iostream>

#include "Stroika/Foundation/Characters/String2Int.h"
#include "Stroika/Foundation/Characters/ToString.h"
#include "Stroika/Foundation/DataExchange/InternetMediaTypeRegistry.h"
#include "Stroika/Foundation/Execution/CommandLine.h"
#include "Stroika/Foundation/Execution/Module.h"
#include "Stroika/Foundation/Execution/SignalHandlers.h"
#include "Stroika/Foundation/Execution/TimeOutException.h"
#include "Stroika/Foundation/Execution/WaitableEvent.h"
#include "Stroika/Foundation/IO/Network/HTTP/Exception.h"
#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
#include "Stroika/Foundation/IO/Network/HTTP/Methods.h"
#include "Stroika/Foundation/Streams/TextReader.h"
#include "Stroika/Foundation/Time/Duration.h"

#include "Stroika/Frameworks/WebServer/ConnectionManager.h"
#include "Stroika/Frameworks/WebServer/FileSystemRequestHandler.h"
#include "Stroika/Frameworks/WebServer/Router.h"

#include "AppVersion.h"

using namespace std;

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Frameworks::WebServer;

using Characters::String;
using Memory::BLOB;
using Stroika::Frameworks::WebServer::FileSystemRequestHandler;
using Stroika::Frameworks::WebServer::Request;
using Stroika::Frameworks::WebServer::Response;
using Time::Duration;

namespace {
    Sequence<pair<RegularExpression, CacheControl>> kCacheControlSettings_
    {
#if __cpp_designated_initializers
        {
            RegularExpression{L".*\\.gif", CompareOptions::eCaseInsensitive}, CacheControl { .fMaxAge = Duration{24h}.As<int32_t> () }
        }
#else
        {
            RegularExpression{L".*\\.gif", CompareOptions::eCaseInsensitive}, CacheControl { nullopt, Duration{24h}.As<int32_t> () }
        }
#endif
    };

    FileSystemRequestHandler::Options kFSHandlersOptions_{L"Files"_k, Sequence<String>{L"index.html"_k}, nullopt, kCacheControlSettings_};

    /*
     *  It's often helpful to structure together, routes, special interceptors, with your connection manager, to package up
     *  all the logic /options for HTTP interface.
     *
     *  This particular organization also makes it easy to save instance variables with the webserver (like a pointer to a handler)
     *  and accesss them from the Route handler functions.
     */
    struct MyWebServer_ {
        const Sequence<Route> kRoutes_;
        ConnectionManager     fConnectionMgr_;
        MyWebServer_ (uint16_t portNumber)
            : kRoutes_
        {
            Route{L""_RegEx, DefaultPage_}, Route{HTTP::MethodsRegEx::kPost, L"SetAppState"_RegEx, SetAppState_}, Route{L"FRED"_RegEx, [] (Request*, Response* response) {
                                                                                response->contentType = DataExchange::InternetMediaTypes::kText_PLAIN;
                                                                                response->write (L"FRED");
                                                                                }},
                Route
            {
                L"Files/.*"_RegEx, FileSystemRequestHandler { Execution::GetEXEDir () / L"html", kFSHandlersOptions_ }
            }
        }
#if __cpp_designated_initializers
        , fConnectionMgr_
        {
            SocketAddresses (InternetAddresses_Any (), portNumber), kRoutes_, ConnectionManager::Options { .fBindFlags = Socket::BindFlags{}, .fServerHeader = L"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString () }
        }
#else
        , fConnectionMgr_
        {
            SocketAddresses (InternetAddresses_Any (), portNumber), kRoutes_, ConnectionManager::Options { nullopt, nullopt, Socket::BindFlags{}, L"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString () }
        }
#endif
        {
        }
        // Can declare arguments as Request*,Response*
        static void DefaultPage_ (Request*, Response* response)
        {
            //constexpr bool kUseTransferCoding_ = true;
            constexpr bool kUseTransferCoding_ = false;
            if (kUseTransferCoding_) {
                response->rwHeaders ().transferEncoding = HTTP::TransferEncoding::eChunked;
            }
            response->contentType = DataExchange::InternetMediaTypes::kHTML;
            response->writeln (L"");
            response->writeln (L"
Hi Mom
");
            response->writeln (L"
");
            response->writeln (L"Run the service (under the debugger if you wish)");
            response->writeln (L"
curl http://localhost:8080/ OR
");
            response->writeln (L"
curl http://localhost:8080/FRED OR      (to see error handling)
");
            response->writeln (L"
curl -H \"Content-Type: application/json\" -X POST -d '{\"AppState\":\"Start\"}' http://localhost:8080/SetAppState
");
            response->writeln (L"
curl http://localhost:8080/Files/index.html -v
");
            response->writeln (L"
");
            response->writeln (L"");
        }
        // Can declare arguments as Message* message
        static void SetAppState_ (Message* message)
        {
            message->rwResponse ().contentType = DataExchange::InternetMediaTypes::kHTML;
            String argsAsString                = Streams::TextReader::New (message->rwRequest ().GetBody ()).ReadAll ();
            message->rwResponse ().writeln (L"
Hi SetAppState (" + argsAsString.As<wstring> () + L")
");
        }
    };
}

int main (int argc, const char* argv[])
{
    Debug::TraceContextBumper                            ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"main", L"argv=%s", Characters::ToString (vector<const char*>{argv, argv + argc}).c_str ())};
    Execution::SignalHandlerRegistry::SafeSignalsManager safeSignals;
#if qPlatform_POSIX
    Execution::SignalHandlerRegistry::Get ().SetSignalHandlers (SIGPIPE, Execution::SignalHandlerRegistry::kIGNORED);
#endif
    uint16_t                  portNumber = 8080;
    Time::DurationSecondsType quitAfter  = numeric_limits<Time::DurationSecondsType>::max ();

    Sequence<String> args = Execution::ParseCommandLine (argc, argv);
    for (auto argi = args.begin (); argi != args.end (); ++argi) {
        if (Execution::MatchesCommandLineArgument (*argi, L"port")) {
            ++argi;
            if (argi != args.end ()) {
                portNumber = Characters::String2Int<uint16_t> (*argi);
            }
            else {
                cerr << "Expected arg to -port" << endl;
                return EXIT_FAILURE;
            }
        }
        else if (Execution::MatchesCommandLineArgument (*argi, L"quit-after")) {
            ++argi;
            if (argi != args.end ()) {
                quitAfter = Characters::String2Float<Time::DurationSecondsType> (*argi);
            }
            else {
                cerr << "Expected arg to -quit-after" << endl;
                return EXIT_FAILURE;
            }
        }
    }

    try {
        MyWebServer_ myWebServer{portNumber};        // listen and dispatch while this object exists
        Execution::WaitableEvent{}.Wait (quitAfter); // wait forever - til user hits ctrl-c
    }
    catch (const Execution::TimeOutException&) {
        cerr << "Timed out - so - exiting..." << endl;
        return EXIT_SUCCESS;
    }
    catch (...) {
        cerr << "Error encountered: " << Characters::ToString (current_exception ()).AsNarrowSDKString () << " - terminating..." << endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
<|endoftext|>